3

We're starting to use Puppet as a way of managing configuration across our various servers. We have a Portage repository with a few packages of our own, with each machine using Layman to manage the overlays.

It's easy enough to install packages in puppet, but how should we make sure Layman is configured? Is there a module out there?

Marcus Downing
  • 788
  • 10
  • 18

2 Answers2

3

I don't know the gentoo, portage or layman specifics, and I don't see any existing modules in the puppet module forge but from a quick look at some gentoo layman documentation it looks like it would be fairly straightforward to write it yourself with Puppet:

stage { "first": before => Stage[main] } # Set up first stage, before main

class layman { # "overlays"?
  package { "layman": ensure => present }
  # Then everything else (file, execs, whatever) to configure layman,
  # overlays, etc
  # Looks to me like you need to change /etc/make.conf, /etc/layman/layman.cfg
  # and write some execs that run "layman -a <overlay-name>"
  # depending on output of "layman -i <overlay-name>"
  # or possibly grepping /var/lib/layman/overlays.xmls
}

class{"layman": stage => "first"} # Set layman class to run in the first stage

Instead of using stages you could instead have require => Class[layman] on all the package statements that need it. Using require is more verbose; I'd use it if I only needed a few things, or if I was requiring a specific overlay. I believe you should generally avoid using requires that cross stage boundaries, however, as it's both redundant and likely to tickle weird bugs.

Alternate, depending on what you need, avoids stages and only does explicit require ordering. I do stuff like this with RHEL and yum repos:

# In a "layman" module.
class layman {
  [...]
}

define layman::overlay() {
  exec {
    "layman -a $name":
      require => Class[layman],
      creates => "/var/lib/layman/${name}",
  }
}

class layman::overlay::php {
  layman::overlay { "php": }
}

class layman::overlay::apache2 {
  layman::overlay { "apache2": }
}

class apache {
  include layman::overlay::apache2
  package { "apache2":
    ensure => present,
    require => Class[layman::overlay::apache2];
  }
  file { "/etc/apache2/conf.d/whatever.conf":
    source => "...",
    require => Package[apache2],
    notify => Service[apache2];
  }
  service { "apache2":
    ensure => running,
    enable => true,
    require => [ Package[apache2], File["/etc/apache2/conf.d/whatever.conf"] ];
  }

}

# "yoursite" module or "somephpapp" module
class yoursite::somephpapp {
  include apache
  include layman::overlay::php
  package { "somephpapp":
    ensure => present,
    require => [ Class[apache], Class[layman::overlay::php] ];
  }
  file {
    "/path/to/somephpapp.conf":
      source => "...",
      require => Package[somephpapp],
      notify => Service[apache2]; # probably not actually required, example
  }
}
Andor
  • 103
  • 2
freiheit
  • 14,544
  • 1
  • 47
  • 69
1

Further to freiheit's answer, here's what I ended up with.

class packages-layman {
    Exec { path => '/usr/bin:/bin:/usr/sbin:/sbin', loglevel => 'debug' }

    package { 'app-portage/layman': ensure => 'installed' }

    file { '/etc/eix-sync.conf':
        ensure => present,
        content => '*',
    }

    line { 'layman-make.conf-overlay':
        file => '/etc/make.conf',
        line => 'source /var/lib/layman/make.conf',
    }

    exec { 'layman-list':
        command => 'layman -o "http://dev.mycompany.com" -L',
        require => [
            Package['app-portage/layman'],
            Service['openvpn']
        ],
    }

    exec { 'layman-my-overlay':
        command => 'layman -o "http://dev.mycompany.com" -a myoverlay',
        returns => [0,1],
        require => Exec['layman-list'],
    }

    exec { 'layman-eix-sync':
        command => 'eix-sync',
        require => [
            File['/etc/eix-sync.conf'],
            Line['layman-make.conf-overlay'],
            Exec['layman-my-overlay'],
        ],
    }
}

Note that the 'layman-list' exec is there to overcome what appears to be a bug in the version of layman on Gentoo that prevents overlays from working until they've been listed.

Puppet can choose to run commands in any random order, so the order of the various tasks is enforced with all the require entries. To make sure a task happens after this one, use a require like so:

package { 'app-misc/my-custom-package':
    ensure => 'installed',
    require => Exec['layman-eix-sync']
}

It needs this definition of line from the Puppet wiki to let you edit single lines of a bigger file:

define line($file, $line, $ensure = 'present') {
    case $ensure {
        default : { err ( "unknown ensure value ${ensure}" ) }
        present: {
            exec { "/bin/echo '${line}' >> '${file}'":
                unless => "/bin/grep -qFx '${line}' '${file}'"
            }
        }
        absent: {
            exec { "/usr/bin/perl -ni -e 'print unless /^\\Q${line}\\E\$/' '${file}'":
                onlyif => "/bin/grep -qFx '${line}' '${file}'"
            }
        }
    }
}
Marcus Downing
  • 788
  • 10
  • 18
  • There is of course one problem with this solution: it runs eix-sync every time, which a typical puppet agent that means every half hour. That's way more often than is considered 'polite'. – Marcus Downing Mar 21 '11 at 14:27