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

Test Driven Development for Puppet

Test Driven Development for Puppet

Puppet Camp London presentation introducing the tools and techniques used for testing Puppet code. Also introduces Test Driven Development, what it is and why it's useful.

Gareth Rushgrove

April 04, 2014
Tweet

More Decks by Gareth Rushgrove

Other Decks in Programming

Transcript

  1. Test Driven
    Development!
    for Puppet!
    Puppet needs software development
    Gareth Rushgrove

    View full-size slide

  2. Who
    (Who is this person?)

    View full-size slide

  3. UK Government
    Digital Service

    View full-size slide

  4. The problem
    (This isn’t a rant, but…)

    View full-size slide

  5. Who here is a
    software developer?
    Gareth Rushgrove

    View full-size slide

  6. If you’re writing
    Puppet code you’re a
    software developer
    Gareth Rushgrove

    View full-size slide

  7. As a software
    developer it’s your job
    to learn software
    engineering practices
    Gareth Rushgrove

    View full-size slide

  8. What is Test Driven
    Development
    (And why should you care)

    View full-size slide

  9. A common practice in
    software engineering
    Gareth Rushgrove

    View full-size slide

  10. Not just testing
    Gareth Rushgrove

    View full-size slide

  11. Encourages simple
    designs and inspires
    confidence
    Gareth Rushgrove

    View full-size slide

  12. First write an (initially
    failing) automated
    test case
    Gareth Rushgrove

    View full-size slide

  13. Then produce the
    minimum amount of
    code to pass that test
    Gareth Rushgrove

    View full-size slide

  14. And finally refactor
    the new code to
    acceptable standards
    Gareth Rushgrove

    View full-size slide

  15. Test Driven Design
    Gareth Rushgrove

    View full-size slide

  16. Gareth Rushgrove

    View full-size slide

  17. Unit testing
    with RSpec
    and Guard
    (Not puppet specific)

    View full-size slide

  18. A unit is the smallest
    testable part of an
    application
    Gareth Rushgrove

    View full-size slide

  19. Testing puppet
    requires a little Ruby
    knowledge so we’ll
    use Ruby examples
    Gareth Rushgrove

    View full-size slide

  20. class Person
    def say(word)
    end
    end
    Gareth Rushgrove

    View full-size slide

  21. First lets write a test.
    For this we use the
    RSpec testing
    framework
    Gareth Rushgrove

    View full-size slide

  22. require 'person'
    !
    describe Person, "#say" do
    it "should say something" do
    !
    end
    end
    Gareth Rushgrove

    View full-size slide

  23. require 'person'
    !
    describe Person, "#say" do
    it "should say something" do
    bob = Person.new
    bob.say("hello").should \
    eq("hello everyone")
    end
    end
    Gareth Rushgrove

    View full-size slide

  24. Now lets run our test.
    It should fail
    Gareth Rushgrove

    View full-size slide

  25. rspec
    Gareth Rushgrove

    View full-size slide

  26. Failures:
    1) Person#say should say something
    Failure/Error: bob.say("hello").should
    eq("hello everyone")
    expected: "hello everyone"
    got: nil
    Finished in 0.00171 seconds
    1 example, 1 failure
    Gareth Rushgrove

    View full-size slide

  27. Now lets write the
    implementation
    Gareth Rushgrove

    View full-size slide

  28. class Person
    def say(word)
    word + " everyone"
    end
    end
    Gareth Rushgrove

    View full-size slide

  29. And run our test
    again
    Gareth Rushgrove

    View full-size slide

  30. Person#say
    should say something
    !
    Finished in 0.00199 seconds
    1 example, 0 failures
    Gareth Rushgrove

    View full-size slide

  31. Why not have tests
    automatically run
    whenever you
    change the code?
    Gareth Rushgrove

    View full-size slide

  32. That’s what Guard
    does
    Gareth Rushgrove

    View full-size slide

  33. guard :rspec, cmd: 'bundle exec rspec' do
    watch(%r{^spec/.+_spec\.rb$})
    watch(%r{^lib/.+\.rb$}) { 'spec' }
    end
    Gareth Rushgrove

    View full-size slide

  34. guard
    Gareth Rushgrove

    View full-size slide

  35. Lets see a quick
    demo
    Gareth Rushgrove

    View full-size slide

  36. Why test puppet
    code at all
    (Testing declarative languages)

    View full-size slide

  37. Modules increasingly
    contain logic
    Gareth Rushgrove

    View full-size slide

  38. Modules increasingly
    take arguments
    Gareth Rushgrove

    View full-size slide

  39. Modules increasingly
    have interfaces with
    other modules
    Gareth Rushgrove

    View full-size slide

  40. Modules increasingly
    used in many
    operating system and
    version combinations
    Gareth Rushgrove

    View full-size slide

  41. Modules increasingly
    used in many Ruby
    and Puppet version
    combinations
    Gareth Rushgrove

    View full-size slide

  42. Unit testing
    puppet with
    rspec-puppet
    (Finally some puppet code)

    View full-size slide

  43. Unit testing
    for Puppet

    View full-size slide

  44. A very simple puppet
    class
    Gareth Rushgrove

    View full-size slide

  45. class sample {
    }
    Gareth Rushgrove

    View full-size slide

  46. First write the test
    Gareth Rushgrove

    View full-size slide

  47. require 'spec_helper'
    !
    describe "sample" do
    it { should create_file('/tmp/sample')}
    end
    Gareth Rushgrove

    View full-size slide

  48. Then run the test
    Gareth Rushgrove

    View full-size slide

  49. sample
    should contain File[/tmp/sample] (FAILED - 1)
    !
    Finished in 0.4584 seconds
    1 example, 1 failure
    Gareth Rushgrove

    View full-size slide

  50. And then write the
    (puppet) code to
    make the test pass
    Gareth Rushgrove

    View full-size slide

  51. class sample {
    file { "/tmp/sample":
    ensure => present,
    }
    }
    Gareth Rushgrove

    View full-size slide

  52. sample
    should contain File[/tmp/sample]
    !
    Finished in 0.3881 seconds
    1 example, 0 failures
    Gareth Rushgrove

    View full-size slide

  53. Lets run the tests
    automatically
    whenever you
    change anything
    Gareth Rushgrove

    View full-size slide

  54. guard :rspec, cmd: 'bundle exec rspec' do
    watch(%r{^spec/.+_spec\.rb$})
    watch(%r{^manifests/.+\.pp$}) { 'spec' }
    end
    Gareth Rushgrove

    View full-size slide

  55. Lets see a quick
    demo of that too
    Gareth Rushgrove

    View full-size slide

  56. You can also test
    hosts, defines, facts,
    functions, hieradata
    Gareth Rushgrove

    View full-size slide

  57. Syntax checking,
    linting, oh my
    (Creating a build process)

    View full-size slide

  58. puppet-lint
    Gareth Rushgrove

    View full-size slide

  59. Puppet!
    style guide

    View full-size slide

  60. Available!
    as a gem

    View full-size slide

  61. puppet-lint --with-filename /etc/puppet/modules
    foo/manifests/bar.pp: trailing whitespace found
    on line 1 apache/manifests/server.pp: variable
    not enclosed in {} on line 56
    Gareth Rushgrove

    View full-size slide

  62. puppet-syntax
    Gareth Rushgrove

    View full-size slide

  63. Validate Puppet
    and ERB syntax

    View full-size slide

  64. require 'puppet-syntax/tasks/puppet-syntax'
    Gareth Rushgrove

    View full-size slide

  65. rake syntax
    ---> syntax:manifests
    ---> syntax:templates
    ---> syntax:hiera:yaml
    Gareth Rushgrove

    View full-size slide

  66. What is Rake and
    why do we use it
    (Still no puppet)

    View full-size slide

  67. Rake is a Ruby!
    build tool
    Gareth Rushgrove

    View full-size slide

  68. It’s like Make but in
    Ruby
    Gareth Rushgrove

    View full-size slide

  69. It’s very easy to
    distribute Rake tasks
    as Ruby gems
    Gareth Rushgrove

    View full-size slide

  70. rake
    Gareth Rushgrove

    View full-size slide

  71. rake
    Gareth Rushgrove

    View full-size slide

  72. rake -T
    Gareth Rushgrove

    View full-size slide

  73. Lets make a
    command to run lint,
    syntax and spec
    Gareth Rushgrove

    View full-size slide

  74. task :test => [
    :syntax,
    :lint,
    :spec,
    ]
    Gareth Rushgrove

    View full-size slide

  75. rake test
    Gareth Rushgrove

    View full-size slide

  76. Acceptance
    testing with
    beaker
    (Living on the edge)

    View full-size slide

  77. Acceptance
    test against
    real systems
    Gareth Rushgrove

    View full-size slide

  78. Test what actually
    happens, not what is
    meant to happen
    Gareth Rushgrove

    View full-size slide

  79. Build by
    Gareth Rushgrove

    View full-size slide

  80. Very new
    Gareth Rushgrove

    View full-size slide

  81. Test against different
    operating systems
    Gareth Rushgrove

    View full-size slide

  82. HOSTS:
    ubuntu-server-12042-x64:
    roles:
    - master
    platform: ubuntu-server-12.04-amd64
    box: ubuntu-server-12042-x64-vbox4210-nocm
    box_url: http://puppet-vagrant-boxes.puppetlabs.com/u
    hypervisor: vagrant
    !
    CONFIG:
    log_level: verbose
    type: foss
    Gareth Rushgrove

    View full-size slide

  83. HOSTS:
    centos-64-x64:
    roles:
    - master
    platform: el-6-x86_64
    box : centos-64-x64-vbox4210-nocm
    box_url : http://puppet-vagrant-boxes.puppetlabs.com/
    hypervisor : vagrant
    !
    CONFIG:
    log_level: verbose
    type: foss
    Gareth Rushgrove

    View full-size slide

  84. Supports multiple
    hypervisors
    Gareth Rushgrove

    View full-size slide

  85. Vagrant
    hypervisor

    View full-size slide

  86. VSphere
    hypervisor

    View full-size slide

  87. Helpers to install
    puppet and modules
    Gareth Rushgrove

    View full-size slide

  88. install_puppet
    Gareth Rushgrove

    View full-size slide

  89. puppet('module', 'install', 'puppetlabs-stdlib')
    Gareth Rushgrove

    View full-size slide

  90. Test that Puppet runs
    without errors
    Gareth Rushgrove

    View full-size slide

  91. context 'default parameters' do
    it 'should work with no errors' do
    pp = “class { 'sample': }”
    !
    expect(apply_manifest(pp).exit_code).to_not eq(1)
    end
    end
    Gareth Rushgrove

    View full-size slide

  92. Test runs are
    idempotent
    Gareth Rushgrove

    View full-size slide

  93. context 'default parameters' do
    it 'should work with no errors' do
    pp = “class { 'sample': }”
    !
    expect(apply_manifest(pp).exit_code).to_not eq(1)
    expect(apply_manifest(pp).exit_code).to eq(0)
    end
    end
    Gareth Rushgrove

    View full-size slide

  94. Test that the module
    installs packages, run
    services, etc.
    Gareth Rushgrove

    View full-size slide

  95. Gareth Rushgrove

    View full-size slide

  96. describe package('nginx') do
    it { should be_installed }
    end
    !
    describe service('nginx') do
    it { should be_enabled }
    it { should be_running }
    end
    !
    describe port(80) do
    it { should be_listening}
    end
    Gareth Rushgrove

    View full-size slide

  97. Other useful tools
    (and what we’re still missing)

    View full-size slide

  98. Fixtures,
    matchers

    View full-size slide

  99. Gareth Rushgrove

    View full-size slide

  100. Nice continuous
    integration

    View full-size slide

  101. Test pull request
    branches too

    View full-size slide

  102. ---
    language: ruby
    bundler_args: --without development
    before_install: rm Gemfile.lock || true
    rvm:
    - 1.8.7
    - 1.9.3
    - 2.0.0
    script: bundle exec rake test
    env:
    - PUPPET_VERSION="~> 2.7.0"
    - PUPPET_VERSION="~> 3.1.0"
    - PUPPET_VERSION="~> 3.2.0"
    - PUPPET_VERSION="~> 3.3.0"
    - PUPPET_VERSION="~> 3.4.0"
    Gareth Rushgrove

    View full-size slide

  103. Official!
    ruby
    support

    View full-size slide

  104. matrix:
    exclude:
    - rvm: 2.0.0
    env: PUPPET_VERSION="~> 2.7.0"
    - rvm: 2.0.0
    env: PUPPET_VERSION="~> 3.1.0"
    - rvm: 1.9.3
    env: PUPPET_VERSION="~> 2.7.0"
    Gareth Rushgrove

    View full-size slide

  105. Experimental code
    coverage support in
    rspec-puppet master
    Gareth Rushgrove

    View full-size slide

  106. at_exit { RSpec::Puppet::Coverage.report! }
    Gareth Rushgrove

    View full-size slide

  107. Total resources: 24
    Touched resources: 8
    Resource coverage: 33.33%
    !
    Untouched resources:
    Class[Nginx]
    File[preferences.d]
    Anchor[apt::update]
    Class[Apt::Params]
    File[sources.list]
    Exec[Required packages: 'debian-keyring debian-arch
    Anchor[apt::source::nginx]
    Class[Apt::Update]
    File[configure-apt-proxy]
    Apt::Key[Add key: 7BD9BF62 from Apt::Source nginx]
    Anchor[apt::key/Add key: 7BD9BF62 from Apt::Source
    Anchor[apt::key 7BD9BF62 present]
    File[nginx.list]
    Gareth Rushgrove

    View full-size slide

  108. A puppet module
    skeleton with
    everything working
    out of the box
    Gareth Rushgrove

    View full-size slide

  109. puppet module
    skeleton

    View full-size slide

  110. puppet module generate sample
    Gareth Rushgrove

    View full-size slide

  111. A pretty complete
    example
    (The Docker module)

    View full-size slide

  112. Gareth Rushgrove

    View full-size slide

  113. Gareth Rushgrove
    Featured on
    the Forge

    View full-size slide

  114. Gareth Rushgrove
    50 pull request
    and counting

    View full-size slide

  115. Gareth Rushgrove
    Contributing
    guidelines

    View full-size slide

  116. Gareth Rushgrove

    View full-size slide

  117. Gareth Rushgrove
    Currently has
    121 tests

    View full-size slide

  118. 6 classes, 2 defines,
    413 lines of puppet
    code, 387 lines of
    test code
    Gareth Rushgrove

    View full-size slide

  119. Take away
    (If all you remember is…)

    View full-size slide

  120. Infrastructure as
    code
    Gareth Rushgrove

    View full-size slide

  121. The first test is the
    hardest
    Gareth Rushgrove

    View full-size slide

  122. Politely demand tests
    for contributions
    Gareth Rushgrove

    View full-size slide

  123. Test the interface not
    the implementation
    Gareth Rushgrove

    View full-size slide

  124. Questions?
    (And thanks for listening)

    View full-size slide