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! 🎉
-
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
Include
directive forssh_config
files: https://www.openssh.com/txt/release-7.3 ↩︎