Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Make it SOLID. Software Architecture for System...

Make it SOLID. Software Architecture for System Administrators

Starting with Chef or Puppet as a System Administrator will lead you to a problem where you are not sure what's the best solution of a problem in terms of software architecture. We will give you a brief overview of general well known and battle tested software patterns, which also applies to infrastructure management code. In addition, we'll also show Antipatterns, and best practices.

Soenke Ruempler

October 30, 2013
Tweet

More Decks by Soenke Ruempler

Other Decks in Programming

Transcript

  1. Ole Michaelis & Sönke Ruempler | Jimdo Make it SOLID

    Software Architecture for System Administrators
  2. node web-1 { $role = 'web-app-a' $has_apache = true; include

    php5 } node web-2 { $role = 'web-app-b' $has_fcgi=true include php5 } # has both enabled, but needs a special config node web-3 { $role = 'web-app-c' $has_fcgi=true $has_apache=true include php5 } Web App 1 Web App 2 Web App 3
  3. class php5 { if ($::has_fcgi) package { 'php5-cli' : ensure

    => installed } } if ($::has_apache) { package { 'php5-apache' : ensure => installed } } if ($::role == 'web-app-c') package { 'php5-web-app-c-special-module' } file { '/etc/php5/php.ini' : source => 'puppet:///modules/php5/php.ini-web-app-c' } } } Global variable! Out of Context! No abstraction
  4. class php5( $has_fcgi = false, $has_apache = false, $role =

    undef ) { if ($has_fcgi) package { 'php5-cli' : ensure => installed } } if ($has_apache) { package { 'php5-apache' : ensure => installed } } if ($role == 'web-app-c') { package { 'php5-web-app-c-special-module' } file { '/etc/php5/php.ini' : source => 'puppet:///modules/php5/php.ini-web-app-c' , require => Package[php-fcgi] } }
  5. node 'web-1' { $role = 'web-app-a' $has_apache = true class

    { 'php5' : has_apache => true, role => 'web-app-a' } } node 'web-2' { $role = 'web-app-b' $has_fcgi = true class { 'php5' : has_fcgi => true, role => 'web-app-b' } } Injection! Injection!
  6. class php5( $has_fcgi = false, $has_apache = false, $role =

    undef ) { if ($has_fcgi) package { 'php5-cli' : ensure => installed } } if ($has_apache) { package { 'php5-apache' : ensure => installed } } if ($role == 'web-app-c') { package { 'php5-web-app-c-special-module' } file { '/etc/php5/php.ini' : source => 'puppet:///modules/php5/php.ini-web-app-c' require => Package[php-fcgi] } } }
  7. if ($lighttpd) { package { 'php5-cgi': } configdir { '/etc/php5/cgi':

    require => Package['php5-cgi'] } configdir { '/etc/php5/cgi/conf.d': require => Package['php5-cgi'] } if ($testserver) { configfile { '/etc/php5/cgi/php.ini': sourcename => 'php5/cgi/php.ini-testserver', require => Package['php5-cgi'] } } else { if ($cms ) { configfile { '/etc/php5/cgi/php.ini': sourcename => 'php5/cgi/php.ini-cms', require => Package['php5-cgi'] } } else { if ($mgmt ) { configfile { '/etc/php5/cgi/php.ini': sourcename => 'php5/cgi/php.ini-mgmt', require => Package['php5-cgi'] } } else { if ($lc ) { configfile { '/etc/php5/cgi/php.ini': sourcename => 'php5/cgi/php.ini-lc. jimdo.com', require => Package['php5-cgi'] } } else { configfile { '/etc/php5/cgi/php.ini': sourcename => 'php5/cgi/php.ini', require => Package['php5-cgi'] } } } } } configfile { '/etc/php5/cgi/conf.d/pdo.ini': sourcename => 'php5/cgi/conf.d/pdo.ini', require => Package['php5-cgi'] } }
  8. ?

  9. describe :node => 'web-1' do it { should contain_package ('php5-fcgi')

    } end describe :node => 'web-2' do it { should contain_package ('php5-apache2' ) } end describe :node => 'web-3' do it { should contain_package ('php5-fcgi') } it { should contain_package ('php5-apache2' ) } end Test php5 class: Install Package ?
  10. describe :class => 'php5::fcgi' do it { should contain_package('php5-cli') }

    end describe :class => 'php5::apache' do it { should contain_package('php5-apache2') } end class php5::fgci { package { 'php5-cgi' : ensure => installed } } class php5::apache { package { 'php5-apache' : ensure => installed } }
  11. describe :node => 'web-1' do it { should include_class('php5::fcgi') }

    it { should contain_package('php5-fcgi') } end describe :node => 'web-2' do it { should include_class('php5::apache') } it { should contain_package('php5-apache2') } end node web-1 { include php5::apache } node web-2 { include php5::fcgi } Tests Code
  12. “programs … are changed by adding new code, rather than

    by changing existing code” Open Close Principle
  13. new requirements... now web app 1 needs a second node

    node web-1 { $role = 'web-app-a' include php5::apache } node web-1a { $role = 'web-app-a' include php5::apache }
  14. class role::web-app-a { include php5::apache } node web-1 { include

    role::web-app- a } node web-1a { include role::web-app- a } Our new role! And nodes just include it!
  15. >

  16. ? class php5::fcgi($role = undef) { if ($role == 'web-app-c')

    { package { 'php5-web-app-c-special-module' } file { '/etc/php5/php.ini' : source => 'puppet:///modules/php5/php.ini-web-app-c', require => Package[php-fcgi] } } }
  17. class role::web-app-c::special-php-stuff { package { 'php5-web-app-c-special-module' } file { '/etc/php5/php.ini'

    : source => 'puppet:///modules/php5/php.ini-web-app-c' } } 1. Split out the special case into its own class.
  18. class php5::fcgi($include_after_package_install = undef) { package { 'php5-fcgi' : ensure

    => installed } if ($include_after_package_install) include $include_after_package_install Package['php5-fcgi'] -> Class[$include_after_package_install] } } 2. Make the special case pluggable and reusable. 2a. And name it abstract.
  19. class role::web-app-c { include php5::apache class { 'php5::fcgi' : include_after_package_install

    => 'profile::web-app-c::special-php-stuff' } } 3. Pass the special case as dependency in our web-app-c.
  20. define php5::specialconfig( $ensure = present, $sapi, $module, $key, $value, $section

    = undef, $path = '/etc/php5/%s/conf.d/%s.ini' ) { ini_setting { $title: ensure => present, path => sprintf($path, $sapi, $module), section => $section, setting => $key, value => $value, require => Package["php5-${sapi}"] } }
  21. ?

  22. # /etc/puppet/hieradata/web1.yaml --- classes: - profile::web-app-a # /etc/puppet/hieradata/web1a.yaml --- classes:

    - profile::web-app-a # /etc/puppet/hieradata/web2.yaml --- classes: - profile::web-app-c # /etc/puppet/hieradata/web3.yaml --- classes: - profile::web-app-c puppet-hiera example in YAML
  23. node default { hiera_include('classes') } node web-1 { include role::web-app-a

    } node web-1a { include role::web-app-a } node web-2 { include role::web-app-b } node web-3 { include role::web-app-c }
  24. If it looks like a duck, quacks like a duck,

    but need batteries - you probably have the wrong abstraction! Liskov Substitution
  25. Sources • http://www.slideshare.net/PuppetLabs/garethrushgrove-puppetconf • http://www.slideshare.net/PuppetLabs/alessandro-franceschi-new • https://github.com/jedi4ever/stop-the-fork • http://lostechies.com/derickbailey/2009/02/11/solid-development-principles-in-motivational- pictures/

    • https://speakerdeck.com/jfryman/refactoring-puppet • http://www.clker.com/cliparts/e/2/a/d/1206574733930851359Ryan_Taylor_Green_Tick.svg.med. png • http://www.craigdunn.org/2012/05/239/ • http://cdn.slashgear.com/wp-content/uploads/2012/10/google-datacenter-tech-13.jpg • http://deviq.com/Media/Default/Article/Dont-Call-Us-Well-Call-You-Jun-2013.png • http://www.timeshighereducation.co.uk/Pictures/web/g/q/g/copy_paste_keyboard_button_450.jpg • http://sanremo.com.au/wp-content/uploads/2013/05/lasagna.jpg • http://4.bp.blogspot. com/_9kQQgQD35rY/SaV5p8YBGhI/AAAAAAAAAkg/HOvlhIo7yGI/s400/06_Red_Green_Refactor. JPG • http://www.thelolshop.com/wp-content/uploads/2012/11/20121126-101937.jpg •