Ever need to make a decision based on the version of something? The version_compare() function might be useful for you.1
Over time, software changes and features are added and removed. Sometimes, we need to make a decision based on versions. For example, the Include directive in ssh_config was introduced in OpenSSH 7.3.2 Let’s take a look at how we could possibly use it.
This example illustrates the basic use of version_compare():
bundle agent __main__
{
vars:
"version_1" string => "7.3.0";
"version_2" string => "7.2.0";
"comparisons"
slist => { "=", "==", "!=", ">", "<", ">=", "<=" };
reports:
"$(version_1) is $(comparisons) $(version_2)"
if => version_compare( "$(version_1)", "$(comparisons)", "$(version_2)" );
}The example above compares version_1 against version_2 using each of the supported comparison operators, and it emits a report when the comparison evaluates to true. Running the example, we see the expected reports:
cf-agent -Kf /tmp/feature-friday-37-0.cf
R: 7.3.0 is != 7.2.0
R: 7.3.0 is > 7.2.0
R: 7.3.0 is >= 7.2.0
For something a bit more realistic, let’s extend the example to test against the currently installed version of openssh-client:
bundle agent __main__
{
vars:
"installed_version_data"
data => packagesmatching("openssh-client", ".*", ".*", ".*");
"ssh_conf[HashKnownHosts]"
string => "yes";
"ssh_conf[Include]"
string => "/etc/ssh/ssh_config.d/*.conf",
if => version_compare(
# Debian version numbers may contain a leading epoch:string that is unrelated to the upstream package version
# Here, if it exists we strip it.
regex_replace( "$(installed_version_data[0][version])","^\d+:(.*)", "$1", "" ),
">=",
"7.3.0"
);
reports:
"ssh_conf for openssh-client '$(installed_version_data[0][version])' should include: $(with)"
with => join( ", ", getindices( ssh_conf ) );
}Remember that version_compare() compares semantic versions. But not all versions you encounter are semantic. For example, Debian package versions may include a leading epoch followed by a colon, resulting in a version that looks like this 1:8.9p1-3ubuntu0.6. In that case, you must extract the “real” version you want to compare, which we did in the example using regex_replace(). Here ssh-conf[Include] is defined as /etc/ssh/ssh_config.d/*.conf if the version of the installed package matching openssh-client with the epoch stripped if it’s present is greater than or equal to 7.3.0.
"ssh_conf[Include]"
string => "/etc/ssh/ssh_config.d/*.conf",
if => version_compare(
# Debian version numbers may contain a leading epoch: string that is unrelated to the upstream package version
# Here, if it exists we strip it.
regex_replace( "$(installed_version_data[0][version])", "^\d+:(.*)", "$1", "" ),
">=",
"7.3.0"
);Running the policy we can see that configuration for both HashKnownHosts and Include should be included:
cf-agent -Kf /tmp/feature-friday-37-1.cf
R: ssh_conf for openssh-client '1:8.9p1-3ubuntu0.6' should include: HashKnownHosts, Include
If you want to play around with this you can simply define a data container matching the results of the packagesmatching() function call you expect. For example, here we can see the result if the version of the package installed was 6.9p1-3ubuntu0.6:
bundle agent __main__
{
vars:
# "installed_version_data" data => packagesmatching( "openssh-client",
# ".*", ".*", ".*" );
"installed_version_data" data => '[
{
"arch": "amd64",
"method": "apt_get",
"name": "openssh-client",
"version": "6.9p1-3ubuntu0.6"
}
]';
"ssh_conf[HashKnownHosts]"
string => "yes";
"ssh_conf[Include]"
string => "/etc/ssh/ssh_config.d/*.conf",
if => version_compare(
# Debian version numbers may contain a leading epoch: string that is unrelated to the upstream package version
# Here, if it exists we strip it.
regex_replace( "$(installed_version_data[0][version])", "^\d+:(.*)", "$1", "" ),
">=",
"7.3.0"
);
reports:
"ssh_conf for openssh-client '$(installed_version_data[0][version])' should include: $(with)"
with => join( ", ", getindices( ssh_conf ) );
}Executing the policy we can see that only configuration for HashKnownHosts should be included since 6.9p1-3ubuntu0.6 is not greater than or equal to 7.3.0.
cf-agent -Kf /tmp/feature-friday-37-2.cf
R: ssh_conf for openssh-client '6.9p1-3ubuntu0.6' should include: HashKnownHosts
Happy Friday! 🎉
Checkout the rest of the posts in the series.
-
The
version_compare()function was introduced in CFEngine 3.23.0, find the documentation for it here: https://docs.cfengine.com/docs/3.23/reference-functions-version_compare.html ↩︎ -
The OpenSSH 7.3 release notes indicate addition of
Includedirective forssh_configfiles: https://www.openssh.com/txt/release-7.3 ↩︎