Did you know you can find classes by name and tag?
classesmatching()
dynamically sources information from the current state. For example, let’s say you have classes representing a system’s role. Furthermore, let’s say that we want a host to only have a single role class defined. Finally, if we have more than one role class defined, then we don’t want to proceed.
To achieve this without classesmatching()
, we might have a policy file that looks like
this (/tmp/feature-friday-13/tags-on-classes-0.cf
)
bundle agent __main__
{
classes:
"have_role"
expression => "role_1|role_2|role_3";
"too_many_roles"
expression => "(role_1.(role_2|role3))|(role_2.(role_1|role3))|role_3.(role_2|role_3)";
"no_roles"
expression => "!(role_1|role_2|role_3)";
reports:
have_role::
"I have a role";
role_1::
"I have role_1";
role_2::
"I have role_2";
role_3::
"I have role_3";
too_many_roles::
"Uh oh! I have more than 1 role class defined.";
no_roles::
"Uh oh! I don't have any role classes defined.";
}
We can run the policy defining different variations, checking that the various cases are covered.
With no role classes:
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-0.cf
R: Uh oh! I don't have any role classes defined.
With all possible role classes:
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-0.cf --define role_1,role_2,role_3
R: I have a role
R: I have role_1
R: I have role_2
R: I have role_3
R: Uh oh! I have more than 1 role class defined.
With multiple role classes:
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-0.cf --define role_1,role_3
R: I have a role
R: I have role_1
R: I have role_3
R: Uh oh! I have more than 1 role class defined.
With a single role class defined:
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-0.cf --define role_1
R: I have a role
R: I have role_1
This can be much more clear and maintainable if implemented using classesmatching()
:
bundle agent __main__
{
vars:
"role_classes"
slist => classesmatching("role_.*");
classes:
"have_role"
expression => isgreaterthan(length(role_classes), 0);
"too_many_roles"
expression => isgreaterthan(length(role_classes), 1);
"no_roles"
expression => strcmp(length(role_classes), 0);
reports:
have_role::
"I have a role";
"I have $(role_classes)"
if => "$(role_classes)";
too_many_roles::
"Uh oh! I have more than 1 role class defined.";
no_roles::
"Uh oh! I don't have any role classes defined.";
}
We can run it through the same checks we did previously and see that the result is identical. However, we no longer have to maintain expressions for each combination. All we need to do is to follow our role class naming convention.
First with no role classes defined:
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-1.cf
R: Uh oh! I don't have any role classes defined.
With all possible role classes:
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-1.cf --define role_1,role_2,role_3
R: I have a role
R: I have role_1
R: I have role_3
R: I have role_2
R: Uh oh! I have more than 1 role class defined.
With multiple role classes:
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-1.cf --define role_1,role_3
R: I have a role
R: I have role_1
R: I have role_3
R: Uh oh! I have more than 1 role class defined.
With a single role class defined:
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-1.cf --define role_1
R: I have a role
R: I have role_1
Sometimes, naming conventions aren’t enough. Sometimes we want a bit more. Well, classesmatching()
can also be searched by tag. Let’s refactor the above example to not care about the class name. Instead, let’s care about having more than one class defined with the same tag.
bundle agent __main__
{
vars:
"role_classes"
slist => classesmatching(".*", "role_class");
classes:
"have_role"
expression => isgreaterthan(length(role_classes), 0);
"too_many_roles"
expression => isgreaterthan(length(role_classes), 1);
"no_roles"
expression => strcmp(length(role_classes), 0);
reports:
have_role::
"I have a role";
"I have $(role_classes)"
if => "$(role_classes)";
too_many_roles::
"Uh oh! I have more than 1 role class defined.";
no_roles::
"Uh oh! I don't have any role classes defined.";
}
Unfortunately, we can’t specify tags for a class when using the --define
option as we did previously, so we will modify the policy to define the classes used.
bundle common classification
{
classes:
"Rick"
expression => "any",
meta => { "role_class" };
"Astley"
expression => "any",
meta => { "role_class" };
"Never_Gonna_Give_You_Up"
expression => "any",
meta => { "role_class" };
}
bundle agent __main__
{
vars:
"role_classes"
slist => classesmatching(".*", "role_class");
classes:
"have_role"
expression => isgreaterthan(length(role_classes), 0);
"too_many_roles"
expression => isgreaterthan(length(role_classes), 1);
"no_roles"
expression => strcmp(length(role_classes), 0);
reports:
have_role::
"I have a role";
"I have $(role_classes)"
if => "$(role_classes)";
too_many_roles::
"Uh oh! I have more than 1 role class defined.";
no_roles::
"Uh oh! I don't have any role classes defined.";
}
cf-agent -Kf /tmp/feature-friday-13/tags-on-classes-3.cf
R: I have a role
R: I have Astley
R: I have Never_Gonna_Give_You_Up
R: I have Rick
R: Uh oh! I have more than 1 role class defined.
Take it for a spin, and see how this methodology can be applied in your own environment.
Happy Friday! 🎉