Top Banner
Basic Puppet Module Design Jeremy Kitchen Systems Engineer at NationBuilder
44

Puppetcamp module design talk

Jul 31, 2015

Download

Technology

Jeremy Kitchen
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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!)