44
Basic Puppet Module Design Jeremy Kitchen Systems Engineer at NationBuilder

Puppetcamp module design talk

Embed Size (px)

Citation preview

Page 1: Puppetcamp module design talk

Basic Puppet Module DesignJeremy Kitchen

Systems Engineer at NationBuilder

Page 2: Puppetcamp module design talk

What is a module?

Page 3: Puppetcamp module design talk

Package Config Service

Page 4: Puppetcamp module design talk

Module Contents

foo/ manifests/ defaults.pp init.pp install.pp config.pp service.pp templates/ foo.conf.erb

Page 5: Puppetcamp module design talk

manifests/defaults.ppclass foo::defaults { case $osfamily { 'Debian': { $package_name = 'foo-ruby' $service_name = 'foo' $config_path = '/etc/foo/foo.conf' $log_file = '/var/log/foo/foo.log' $storage_path = '/var/lib/foo' } 'RedHat': { $package_name = 'ruby-foo' $service_name = 'foo' $config_path = '/etc/foo.conf' $log_file = '/var/log/foo.log' $storage_path = '/var/lib/foo' } default: { fail("${osfamily} not currently supported by this module") } }}

Page 6: Puppetcamp module design talk

manifests/init.ppclass foo ( $package_ensure = installed, $listen = '127.0.0.1', $port = '1234', $verbose = false,) inherits foo::defaults { if (!is_ip_address($listen)) { fail('listen parameter needs to be an ip address') } $verbose_bool = str2bool($verbose)

include foo::install include foo::config include foo::service

Class['foo::install'] -> Class['foo::config'] ~> Class['foo::service']

anchor { 'foo::begin': before => Class['foo::install','foo::config'], notify => Class['foo::service']; 'foo::end': require => Class['foo::service']; }}

Page 7: Puppetcamp module design talk

manifests/init.pp

