Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Puppetcamp module design talk

257 views

Published on

Slides from my talk at PuppetCamp LA 2015 at SCaLE13x

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Puppetcamp module design talk

  1. 1. Basic Puppet Module Design Jeremy Kitchen Systems Engineer at NationBuilder
  2. 2. What is a module?
  3. 3. Package Config Service
  4. 4. Module Contents foo/ manifests/ defaults.pp init.pp install.pp config.pp service.pp templates/ foo.conf.erb
  5. 5. 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' } '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") } } }
  6. 6. manifests/init.pp class 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']; } }
  7. 7. manifests/init.pp class foo ( $package_ensure = installed, $listen = '127.0.0.1', $port = '1234', $verbose = false, ) inherits foo::defaults {
  8. 8. manifests/init.pp if (!is_ip_address($listen)) { fail('listen parameter needs to be an ip address') } $verbose_bool = str2bool($verbose)
  9. 9. manifests/init.pp include foo::install include foo::config include foo::service Class['foo::install'] -> Class['foo::config'] ~> Class['foo::service']
  10. 10. manifests/init.pp anchor { 'foo::begin': before => Class['foo::install','foo::config'], notify => Class['foo::service']; 'foo::end': require => Class['foo::service']; }
  11. 11. containment example include site::mounts include site::webapp include mysql include apache Class['site::mounts'] -> Class['mysql'] Class['site::mounts'] -> Class['apache'] Class['mysql'] -> Class['apache'] Class['mysql'] -> Class['site::webapp']
  12. 12. manifests/init.pp anchor { 'foo::begin': before => Class['foo::install','foo::config'], notify => Class['foo::service']; 'foo::end': require => Class['foo::service']; }
  13. 13. what about contain?
  14. 14. manifests/init.pp class 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']; } }
  15. 15. manifests/install.pp class foo::install inherits foo { package { $package_name: ensure => $package_ensure, } }
  16. 16. manifests/config.pp class foo::config inherits foo { file { $config_path: content => template('foo/foo.conf.erb'), owner => 'root', group => 'root', mode => '0644'; } }
  17. 17. Module Contents class foo::service inherits foo { service { $service_name: ensure => running, enable => true, } }
  18. 18. templates/foo.conf.erb [main] listen = <%= @listen %> port = <%= @port %> verbose = <%= @verbose_bool ? 'yes' : 'no' %> log_file = <%= @log_file %> storage_dir = <%= @storage_dir %>
  19. 19. Module Contents foo/ manifests/ defaults.pp init.pp install.pp config.pp service.pp templates/ foo.conf.erb
  20. 20. example # profile/manifests/server.pp class profile::server { class { 'foo': port => 3389, verbose => true, } }
  21. 21. example # hiera/global.yaml foo::port: 3389 foo::verbose: true # profile/manifests/server.pp class profile::server { include ::foo }
  22. 22. conf.d
  23. 23. Module Contents foo/ manifests/ defaults.pp init.pp install.pp config.pp service.pp plugin.pp templates/ foo.conf.erb plugin.conf.erb
  24. 24. 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' } } }
  25. 25. manifests/config.pp class 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'; } }
  26. 26. 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
  27. 27. manifests/plugin.pp define 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'], } }
  28. 28. templates/plugin.conf.erb [plugin <%= @name %>] type = <%= @type %> path = <%= @path %> <%- if !@verbose.nil? -%> verbose = <%= @verbose_bool ? 'yes' : 'no' %> <%- end -%>
  29. 29. example include foo foo::plugin { 'webapp': type => 'passenger'; 'db': type => 'mysql', verbose => true, path => '/usr/local/share/custom_mysql.rb'; }
  30. 30. role/profile example class 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 }
  31. 31. no conf.d?
  32. 32. 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', } }
  33. 33. manifests/plugin.pp define 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}", } }
  34. 34. what about docs?
  35. 35. README.md • What it manages • Top level class parameters • Defined types and their parameters • Optional dependencies • Common usage examples
  36. 36. Testing
  37. 37. 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') } end end
  38. 38. spec/classes/foo_spec.rb describe '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, )} end end
  39. 39. spec/classes/foo_spec.rb describe '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}) } end end
  40. 40. spec/classes/foo_spec.rb describe '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/) } end end
  41. 41. Beaker?
  42. 42. Forge Tips • Limit dependencies • External resource dependencies (user, repo) • anchor pattern • use include foo vs class {‘foo’: }
  43. 43. Resources Example 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
  44. 44. Thanks! kitchen@kitchen.io github.com/kitchen twitter.com/kitchen NationBuilder.com (we’re hiring!)

×