Make it SOLID. Software Architecture for System Administrators

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.

9f40c41cec8ab0f4fd1e7900a69b1d3d?s=128

Soenke Ruempler

October 30, 2013
Tweet

Transcript

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

    Software Architecture for System Administrators
  2. Ole Michaelis “Open Source Rockstar” @CodeStars github.com/nesQuick codestars.eu

  3. Sönke Ruempler “Chief Trolling Officer” @s0enke github.com/s0enke ruempler.eu

  4. Jimdo

  5. because infrastructure development is neither a Jenga game! S.O.L.I.D

  6. a little poll! http://images.fineartamerica.com/images-medium-large/live-long-mr-spock-tobias-woelki.jpg

  7. None
  8. S O L I D

  9. A dev/ops story Ole as Junior Admin Sönke as Dr.

    Software
  10. the basics

  11. software architecture VS. 36 years >2000 years

  12. configuration management Idempotent Declarative Convergent Abstract

  13. None
  14. 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
  15. 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
  16. 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] } }
  17. 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!
  18. Would you solder a lamp directly to the electrical wiring

    in a wall? Dependency Inversion
  19. None
  20. 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] } } }
  21. Stuff tends to get big Beware of god classes!

  22. 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'] } }
  23. None
  24. ?

  25. None
  26. rspec-puppet test-kitchen

  27. None
  28. 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 ?
  29. 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 } }
  30. Just because you can, doesn’t mean you should! Single Responsibility

  31. 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
  32. None
  33. “programs … are changed by adding new code, rather than

    by changing existing code” Open Close Principle
  34. None
  35. 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 }
  36. None
  37. None
  38. 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!
  39. >

  40. None
  41. ? 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] } } }
  42. Don’t call us, we call you! Hollywood Principle

  43. 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.
  44. 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.
  45. 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.
  46. None
  47. class profile::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' } } ?
  48. 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}"] } }
  49. None
  50. ?

  51. “puppet separate code and data” I’m feeling lucky

  52. # /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
  53. 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 }
  54. hiera AWS/Cloudformation LDAP DNS your selfwritten stuff

  55. None
  56. None
  57. None
  58. What about I and L in SOLID?

  59. You want me to plug this in, where? Interface Segregation

  60. If it looks like a duck, quacks like a duck,

    but need batteries - you probably have the wrong abstraction! Liskov Substitution
  61. S O L I D

  62. None
  63. Structure your code! Node Profile Profiles Role Profile Modules Resources

  64. Puppet forge puppet module registry

  65. Dependency Management Librarian Puppet Berkshelf (chef)

  66. stdmod https://github.com/stdmod

  67. server spec http://serverspec.org/

  68. sandboxed development https://github.com/mlafeldt/skeleton-cookbook https://github.com/Jimdo/puppet-skeleton

  69. Thank you! Rate this talk! https://joind.in/9578

  70. @CodeStars ole@jimdo.com @s0enke soenke@jimdo.com Questions?

  71. *I ’m just a blank slide*

  72. 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 •