class foo ( $package_ensure = installed, $listen = '127.0.0.1', $port = '1234', $verbose = false,) inherits foo::defaults {

Page 8: Puppetcamp module design talk

manifests/init.pp

if (!is_ip_address($listen)) { fail('listen parameter needs to be an ip address') } $verbose_bool = str2bool($verbose)

Page 9: Puppetcamp module design talk

manifests/init.pp

include foo::install include foo::config include foo::service

Class['foo::install'] -> Class['foo::config'] ~> Class['foo::service']

Page 10: Puppetcamp module design talk

manifests/init.pp

anchor { 'foo::begin': before => Class['foo::install','foo::config'], notify => Class['foo::service']; 'foo::end': require => Class['foo::service']; }

Page 11: Puppetcamp module design talk

containment example

include site::mountsinclude site::webappinclude mysqlinclude apache

Class['site::mounts'] -> Class['mysql']Class['site::mounts'] -> Class['apache']Class['mysql'] -> Class['apache']Class['mysql'] -> Class['site::webapp']

Page 12: Puppetcamp module design talk

manifests/init.pp

anchor { 'foo::begin': before => Class['foo::install','foo::config'], notify => Class['foo::service']; 'foo::end': require => Class['foo::service']; }

Page 13: Puppetcamp module design talk

what about contain?

Page 14: Puppetcamp module design talk

manifests/init.ppclass foo ( $package_ensure = installed, $listen = '127.0.0.1', $port = '1234', $verbose = false,) inherits foo::defaults { if (!is_ip_address($listen)) { fail('listen parameter needs to be an ip address') } $verbose_bool = str2bool($verbose)

include foo::install include foo::config include foo::service

Class['foo::install'] -> Class['foo::config'] ~> Class['foo::service']

anchor { 'foo::begin': before => Class['foo::install','foo::config'], notify => Class['foo::service']; 'foo::end': require => Class['foo::service']; }}

Page 15: Puppetcamp module design talk

manifests/install.pp

class foo::install inherits foo { package { $package_name: ensure => $package_ensure, }}

Page 16: Puppetcamp module design talk

manifests/config.pp

class foo::config inherits foo { file { $config_path: content => template('foo/foo.conf.erb'), owner => 'root', group => 'root', mode => '0644'; }}

Page 17: Puppetcamp module design talk

Module Contents

class foo::service inherits foo { service { $service_name: ensure => running, enable => true, }}

Page 18: Puppetcamp module design talk

templates/foo.conf.erb

[main]listen = <%= @listen %>port = <%= @port %>verbose = <%= @verbose_bool ? 'yes' : 'no' %>log_file = <%= @log_file %>storage_dir = <%= @storage_dir %>

Page 19: Puppetcamp module design talk

Module Contents

foo/ manifests/ defaults.pp init.pp install.pp config.pp service.pp templates/ foo.conf.erb

Page 20: Puppetcamp module design talk

example

# profile/manifests/server.ppclass profile::server { class { 'foo': port => 3389, verbose => true, }}

Page 21: Puppetcamp module design talk

example

# hiera/global.yamlfoo::port: 3389foo::verbose: true

# profile/manifests/server.ppclass profile::server { include ::foo}

Page 22: Puppetcamp module design talk

conf.d

Page 23: Puppetcamp module design talk

Module Contents

foo/ manifests/ defaults.pp init.pp install.pp config.pp service.pp plugin.pp templates/ foo.conf.erb plugin.conf.erb

Page 24: Puppetcamp module design talk

manifests/defaults.pp

class foo::defaults { case $osfamily { 'Debian': { $package_name = 'foo-ruby' $service_name = 'foo' $config_path = '/etc/foo/foo.conf' $log_file = '/var/log/foo/foo.log' $storage_path = '/var/lib/foo' $conf_d_path = '/etc/foo/conf.d' $plugin_path = '/usr/share/foo/plugins' } 'Redhat': { $package_name = 'ruby-foo' $service_name = 'foo' $config_path = '/etc/foo.conf' $log_file = '/var/log/foo.log' $storage_path = '/var/lib/foo' $conf_d_path = '/etc/foo.d/' $plugin_path = '/usr/lib/foo/plugins' } }}

Page 25: Puppetcamp module design talk

manifests/config.ppclass foo::config inherits foo { file { $config_path: content => template('foo/foo.conf.erb'), owner => 'root', group => 'root', mode => '0644'; $conf_d_path: ensure => directory, purge => true, recurse => true, owner => 'root', group => 'root', mode => '0755'; }}

Page 26: Puppetcamp module design talk

templates/foo.conf.erb

[main]listen = <%= @listen %>port = <%= @port %>verbose = <%= @verbose_bool ? 'yes' : 'no' %>log_file = <%= @log_file %>storage_dir = <%= @storage_dir %>@include <%= @conf_d_path %>/*.conf

Page 27: Puppetcamp module design talk

manifests/plugin.ppdefine foo::plugin ( $type, $path = "${foo::defaults::plugin_path}/${name}.rb", $verbose = undef,) { include foo::defaults

validate_absolute_path($path) $verbose_bool = str2bool($verbose)

file { "${foo::defaults::conf_d_path}/${name}.conf": content => template('foo/plugin.conf.erb'), owner => 'root', group => 'root', mode => '0755', notify => Class['foo::service'], }}

Page 28: Puppetcamp module design talk

templates/plugin.conf.erb

[plugin <%= @name %>]type = <%= @type %>path = <%= @path %>

<%- if [email protected]? -%>verbose = <%= @verbose_bool ? 'yes' : 'no' %><%- end -%>

Page 29: Puppetcamp module design talk

example

include foo

foo::plugin { 'webapp': type => 'passenger'; 'db': type => 'mysql', verbose => true, path => '/usr/local/share/custom_mysql.rb';}

Page 30: Puppetcamp module design talk

role/profile exampleclass profile::app { include foo foo::plugin { 'webapp': type => 'passenger'; }}

class profile::db { include foo foo::plugin { 'db': type => 'mysql'; }}

class role::server { include profile::app include profile::db}

Page 31: Puppetcamp module design talk

no conf.d?

Page 32: Puppetcamp module design talk

manifests/config.pp

class foo::config inherits foo { concat { $config_path: owner => 'root', group => 'root', mode => '0644'; }

concat::fragment { 'foo_conf_header': content => template('foo/foo.conf.erb'), target => $config_path, order => '00_header', }}

Page 33: Puppetcamp module design talk

manifests/plugin.ppdefine foo::plugin ( $type, $path = "${foo::defaults::plugin_path}/${name}.rb", $verbose = undef,) { include foo::defaults

validate_absolute_path($path) $verbose_bool = str2bool($verbose)

concat { "foo_conf_plugin_${name}": content => template('foo/plugin.conf.erb'), target => $conf_file, order => "10_plugin_${name}", }}

Page 34: Puppetcamp module design talk

what about docs?

Page 35: Puppetcamp module design talk

README.md

• What it manages

• Top level class parameters

• Defined types and their parameters

• Optional dependencies

• Common usage examples

Page 36: Puppetcamp module design talk

Testing

Page 37: Puppetcamp module design talk

spec/classes/foo_spec.rb

describe 'foo' do context 'default parameters' do let (:params) {{ }} it { should compile.with_all_deps } it { should contain_class('foo::defaults') } it { should contain_class('foo::install') } it { should contain_class('foo::config') } it { should contain_class('foo::service') } endend

Page 38: Puppetcamp module design talk

spec/classes/foo_spec.rbdescribe 'foo' do let (:facts) {{ :osfamily => 'Redhat' }} context 'default parameters' do it { should contain_package('ruby-foo') } it { should contain_file('/etc/foo.conf').with( :owner => 'root', :group => 'root', :mode => '0644', )} it { should contain_file('/etc/foo.d').with( :owner => 'root', :group => 'root', :mode => '0755', :purge => true, :recurse => true, )} it { should contain_service('foo').with( :ensure => 'running', :enable => true, )} endend

Page 39: Puppetcamp module design talk

spec/classes/foo_spec.rbdescribe 'foo' do let (:facts) {{ :osfamily => 'Redhat' }} let (:config_file) { '/etc/foo.conf' } context 'default parameters' do it { should contain_file(config_file) .with_content(/listen = 127\.0\.0\.1/) } it { should contain_file(config_file) .with_content(/port = 1234/) } it { should contain_file(config_file) .with_content(/verbose = no/) } it { should contain_file(config_file) .with_content(%r{log_file = /var/log/foo.log}) } it { should contain_file(config_file) .with_content(%r{storage_dir = /var/lib/foo}) } endend

Page 40: Puppetcamp module design talk

spec/classes/foo_spec.rbdescribe 'foo' do let (:facts) {{ :osfamily => 'Redhat' }} let (:config_file) { '/etc/foo.conf' }

context 'parameters set' do let (:params) {{ :listen => '10.0.0.42', :port => '4567', }} it { should contain_file(config_file).with_content(/listen = 10\.0\.0\.42/) } it { should contain_file(config_file).with_content(/port = 4567/) } end

context 'boolean values true' do let (:params) {{ :verbose => true, }} it { should contain_file(config_file).with_content(/verbose = yes/) } end

context 'boolean values fales' do let (:params) {{ :verbose => true, }} it { should contain_file(config_file).with_content(/verbose = no/) } endend

Page 41: Puppetcamp module design talk

Beaker?

Page 42: Puppetcamp module design talk

Forge Tips

• Limit dependencies

• External resource dependencies (user, repo)

• anchor pattern

• use include foo vs class {‘foo’: }

Page 43: Puppetcamp module design talk

ResourcesExample modules

• puppetlabs/ntp: https://forge.puppetlabs.com/puppetlabs/ntp

• pdxcat/collectd: https://forge.puppetlabs.com/pdxcat/collectd

Library modules

• puppetlabs/stdlib: https://forge.puppetlabs.com/puppetlabs/stdlib

• puppetlabs/concat: https://forge.puppetlabs.com/puppetlabs/concat

Testing

• rspec-puppet: http://rspec-puppet.com

• beaker: https://github.com/puppetlabs/beaker

Page 44: Puppetcamp module design talk

[email protected] github.com/kitchen twitter.com/kitchen

NationBuilder.com (we’re hiring!)