boolean like operation for puppetdb query

47 views
Skip to first unread message

Matt Zagrabelny

unread,
Feb 11, 2022, 4:29:01 PM2/11/22
to Puppet Users
Greetings,

I have a puppetdb installation that I leverage by querying from my manifests.

I'd like to have a boolean-like operation for puppetdb that pretty much tests if the current node has a given class as part of the catalog. Here is my current code:

        $query = [
            'resources[certname] {',
            'type = "Class"',
            'and',
            "title = \"fail2ban\"",
            'and',
            "certname = \"${trusted['certname']}\"",
            '}',
        ]
        $this_host_has_fail2ban = puppetdb_query(
            $query.join(' ')
        ).map |$entity| {
            $entity["certname"]
        }

        if "${trusted['certname']}" in $this_host_has_fail2ban {
            $shall_allow_from_internet = true
        }
        else {
            $shall_allow_from_internet = false
        }

Is there a simpler mechanism to find out if a node has a given class in its catalog?

Thanks for any help!

-m

Daniel Krämer

unread,
Feb 15, 2022, 4:22:32 AM2/15/22
to puppet...@googlegroups.com
Hi,

no answer yet? Or did miss them? I'm not a puppet professional but i may provide some other approaches and an opinion.

There is the function defined() https://puppet.com/docs/puppet/7/function.html#defined , but it's tricky because you need to be 100% sure that in your example class fail2ban is included before the function call.
Here is an example:

❯ cat profile/manifests/test.pp
class profile::test (
) {
  notify {"this is test.": }
}

❯ cat profile/manifests/test2.pp
class profile::test2 {
  if defined(Class["profile::test"]) {
    notify { "This ist test2. test is also here.": }
  } else {
    notify { "This ist test2. i'm alone. i'm cold.": }
  }
}

❯ cat profile/manifests/wrapper1.pp
class profile::wrapper1 {
  include profile::test
  include profile::test2
}

❯ cat profile/manifests/wrapper2.pp
class profile::wrapper2 {
  include profile::test2
  include profile::test
}

Performing puppet runs with ('profile::wrapper1',)
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Retrieving locales
Info: Loading facts
Info: Caching catalog for bullseye.local
Info: Applying configuration version '1644913701'
Notice: this is test.
Notice: /Stage[main]/Profile::Test/Notify[this is test.]/message: defined 'message' as 'this is test.'
Notice: This ist test2. test is also here.
Notice: /Stage[main]/Profile::Test2/Notify[This ist test2. test is also here.]/message: defined 'message' as 'This ist test2. test is also here.'
Notice: Applied catalog in 16.16 seconds

Performing puppet runs with ('profile::wrapper2',)
Reading package lists...
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Retrieving locales
Info: Loading facts
Info: Caching catalog for bullseye.local
Info: Applying configuration version '1644913738'
Notice: This ist test2. i'm alone. i'm cold.
Notice: /Stage[main]/Profile::Test2/Notify[This ist test2. i'm alone. i'm cold.]/message: defined 'message' as 'This ist test2. i\'m alone. i\'m cold.'
Notice: this is test.
Notice: /Stage[main]/Profile::Test/Notify[this is test.]/message: defined 'message' as 'this is test.'
Notice: Applied catalog in 15.80 seconds

For me this wouldn't be reliable enough to use.


Another approach would be to create a fact. https://puppet.com/docs/puppet/7/external_facts.html

❯ cat profile/facts.d/fail2ban.sh
#!/bin/sh
FAIL2BAN="/usr/bin/fail2ban-client"
[ -x ${FAIL2BAN} ] && echo fail2ban_version=$(/usr/bin/fail2ban-client --version)

❯ cat profile/manifests/test3.pp
class profile::test3 {
  if $facts["fail2ban_version"] {
    notify {"The fail2ban version is ${facts['fail2ban_version']}":}
  }
}

But this solution has the same drawbacks as yours, it adds to execution time and it can only be true on the second puppetrun.

Imho you should find the point where you include fail2ban and do your stuff there. Or you can wrap fail2ban.


I'm sure there are puppetnerds out there with way better advice.

Greetings,
Daniel



--
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/bc6d5bf6-df6f-4850-ab35-59edc49e13c6n%40googlegroups.com.

Matt Zagrabelny

unread,
Feb 16, 2022, 8:46:52 PM2/16/22
to Puppet Users
Hey Daniel,

Thanks for the reply and hints about "defined".

I ended up putting the puppetdb code into a function and calling the function:

$ cat modules/util/functions/does_host_have_class.pp
function util::does_host_have_class(
    String $class
) >> Boolean {
    $func_name = "util::does_host_have_class()"


    $query = [
        'resources[certname] {',
        'type = "Class"',
        'and',
        "title = \"${class}\"",
        'order by certname',
        '}',
    ]  
    $hosts_with_class = puppetdb_query(
        join($query, ' ')
    ).map |$entity| {
        $entity["certname"]
    }  

    "${trusted['certname']}" in $hosts_with_class
}

Cheers!

-m
Reply all
Reply to author
Forward
0 new messages