Theofilos Mouratidis
Theofilos Mouratidis

Reputation: 1156

how to lazily define a class in a puppet manifest

I have a system which has different roles, A B C. There is a class daemon::conf that defines the conf and concats the configs of all the classes arguments e.g.

class daemon::conf (
  global_config = {}
  a_config      = {}
  b_config      = {}
  c_config      = {}
) {
concat::fragment {...}
}

So when I do this:

class hg_mysystem::mycluster::hybrid {
  include daemon::A
  include daemon::B
}

I want to have:

$ cat /etc/mysystem/config
[Global]
...
[A]
...
[B]
...

each daemon is defined as daemon::A, daemon::B, daemon::C, but they call daemon::conf with heir own parameters that are either defined in hiera on inside the .pp manifest files. Now I need to create a node that has 2 or 3 roles (writing include daemon::A;include daemon::B etc), but I get a problem with a class redefinition, because daemon::conf is defined in all A B and C.

My first thought was to define the class on one node and add if defined(Class['daemon::conf']) {add argument to the defined class} else {class{'daemon::conf'...}} but I don't know how to create a dynamic hiera variable from a manifest, or how to do a hiera style assignment from a manifest. I was also searching on how to do a lazy init of the class with those virtual resources, but I don't understand how could that help, when realize doesn't override an argument but with realize you only do this realise Class['daemon::conf'] and not realise Class['daemon::conf'] {b_config={...}}. Is there any way I can restructure daemon::conf with subclasses that notify another class that builds the conf based on the classes' data.

Edit:

I followed the second approach and split daemon::conf to daemon::conf, daemon::conf::A, daemon::conf::B

class daemon::conf (...) {
  concat { '/etc/daemon/conf':
    owner   => 'root',
    group   => 'root',
    mode    => '0664',
    require => Package['daemon'],
  }

  Concat::Fragment <<| target == '/etc/daemon/config' |>>

  concat::fragment { 'daemon.conf':
    tag     => "daemon.conf",
    target  => '/etc/daemon/config',
    order   => '01',
    content => template('daemon/daemon.conf.erb'),
  }
}

define daemon::conf::A (...) {
  include ::daemon::conf

  @@concat::fragment { "${::hostname}.daemon.conf":
    tag     => "daemon.conf",
    target  => '/etc/daemon/config',
    order   => '20',
    content => template('daemon/daemon.conf-A.erb'),
  }

}

class daemon::conf::B (...) {
  include ::daemon::conf

  concat::fragment { $::hostname:
    tag     => "daemon.conf",
    target  => '/etc/daemon/config',
    order   => '10',
    content => template('daemon/daemon.conf-B.erb'),
  }

}

class daemon::A (
  $A_addr,
  $port,
) {
  include ::daemon::conf

  daemon::conf::A { $::hostname:
    addr => $A_addr,
    port => $port,
  }
}

class daemon::B (
  $B_rack_loc,
) {
  include ::daemon::conf

  class {'::daemon::conf::B':
    B_config => {
      B_rack_location => $B_rack_loc,
    }
  }
}

Running puppet on 3 nodes in the same hostgroup I should get:

[user@hostname1: ~]$ cat /etc/daemon/config
[Global]
...
[B]
loc = row RO, rack RA, host hostname1
[A/hostname1 ip]
addr=...
port=...
[A/hostname2 ip]
addr=...
port=...
[A/hostname3 ip]
addr=...
port=...

But instead I get multiple configs of role B as well of all 3 hosts. What mistake do I do and how to fix it? Thanks. Is it the "<<| |>>" statement that should be altered?

Upvotes: 0

Views: 282

Answers (1)

John Bollinger
John Bollinger

Reputation: 180058

Any solution needs to accommodate the fact that the values of all of a given class's parameters are determined the first time a declaration of that class is evaluated, whether the declaration is a resource-like one or an include-like one. Multiple declarations are permitted only if all those evaluated after the first use one of the include-like forms, and this is among the reasons that resource-like class declarations should be avoided under most circumstances.

There are various ways to take those considerations into account. One would be to invert the logic: instead of having all each daemon::X class declare daemon::conf, declare it once, centrally, and pass it a list of roles to configure. Let it then declare appropriate daemon::conf::X classes based on the role list:

class daemon::conf (
  $global_config = {}
  $roles =         []
) {
  concat { ... }
  concat::fragment {...}
  $roles.each { |$role|
    contain "daemon::conf::${role}"
  }
}

Another way would be to absolve the central daemon::conf class from responsibility for declaring the per-role configuration at all. This is possible because you're using Concat to build your config from fragments. One of the key features of puppetlabs::concat is that concat::fragments can be declared independently of each other and of the concat declaring the file to which they contribute:

class daemon::conf (
  $global_config = {}
) {
  concat { ... }
  concat::fragment {...}  # general configuration only
}

class daemon::a::conf (...) {
  concat::fragment {...}  # daemon A configs
}

class daemon::b::conf (...) {
  concat::fragment {...}  # daemon B configs
}

class daemon::a (...) {
  include daemon::conf     # (maybe)
  contain daemon::a::conf
  # ...
}

class daemon::b (...) {
  include daemon::conf     # (maybe)
  contain daemon::b::conf
  # ...
}

...

Upvotes: 2

Related Questions