Save 37% off PRO during our Black Friday Sale! »

Make it SOLID Software Architecture for System Administrators

Make it SOLID Software Architecture for System Administrators

Gave this talk at the International PHP Conference on October 30th, 2013
Follow me on Twitter: @CodeStars

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.

4c50caee0c6a446a0b60f827a2a1e5e5?s=128

Ole Michaelis

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 •