facter to return version of an installed package

706 views
Skip to first unread message

Helmut Schneider

unread,
May 14, 2019, 5:19:43 AM5/14/19
to puppet...@googlegroups.com
Hi,

before I reinvent the wheel:

Does anyone know a custom fact that returns the version of an installed
package (if it is installed)?

I assume it would be something like

Facter.add(:package_version) do
setcode do
osfamily = Facter.value(:osfamily)
case osfamily
when /ubuntu|debian/
Facter::Util::Resolution.exec("dpkg -l '*$my_package*' | grep
^ii")
when 'freebsd'
Facter::Util::Resolution.exec("pkg info -ix $my_package*")
end
end
end

but I have not found out yet how to pass a variable to facter.

helmut@ubuntu:~$ puppet -V
6.4.2
helmut@ubuntu:~$ facter -v
3.13.2
helmut@ubuntu:~$

Thank you!

Ben Ford

unread,
May 14, 2019, 2:28:46 PM5/14/19
to puppet...@googlegroups.com
You cannot pass a variable to facter, because that's not how its model is designed. Facter is a tool that runs to gather relatively static system information, called facts. A fact with a given name has a given value. In other words, you need one unique fact for every value you're gathering. (though it can be nested into a data structure if needed.)

This is generally considered an anti-pattern in most cases. If you're reactive then your configuration rules aren't prescriptive. In other words, you cannot look at your code and predict what it will do. This means that your configuration isn't repeatable and you get yourself back into the bad old days where nobody really knew for sure what was on any given machine, or if it would even boot up again if you brought it down.

Instead, you should prescribe the version of the package in question. In other words, move away from "if $package is version 1, then use config A, and if it's version 2 use config B" to "this node should be package version 2 with config B."

To be sure, this pattern doesn't always work and you don't always have control over the full system. What's your use case that you're trying to solve?

--
You received this message because you are subscribed to the Google Groups "Puppet Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/xn0ltx8j23azj63000%40news.gmane.org.
For more options, visit https://groups.google.com/d/optout.

Helmut Schneider

unread,
May 15, 2019, 9:45:44 AM5/15/19
to puppet...@googlegroups.com
Ben Ford wrote:

> To be sure, this pattern doesn't always work and you don't always have
> control over the full system. What's your use case that you're trying
> to solve?

if $operatingsystem == "Ubuntu" {
if versioncmp($facts['os']['release']['full'], '18') >= 0 {
file { "/etc/netplan/windows-dhcp.yaml":
mode => '0644',
owner => "${rootUID}",
group => "${rootGID}",
content =>
epp("${module_name}/etc/netplan/windows-dhcp.yaml.epp"),
}
}
}

I would like to check if netplan is installed before doing so.

onlyif => 'test -d /etc/netplan',

would help. Or

onlyif => 'test -n "`dpkg -l | grep netplan | grep ^ii`"',

but I thought a custom fact would be more efficient.

Chris Taylor

unread,
May 15, 2019, 7:04:50 PM5/15/19
to Puppet Users
You'd be better declaring netplan as a requires:

if $operatingsystem == "Ubuntu" { 
  if versioncmp($facts['os']['release']['full'], '18') >= 0 { 
    package { 'netplan':
      ensure => present,
    }
    file { "/etc/netplan/windows-dhcp.yaml": 
      mode    => '0644', 
      owner   => "${rootUID}", 
      group   => "${rootGID}", 
      content => 
epp("${module_name}/etc/netplan/windows-dhcp.yaml.epp"), 
      requires => Package['netplan'],
    } 
  } 


If you only want netplan on specific systems, then you'd want that delcaration to be done in such a way to only apply to said systems, I leave that as an exercise for the reader, as the mechanism that is best depends heavily upon your own setup.

Helmut Schneider

unread,
May 16, 2019, 11:50:45 AM5/16/19
to puppet...@googlegroups.com
Chris Taylor wrote:

> if $operatingsystem == "Ubuntu" {
> if versioncmp($facts['os']['release']['full'], '18') >= 0 {
> package { 'netplan':
> ensure => present,
> }
> file { "/etc/netplan/windows-dhcp.yaml":
> mode => '0644',
> owner => "${rootUID}",
> group => "${rootGID}",
> content =>
> epp("${module_name}/etc/netplan/windows-dhcp.yaml.epp"),
> requires => Package['netplan'],
> }
> }
> }
>
> If you only want netplan on specific systems, then you'd want that
> delcaration to be done in such a way to only apply to said systems, I
> leave that as an exercise for the reader, as the mechanism that is
> best depends heavily upon your own setup.

I don't want to install netplan but if it exists do the needful:

exec { 'test_for_netplan':
path => ['/usr/bin','/usr/sbin','/bin','/sbin'],
command => "/bin/true",
onlyif => 'test -n "`dpkg -l | grep netplan | grep ^ii`"',
}

if $operatingsystem == "Ubuntu" {
if versioncmp($facts['os']['release']['full'], '18') >= 0 {
file { "/etc/netplan/windows-dhcp.yaml":
mode => '0644',
owner => "${rootUID}",
group => "${rootGID}",
content =>
epp("${module_name}/etc/netplan/windows-dhcp.yaml.epp"),
require => Exec['test_for_netplan'],
}
}
}

jcbollinger

unread,
May 17, 2019, 9:53:46 AM5/17/19
to Puppet Users


On Thursday, May 16, 2019 at 10:50:45 AM UTC-5, Helmut Schneider wrote:

I don't want to install netplan but if it exists do the needful:


I reiterate Ben Ford's comments: This is generally considered an anti-pattern.

It is better from an administrative perspective for you to know which systems are supposed to have netplan, and to unconditionally ensure that it is both installed and properly configured on those systems.  You could go so far as to ensure it absent from other machines, though whether that's appropriate is a policy question whose answer will vary.  In any case, it is not only reasonable but safer for all concerned to demand that the identities of machines for which you are going to manage netplan configuration be statically known to you.

With that said, if you insist on treading this path anyway then a custom fact is indeed the right way to convey the wanted information to Puppet, whether that's the version of netplan that's installed, if any, or merely whether netplan is installed at all.  For that you'll want either a fact specific to that purpose (e.g. $::netplan_version), or a more general fact from which you can glean the information (e.g. $::installed_packages).  There is no way to parameterize a fact so that it has different meaning depending on how it is evaluated.


John

Helmut Schneider

unread,
May 20, 2019, 10:45:20 AM5/20/19
to puppet...@googlegroups.com
jcbollinger wrote:

> On Thursday, May 16, 2019 at 10:50:45 AM UTC-5, Helmut Schneider
> wrote:
>
> > I don't want to install netplan but if it exists do the needful:
>
> I reiterate Ben Ford's comments: This is generally considered an
> anti-pattern.
>
> It is better from an administrative perspective for you to know which
> systems are supposed to have netplan, and to unconditionally ensure
> that it is both installed and properly configured on those systems.
> You could go so far as to ensure it absent from other machines,
> though whether that's appropriate is a policy question whose answer
> will vary. In any case, it is not only reasonable but safer for all
> concerned to demand that the identities of machines for which you are
> going to manage netplan configuration be statically known to you.

I see your point and in 99% of my tasks with Puppet I agree.

> With that said, if you insist on treading this path anyway then a
> custom fact is indeed the right way to convey the wanted information
> to Puppet, whether that's the version of netplan that's installed, if
> any, or merely whether netplan is installed at all. For that you'll
> want either a fact specific to that purpose (e.g.
> $::netplan_version), or a more general fact from which you can glean
> the information (e.g. $::installed_packages). There is no way to
> parameterize a fact so that it has different meaning depending on how
> it is evaluated.

Nevertheless knowing what packages are installed might help me, too,
and although my ruby knowledge is very rudimentary here is some output:

#!/usr/bin/ruby

Facter.add('installed_packages') do
confine :osfamily => /freebsd|debian/

setcode do
begin
packages_hash = {}
if Facter.value(:osfamily) == 'Debian'
packages = Facter::Util::Resolution.exec('/usr/bin/dpkg -l |
/bin/grep ^ii | /usr/bin/awk \'{print $2"|"$3}\'')
elsif Facter.value(:osfamily) == 'FreeBSD'
packages = Facter::Util::Resolution.exec('/usr/sbin/pkg info |
/usr/bin/awk \'{print $1}\' | /usr/bin/sed -E
\'s#-([[:digit:]])#\|\1#g\'')
end

packages.each_line do |package|
if package
name,*version = package.chomp.split(/\|/)
packages_hash[name] = version.join("")
end
end
packages_hash
rescue
end
end
end

Reply all
Reply to author
Forward
0 new messages