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

Puppet : Start from Day 1 with Tests, Tests and...

Puppet : Start from Day 1 with Tests, Tests and Tests!

Session played @ PuppetCamp Paris 2016

Alexandre RAOUL

June 07, 2016
Tweet

More Decks by Alexandre RAOUL

Other Decks in Programming

Transcript

  1. Start from Day 1 with Tests, Tests and Tests! Alexandre

    RAOUL Arnaud MAZIN Camp Paris 2016 OCTO Technology
  2. Alexandre Raoul Likes: Puppet, APIs, command line and his Peugeot

    308 GT Line Arnaud Mazin Likes: DevOps, delivery, automation, black consoles and gliders
  3. Tech Leading Newspaper Style Pair Programming Coding Dojo Refactoring Behaviour

    Driven Development Don’t Repeat Yourself (DRY) Boy Scout Rule Collective Code Ownership Test Driven Development (TDD) Clean Code Broken Window Software Entropy Unit Testing Separation Of Concerns (SOC) Egoless Programming Continuous Integration Data Separation Code Control Keep It Simple Stupid (KISS) Code Review You Ain’t Gonna Need It (YAGNI) Legacy Code Design Patterns Style Guide Lint Test Harness Dev Practices bingo card
  4. Clean Code In our context, it’s nice if… “it does

    what it tells and tells what is does” Example: a MySQL module, installs only mysql, not Emacs as a bonus because DBAs like it
  5. Keep It Simple Stupid (KISS) In our context, it’s nice

    if… “I can understand how it works” Example: use Puppet standard PFS (Package, File, Service) resources instead of 400 chars oneliner wrapped in an exec
  6. $ git commit -m "!fixup conf path on RHEL7" $

    git push In our context, it’s nice if… “you manage all your code in git” “you use r10k to deploy code” “you have a branching / env strategy” Code Control
  7. If you don’t take care... We’ve seen so much… 1.

    edit some .pp file 2. crtl+s 3. git add 4. git commit -m “do some stuff” 5. git push 6. ssh puppetmaster 7. git pull 8. ssh canary-prod-server 9. puppet agent -vt 10. alt+tab 11. firefox + f5 12. Is it still up? First victims : - time you lost - prod stability - motivation source: http://xkcd.com/1296/
  8. Alexandre’s lint setup Setup is different but both provide fast

    feedback Choose or assemble your own … but take time to do it ! + =
  9. Tests repartition 1s - 1min 1min - 10min 10min -

    30min 30min+ RSpec-Puppet Beaker / TestKitchen (ServerSpec) DIY tools agregation threshold overall tests run duration overall tests coverage fine-grained coarse-grained
  10. Puppet Agent Puppet Master 1. Facts The node gathers and

    send its facts 2. Catalog Puppet master uses facts, code and hiera data to compile a catalog to specify what is expected on the agent 4. Report The nodes sends back a report containing the catalog execution result 3. Apply changes Agent takes action to conform to the catalog Puppet execution lifecycle Puppet code
  11. Catalog Compilation Puppet master uses facts, code and hiera data

    to compile a catalog to specify what is expected on the agent RSpec-Puppet interactions Puppet code Facts Puppet Catalog RSpec-Puppet Set inputs Verify assertions in catalog
  12. In our context, it’s nice if… “you write tests before

    Puppet code” (A glimpse of) Test Driven Development
  13. TDD: write your test class base_packages () { } describe

    'base_packages' do context 'default params' do let(:params) { {} } it { is_expected.to compile } it { is_expected.to contain_package('vim') } it { is_expected.to contain_package('curl') } end end base_packages default params should contain compile into a catalogue without dependency cycles should contain Package[vim] (FAILED - 1) should contain Package[curl] (FAILED - 2) Finished in 0.62943 seconds (files took 0.42828 seconds to load) 3 examples, 2 failures RSpec-Puppet Tests Puppet code RSpec-Puppet run output
  14. TDD: make your tests go green base_packages default params should

    contain compile into a catalogue without dependency cycles should contain Package[vim] should contain Package[curl] Finished in 0.82657 seconds (files took 0.41641 seconds to load) 3 examples, 0 failures RSpec-Puppet Tests Puppet code RSpec-Puppet run output describe 'base_packages' do context 'default params' do let(:params) { {} } it { is_expected.to compile } it { is_expected.to contain_package('vim') } it { is_expected.to contain_package('curl') } end end class base_packages () { package { ['vim', 'curl'] : ensure => present, } }
  15. describe 'base_packages' do context 'default params' do let(:params) { {}

    } it { is_expected.to compile } it { is_expected.to contain_package('vim') } it { is_expected.to contain_package('curl') } end context 'override params' do let(:params) { { 'packages' => ['wget'], } } it { is_expected.to compile } it { is_expected.to contain_package('wget') } it { is_expected.to_not contain_package('vim') } it { is_expected.to_not contain_package('curl') } end end TDD: add your new feature tests Puppet code RSpec-Puppet Tests class base_packages () { package { ['vim', 'curl'] : ensure => present, } }
  16. describe 'base_packages' do context 'default params' do let(:params) { {}

    } it { is_expected.to compile } it { is_expected.to contain_package('vim') } it { is_expected.to contain_package('curl') } end context 'override params' do let(:params) { { 'packages' => ['wget'], } } it { is_expected.to compile } it { is_expected.to contain_package('wget') } it { is_expected.to_not contain_package('vim') } it { is_expected.to_not contain_package('curl') } end end TDD: add your new feature tests base_packages default params should contain compile into a catalogue without dependency cycles should contain Package[vim] should contain Package[curl] override params should contain compile into a catalogue without dependency cycles (FAILED - 1) should contain Package[wget] (FAILED - 2) should not contain Package[vim] (FAILED - 3) should not contain Package[curl] (FAILED - 4) Finished in 0.91724 seconds (files took 0.39541 seconds to load) 7 examples, 4 failures Puppet code RSpec-Puppet Tests RSpec-Puppet run output class base_packages () { package { ['vim', 'curl'] : ensure => present, } }
  17. describe 'base_packages' do context 'default params' do let(:params) { {}

    } it { is_expected.to compile } it { is_expected.to contain_package('vim') } it { is_expected.to contain_package('curl') } end context 'override params' do let(:params) { { 'packages' => ['wget'], } } it { is_expected.to compile } it { is_expected.to contain_package('wget') } it { is_expected.to_not contain_package('vim') } it { is_expected.to_not contain_package('curl') } end end TDD: make your tests go green (again) Puppet code RSpec-Puppet Tests RSpec-Puppet run output class base_packages ( $packages = ['vim', 'curl'], ) { package { $packages : ensure => present, } }
  18. describe 'base_packages' do context 'default params' do let(:params) { {}

    } it { is_expected.to compile } it { is_expected.to contain_package('vim') } it { is_expected.to contain_package('curl') } end context 'override params' do let(:params) { { 'packages' => ['wget'], } } it { is_expected.to compile } it { is_expected.to contain_package('wget') } it { is_expected.to_not contain_package('vim') } it { is_expected.to_not contain_package('curl') } end end TDD: make your tests go green (again) Puppet code RSpec-Puppet Tests RSpec-Puppet run output class base_packages ( $packages = ['vim', 'curl'], ) { package { $packages : ensure => present, } } base_packages default params should contain compile into a catalogue without dependency cycles should contain Package[vim] should contain Package[curl] override params should contain compile into a catalogue without dependency cycles should contain Package[wget] should not contain Package[vim] should not contain Package[curl] Finished in 0.93594 seconds (files took 0.46291 seconds to load) 7 examples, 0 failures
  19. When it comes to Puppet stuff High-level design Part-level design

    Base OS configuration Hardening Apache Vhost Vhost Static Content Reverse Proxy Java Tomcat WAR Conf My great App which does wonderful stuff uses
  20. When it comes to Puppet stuff High-level modules 80% of

    your time Part-level modules 20% of your time ntp ssh apache java tomcat role::myapp uses profile::hardening profile::baseline profile::prodified_tomcat yum sudo
  21. RSpec-Puppet smoke testing Puppet compilation profile::hardening actually calls things in

    saz/ssh and saz/sudo modules We only ensure that they can work together High-level modules YES ! Part-level modules Nope
  22. How would you ensure that your Puppet code is working?

    1. Take a fresh vm 2. Launch Puppet code 3. Verify the service is up 4. Trash the vm You can do it … - manually - ad-hoc scripting - by leveraging other people work Let’s see test-kitchen & beaker!
  23. Test-Kitchen overview Test Kitchen vagrant chef busser-serverspec driver provisioner verifier

    ec2 puppet serverspec 1. Take a fresh vm 2. Launch Puppet code 3. Verify the service is up 4. Trash the vm 104 matches for “kitchen” in rubygems kitchen-docker, kitchen-gce, kitchen-pester ...
  24. Test-Kitchen to ensure the port is listening and the service

    up High-level modules Nope Part-level modules YES ! $ netstat -lntp | grep -q :80 $ service httpd status | grep -q started describe port(80) do it { should be_listening } end describe service('httpd') do it { should be_running } end Bash (could be in bats) Serverspec
  25. Test-Kitchen to ensure the app is up and running High-level

    modules YES ! Part-level modules Nope $ test -f /opt/app/myapp.war $ curl -sf localhost | grep myapp describe file('/opt/app/myapp.war') do it { should exist } end describe command('curl -sf localhost') do its(:exit_status) { should eq 0 } its(:stdout) { should match /myapp/ } end Bash (could be in bats) Serverspec
  26. ◉ Feedback loop length is similar to Test-Kitchen ◉ You

    can cover idempotency & upgrade paths ◉ You can implement basic multi-hosts topology But it comes at a price: ◉ Beaker comes with its own complexity ◉ Bootstrap is not cheap Make up your mind to see if it’s worth it in your context Beaker for the win?
  27. ◉ Testing a full topology > Beaker can, but will

    probably not apply for production rollouts ◉ Testing anything that needs to be in the middle of a tightly coupled IS > Hunting bugs is easier when we isolate! > Feedback loop is a HUGE pain point ◉ “Do it yourself” scripts to the rescue! What’s left?
  28. Let’s recap ntp ssh apache java tomcat role::myapp uses profile::hardening

    profile::baseline profile::prodified_tomcat yum sudo High-level modules 80% of your time Part-level modules 20% of your time RSpec-Puppet • Smoke test compilation (resource duplication/dependency cycle) • Validate dependencies integration • Part-level module upgrade integration • Puppet version upgrade • Code and template branching • Feature flipping • Puppet version upgrade Kitchen/Beaker • Overall integration • Ensure Puppet manages the technology as expected
  29. Mocking vs. Integration Testability vs. Security Debug vs. Performance Cost

    vs. Representativity Volatility vs. Durability Deal with Tradeoffs Rebuild vs. Upgrade
  30. Module ready Change Puppet Module Run Static Analysis Run Test-Kitchen

    Tests Run RSpec Tests Dev Commit Module Change Trigger Job Run Static Analysis Run RSpec Tests Run Test- Kitchen Tests Tag Module X.Y on Continous Integration Server on Developer’s workstation Module ready Continue with Puppet High-level Repository Development Workflow to deploy module Puppet Part-level module Development Workflow
  31. On Puppet Master on CI on Developer’s workstation Your own

    high-level tests Deploy change DEV Branch Manual validation? Promote code to Production PROD Branch on CI Module ready Change deployed Change Module tag Change Role / Profile or Hiera data Deploy change PROD Branch Puppet High-level module Development Workflow same as previous... ... Dev
  32. Consider Infra-as-code as code Focus on fast and continuous feedback

    Embrace TDD Craft your (dev) tools Start tests on day 1, feed them on a daily basis Take advantage of Puppet rich testing ecosystem WIN WIN WIN WIN WIN WIN
  33. Thank you Questions? Culture Code Software Craftsmanship : Better Places

    with Better Code Free download our book (FR) http://www.octo.com/fr/publications/20-culture-code