I have a couple hosts which have Docker containers on them, so I've defined a class called apps::docker which installs Docker on the hosts:
class apps::docker {
include apps::docker::repository, apps::docker::install,
apps::docker::service
Class["Apps::Docker::Repository"] ->
Class["Apps::Docker::Install"] ->
Class["Apps::Docker::Service"]
Package["Docker"] ~> Service["Docker"]
}
class apps::docker::repository {
apt::source { 'docker':
location => "http://get.docker.io/ubuntu",
key => "A88D21E9",
release => "docker",
repos => "main",
include_src => false
}
}
class apps::docker::install {
package { 'docker':
name => "lxc-docker",
ensure => present
}
class apps::docker::service {
service { 'docker':
provider => 'upstart',
enable => true,
ensure => running,
hasrestart => true,
hasstatus => true
}
}
Pretty simple stuff, actually.
The problem is that when I try to define a class which depends on this class, the execution happens out of order and commands fail. For example, my class profiles::shiningstar::containers depends on apps::docker as defined in profiles::shiningstar:
class profiles::shiningstar {
include apps::docker
include profiles::shiningstar::containers
Class["Apps::Docker"] -> Class["Profiles::Shiningstar::Containers"]
}
Unfortunately, this doesn't work as seen below:
Error: /Stage[main]/Profiles::Shiningstar::Containers::Puppetmaster::Pull/Docker::Image[rfkrocktk/puppetmaster:1.0.5]/Exec[docker pull rfkrocktk/puppetmaster:1.0.5]: Could not evaluate: Could not find command 'docker'
... (similar errors)
Notice: /Stage[main]/Apps::Docker::Repository/Apt::Source[docker]/Apt::Key[Add key: A88D21E9 from Apt::Source docker]/Apt_key[Add key: A88D21E9 from Apt::Source docker]/ensure: created
It's executing things completely out of order. What's wrong with my configuration and how can I specify that ALL of the dependencies of apps::docker must be satisfied before profiles::shiningstar::containers?
You probably want to contain the inner classes instead of just including them.
class apps::docker {
contain apps::docker::repository
contain apps::docker::install
contain apps::docker::service
Class['apps::docker::repository']
->
Class['apps::docker::install']
~>
Class['apps::docker::service']
}
Note that it makes sense (in your case at least) to make the ::install class as a whole notify all of the ::service class. The makes you more flexible in refactoring the respective implementation of those classes.
Edited after first comment - don't try to put chaining arrows between contain statements.
You should use an anchor, that will ensure that all dependencies are built
class apps::docker {
include apps::docker::repository, apps::docker::install,
apps::docker::service
Class["Apps::Docker::Repository"] ->
Class["Apps::Docker::Install"] ->
Class["Apps::Docker::Service"] ->
anchor{"apps::docker":}
Package["Docker"] ~> Service["Docker"]
}
Related
I am installing from github using puppet-vcsrepo. The code looks something like this:
class demo_class(
$my_repo = undef,
$my_tag = undef,
){
vcsrepo { "$my_repo",
path => "/home/user/$my_repo",
source => 'git#github.com:7yl4r/$my_repo.git',
ensure => latest,
provider => git,
}
# then declare resources specific to my_tag
}
This works just fine when called only once, but I am iterating over a list and installing dependencies so this resource sometimes gets declared twice. I think this is roughly equivalent to the code below.
class {"demo_class":
my_repo => test_repo,
my_tag => test_tag1,
}
class {"demo_class":
my_repo => test_repo,
my_tag => test_tag2,
}
Doing this yields a server-side "Duplicate declaration" error because vcsrepo is trying to map the the same path twice. However, this is exactly the behavior I want: for both resources declared by demo_class to require the same repo in the same location. This is so that I can declare one or more resources using demo_class and be sure the repo given by my_repo (which may be common to multiple my_tags) is there.
How can I modify this class so that I can call it twice without hitting an error?
I see the problem.
I reproduced the issue using this code:
define my_vcs_repo ($myRepo, $myTag) {
vcsrepo { "$myRepo-$myTag":
path => "/home/user/$myRepo",
source => "git#github.com:7yl4r/$myRepo.git",
revision => $myTag,
ensure => latest,
provider => git,
}
}
$data = [
{
myRepo => testRepo,
myTag => testTag1,
},
{
myRepo => testRepo,
myTag => testTag2,
},
]
$data.each |$i, $ref| {
$myRepo = $ref['myRepo']
$myTag = $ref['myTag']
my_vcs_repo { "$myRepo-$i":
myRepo => $myRepo,
myTag => $myTag,
}
}
That then results in:
Puppet::PreformattedError:
Evaluation Error: Error while evaluating a Resource Statement, Evaluation Error: Error while evaluating a Resource Statement, Cannot alias Vcsrepo[testRepo-testTag2] to ["/home/user/testRepo"] at /
Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:3; resource ["Vcsrepo", "/home/user/testRepo"] already declared at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/
manifests/init.pp:3 at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:3:5 at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:26 on node alexs-macbook-pro.local
The problem is that you are asking Puppet to clone the same Git module to a directory but with two different tags checked out. That does not make sense.
The fix is that you need to specify a unique path in the vcsrepo path attribute, e.g.:
vcsrepo { "$myRepo-$myTag":
path => "/home/user/$myRepo-$myTag",
source => "git#github.com:7yl4r/$myRepo.git",
revision => 'production',
ensure => latest,
provider => git,
}
By the way, I notice you are using camelCase for your variables. Don't do that. Aside from the fact that it is not idiomatic for Puppet, there are things that will break in some versions of Puppet/Rspec puppet that I have seen.
Use snake_case for your variable names and class parameter names.
Update
The question has been edited, and it is now a question about how to declare the same vcsrepo in more than one class.
In general, try to refactor so that you do not need to do this in the first place. In other words, just move it out of this class and put it somewhere that is only expected to be declared once.
If you cannot do this for some reason, then you can also use virtual resources, which will allow you to declare it in multiple classes that will be declared on the same node.
To do that, you just have to rewrite what you have there as:
#vcsrepo { $my_repo:
path => "/home/user/$my_repo",
source => "git#github.com:7yl4r/$my_repo.git",
ensure => latest,
provider => git,
}
realize Vcsrepo[$my_repo]
Keep in mind that you will not be able to declare the class demo_class twice on the same node either. You would need to turn it into a defined type, as I did above.
It is mentioned in the comments below that you can also use ensure_resource and ensure_resources; see docs in stdlib.
I need to execute something as the very last thing of a puppet apply run. I tried to do that by defining a stage 'last', but the syntax restrictions on declaring a class in resource mode are a problem.
Is there a good way to use stages like this? Or is there some other way to make sure some class is executed last?
for example, this gives me an error for a duplicate declaration(sometimes, and I'm not sure why at this point):
class xyz::firstrun {
exec { 'exec_firstrun':
onlyif => '/usr/bin/test -e /tmp/firstrun.sh',
command => '/tmp/firstrun.sh',
path => ['/usr/bin/','/usr/sbin'],
creates => '/tmp/firstrun.done',
}
}
class { 'xyz::firstrun':
stage => last,
}
Sometimes, the firstrun class runs without error, but in the main stage.
I'm not a big fan of run stages, but they are the the right tool for this job. It's a bit unclear exactly what gives you the duplicate declaration error you describe, but if, for example, your class definition and class declaration both appear in the same file, then that might be a problem.
Here's how a solution using run stages might look:
environments/production/modules/site/manifests/stages.pp
class site::stages {
stage { 'last':
# Stage['main'] does not itself need to be declared
require => Stage['main'],
}
}
environments/production/modules/xyz/manifests/firstrun.pp
class xyz::firstrun {
exec { 'exec_firstrun':
onlyif => '/usr/bin/test -e /tmp/firstrun.sh',
command => '/tmp/firstrun.sh',
path => ['/usr/bin/','/usr/sbin'],
creates => '/tmp/firstrun.done',
}
}
environments/production/manifests/nodes.pp
node 'foobar.my.com' {
include 'site::stages'
include 'something::else'
# Must use a resource-like declaration to assign a class to a stage
class { 'xyz::firstrun':
stage => 'last'
}
}
Note that although include-like class declarations are generally to be preferred, you must use a resource-like declaration to assign a class to a non-default stage. You must therefore also be careful to avoid declaring such classes more than once.
You can use puppet relationship and ordering to do this.
(1) If you want to execute the entire class at the end, you can include your class in init.pp and user ordering arrow (->) to execute it after all other classes.
example:
file: /etc/puppet/modules/tomcat/init.pp
class tomcat {
include ::archive
include ::stdlib
class { '::tomcat::tomcatapiconf': }->
class { '::tomcat::serverconfig': }
}
(2) If you want a specific resource in a class to execute at the end, you can use the same arrow (->) within the class or use before or require in the resource
example:
file { '/etc/profile.d/settomcatparam.sh':
ensure => file,
before => File_line['/etc/profile.d/settomcatparam.sh'],
}
file_line { '/etc/profile.d/settomcatparam.sh':
path => '/etc/profile.d/settomcatparam.sh',
ine => 'export LD_LIBRARY_PATH=/usrdata/apps/sysapps/apr/lib:/usrdata/apps/sysapps/apr-util/lib:/usrdata/apps/sysapps/tomcat-native/lib:$LD_LIBRARY_PATH',
}
OR
exec { 'configure apr-util':
cwd => "/tmp/apr-util-${tomcat::aprutilversion}/",
command => "bash -c './configure --prefix=/usrdata/apps/sysapps/apr-util --with-apr=/usrdata/apps/sysapps/apr'",
} ->
exec { 'make apr-util':
cwd => "/tmp/apr-util-${tomcat::aprutilversion}/",
command => 'make',
}
You can also use combination of before, require and ->. Just make sure you don't create a dependency cycle.
If I have two class's in my own puppet module and class 'b' has a dependency on class 'a'. How can I express this in my require statement:
# a.pp
class rehan::a {
package { 'javaruntime':
ensure => latest,
provider => chocolatey
}
}
# b.pp
class rehan::b {
file { 'C:\foo':
ensure => present,
require => Package['?????']
}
}
# site.pp
node default {
include rehan::a
include rehan::b
}
If you want to express a dependency of class b on class a (and also ensure that a is in the catalog):
class rehan::b {
require rehan::a
}
If you just one resource on rehan::b to depend on class A:
class rehan::b {
include rehan::a # ensure the class is in the catalog
file { 'C:\foo':
ensure => present,
require => Class['rehan::a'],
}
}
You can also express this relationship anywhere with Class['rehan::a'] -> Class['rehan::b'] (assuming both are included in the catalog).
Puppet Version: 3.2.4 (Puppet Enterprise 3.0.1)
In order to better support nagios cfg_dir and cfg_file directives in the config file, I've created the following class(es), one for each option:
# Class to add a cfg_dir to the nagios configuration file
class nagios::server::cfg_dir (
$config_dir,
$nagios_user,
$nagios_group,
$nagios_config_file = '/etc/nagios3/nagios.cfg',
)
{
# Build the config dir
file {$config_dir:
ensure => directory,
owner => $nagios_user,
group => $nagios_group,
mode => '0750',
}
# Append cfg_dir=$config_dir path to nagios.cfg file
augeas { "cfg_dir=$config_dir in $nagios_config_file":
incl => "$nagios_config_file",
lens => 'NagiosCfg.lns',
changes => "set cfg_dir/[last()+1] ${config_dir}",
require => File[$nagios_config_file],
}
}
Trying to use this construct inside nagios::server, I have this:
# Set up config directories
each($cfg_dir) |$x| {
class { 'nagios::server::cfg_dir':
config_dir => $x,
nagios_user => $nagios_user,
nagios_group => $nagios_group,
nagios_config_file => $nagios_config_file,
}
}
Which should, in theory, execute the class instantiation for each path passed in to the nagios::server class like so:
class{'::nagios::server': cfg_dir => ['/etc/nagios.d','/etc/nagios/objects'] }
However, I run into this issue:
Error: Could not match |$x| at /tmp/vagrant-puppet-1/modules-2/nagios/manifests/server.pp:181 on node localhost.localdomain
Can someone provide a working example of each in use? Am I expecting too much from this built-in puppet function?
Aside from a few of the code grammar issues above, I've found that this construct is only evaluated in the future parser:
puppet apply --parser=future --modulepath ...
http://docs.puppetlabs.com/puppet/latest/reference/experiments_lambdas.html
Still getting past other dependency issues. What pattern would I use to support this with the current parser instead of future? A custom function?
The answer to my follow-up question is to use defined types:
http://docs.puppetlabs.com/learning/definedtypes.html
Simply changing the above code from a class to a define and assign $config_dir the value from $target,
define nagios::server::cfg_dir (
$config_dir = $target,
$nagios_user,
$nagios_group,
$nagios_config_file = '/etc/nagios3/nagios.cfg',
){...
you can use constructs such as:
nagios::server::cfg_dir { '/etc/nagios.d/', '/etc/nagios/objects':
nagios_user => 'nagios',
nagios_group => 'nagios'
}
This solves the issue for me.
I want to achieve dependency between puppet classes, so that the classes (their content) get executed in a deterministic way. Reading the documentation I came up with the following two ways:
ORDERING
class Level1 {
Class['Level2']->Class[Level1']
package { "A":
ensure => "installed"
}
}
class Level2 {
include Level3
package { "B":
ensure => "installed"
}
}
class Level3 {
package { "C":
ensure => "installed"
}
}
according to the documentation
-> (ordering arrow) Causes the resource on the left to be applied before the resource on the right. Written with a hyphen and a
greater-than sign.
I expect the following to happen:
Level2 is called before Level1
package B or C get installed (order can be random because it was not specified)
package A gets installed.
REQUIRE
class Level1 {
require Level2
package { "A":
ensure => "installed"
}
}
class Level2 {
require Level3
package { "B":
ensure => "installed"
}
}
class Level3 {
package { "C":
ensure => "installed"
}
}
According to the documentation
cause every resource in the required classes to be applied
before any of the resources in the requiring class.
I expect the following to happen:
Level3 is called by Level2
package C gets installed
Level2 is called by Level1
package B gets installed
package A gets installed
alternative ORDERING
class Level1 {
Class['Level3']->Class['Level2']->Class[Level1']
package { "A":
ensure => "installed"
}
}
class Level2 {
package { "B":
ensure => "installed"
}
}
class Level3 {
package { "C":
ensure => "installed"
}
}
level 1 is needed by level 2
level 2 is needed by level 3
package C gets installed
package B gets installed
package A gets installed
Are my assumptions and conclusions correct?
I've been messing with these kind of dependencies for a while now and they don't seem to behave how I imagine them to behave. Maybe I'm missing something when it comes to the usage of require and -> when used with classes. Any feedback is appreciated!!
EDIT1
staging seems to be a good mechanism for fixing the dependency between two classes (see here), since you have to manually define the stage dependencies between each class pair. How would you use staging with three or four class levels?
EDIT2
Consider this more realistic case, where classes a used as wrappers to install multiple packages at the same time.
Every node is loading some pre-defined role:
node 'some-host' {
include role::processing_machine
}
the role is defined as follows:
class role::processing_machine {
include role::ubuntu_desktop_standard_software
include xorg::lts_12_04_quantal
include software::standard_packages
# define order in which classes should be executed
Class["role::ubuntu_desktop_standard_software"] -> Class["xorg::lts_12_04_quantal"] -> Class["software::standard_packages"] -> Class["role::processing_machine"]
}
and here's the role::ubuntu_desktop_standard_software definition:
class role::ubuntu_desktop_standard_software {
include role
include profile::ubuntu_desktop
include role::server::no_apache
# define order in which classes should be executed.
Class["role"] -> Class["profile::ubuntu_desktop"] -> Class["role::server::no_apache"] -> Class["role::ubuntu_desktop_standard_software"]
}
As you can see, I'm trying to chain multiple classes so that they get executed in a particular order (Class["a"] -> Class["b"]). Previously I've only used include in my classes but puppet would execute the include commands in arbitrary order, so that some commands (which have to run first!) wouldn't run first.
Despite these chaining efforts it still seems like puppet is executing the classes random fashion. What am I doing wrong? Is there a cleaner / better way of defining nodes so that I can be sure they are deployed in a particular, predefined way?
Here is an example :
class L {
package { "C" :
ensure => installed,
require => Package["A", "B",],
}
package { "B" :
ensure => installed,
require => Package["A"],
}
package { "A" :
ensure => installed,
}
}