Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Test Driven Development with Puppet - PuppetConf 2014

4,765 views

Published on

Test Driven Development with Puppet - Gareth Rushgrove, Puppet Labs

Published in: Technology

Test Driven Development with Puppet - PuppetConf 2014

  1. 1. Test Driven Development! for Puppet! Puppet needs software development Gareth Rushgrove
  2. 2. Who (Who is this person?)
  3. 3. @garethr
  4. 4. Gareth Rushgrove
  5. 5. Gareth Rushgrove
  6. 6. Gareth Rushgrove
  7. 7. The problem (This isn’t a rant, but…)
  8. 8. Who here is a software developer? Gareth Rushgrove
  9. 9. If you’re writing Puppet code you’re a software developer Gareth Rushgrove
  10. 10. As a software developer it’s your job to learn software engineering practices Gareth Rushgrove
  11. 11. What is Test Driven Development (And why should you care)
  12. 12. A common practice in software engineering Gareth Rushgrove
  13. 13. Not just testing Gareth Rushgrove
  14. 14. Encourages simple designs and inspires confidence Gareth Rushgrove
  15. 15. First write an (initially failing) automated test case Gareth Rushgrove
  16. 16. Then produce the minimum amount of code to pass that test Gareth Rushgrove
  17. 17. And finally refactor the new code to acceptable standards Gareth Rushgrove
  18. 18. Test Driven Design Gareth Rushgrove
  19. 19. Gareth Rushgrove
  20. 20. Unit testing with RSpec and Guard (Not puppet specific)
  21. 21. A unit is the smallest testable part of an application Gareth Rushgrove
  22. 22. Testing puppet requires a little Ruby knowledge so we’ll use Ruby examples Gareth Rushgrove
  23. 23. class Person def say(word) end end Gareth Rushgrove
  24. 24. First lets write a test. For this we use the RSpec testing framework Gareth Rushgrove
  25. 25. require 'person' ! describe Person, "#say" do it "should say something" do ! end end Gareth Rushgrove
  26. 26. 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
  27. 27. Now lets run our test. It should fail Gareth Rushgrove
  28. 28. rspec Gareth Rushgrove
  29. 29. 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
  30. 30. Now lets write the implementation Gareth Rushgrove
  31. 31. class Person def say(word) word + " everyone" end end Gareth Rushgrove
  32. 32. And run our test again Gareth Rushgrove
  33. 33. Person#say should say something ! Finished in 0.00199 seconds 1 example, 0 failures Gareth Rushgrove
  34. 34. Why not have tests automatically run whenever you change the code? Gareth Rushgrove
  35. 35. That’s what Guard does Gareth Rushgrove
  36. 36. guard :rspec, cmd: 'bundle exec rspec' do watch(%r{^spec/.+_spec.rb$}) watch(%r{^lib/.+.rb$}) { 'spec' } end Gareth Rushgrove
  37. 37. guard Gareth Rushgrove
  38. 38. Lets see a quick demo Gareth Rushgrove
  39. 39. Why test puppet code at all (Testing declarative languages)
  40. 40. Modules increasingly contain logic Gareth Rushgrove
  41. 41. Modules increasingly take arguments Gareth Rushgrove
  42. 42. Modules increasingly have interfaces with other modules Gareth Rushgrove
  43. 43. Modules increasingly used in many operating system and version combinations Gareth Rushgrove
  44. 44. Modules increasingly used in many Ruby and Puppet version combinations Gareth Rushgrove
  45. 45. Unit testing puppet with rspec-puppet (Finally some puppet code)
  46. 46. Unit testing for Puppet
  47. 47. A very simple puppet class Gareth Rushgrove
  48. 48. class sample { } Gareth Rushgrove
  49. 49. First write the test Gareth Rushgrove
  50. 50. require 'spec_helper' ! describe "sample" do it { should create_file('/tmp/sample')} end Gareth Rushgrove
  51. 51. Then run the test Gareth Rushgrove
  52. 52. sample should contain File[/tmp/sample] (FAILED - 1) ! Finished in 0.4584 seconds 1 example, 1 failure Gareth Rushgrove
  53. 53. And then write the (puppet) code to make the test pass Gareth Rushgrove
  54. 54. class sample { file { "/tmp/sample": ensure => present, } } Gareth Rushgrove
  55. 55. sample should contain File[/tmp/sample] ! Finished in 0.3881 seconds 1 example, 0 failures Gareth Rushgrove
  56. 56. Lets run the tests automatically whenever you change anything Gareth Rushgrove
  57. 57. guard :rspec, cmd: 'bundle exec rspec' do watch(%r{^spec/.+_spec.rb$}) watch(%r{^manifests/.+.pp$}) { 'spec' } end Gareth Rushgrove
  58. 58. Lets see a quick demo of that too Gareth Rushgrove
  59. 59. You can also test hosts, defines, facts, functions, hieradata Gareth Rushgrove
  60. 60. Syntax checking, linting, oh my (Creating a build process)
  61. 61. puppet-lint Gareth Rushgrove
  62. 62. Puppet! style guide
  63. 63. Available! as a gem
  64. 64. 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
  65. 65. puppet-syntax Gareth Rushgrove
  66. 66. Validate Puppet and ERB syntax
  67. 67. require 'puppet-syntax/tasks/puppet-syntax' Gareth Rushgrove
  68. 68. rake syntax ---> syntax:manifests ---> syntax:templates ---> syntax:hiera:yaml Gareth Rushgrove
  69. 69. What is Rake and why do we use it (Still no puppet)
  70. 70. Rake is a Ruby! build tool Gareth Rushgrove
  71. 71. It’s like Make but in Ruby Gareth Rushgrove
  72. 72. It’s very easy to distribute Rake tasks as Ruby gems Gareth Rushgrove
  73. 73. rake Gareth Rushgrove
  74. 74. rake <command> Gareth Rushgrove
  75. 75. rake -T Gareth Rushgrove
  76. 76. Lets make a command to run lint, syntax and spec Gareth Rushgrove
  77. 77. task :test => [ :syntax, :lint, :spec, ] Gareth Rushgrove
  78. 78. rake test Gareth Rushgrove
  79. 79. Acceptance testing with beaker (Living on the edge)
  80. 80. Acceptance test against real systems Gareth Rushgrove
  81. 81. Test what actually happens, not what is meant to happen Gareth Rushgrove
  82. 82. Build by Gareth Rushgrove
  83. 83. Very new Gareth Rushgrove
  84. 84. Test against different operating systems Gareth Rushgrove
  85. 85. 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/ubuntu-server-hypervisor: vagrant ! CONFIG: log_level: verbose type: foss Gareth Rushgrove
  86. 86. 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/centos-64-x64-hypervisor : vagrant ! CONFIG: log_level: verbose type: foss Gareth Rushgrove
  87. 87. Supports multiple hypervisors Gareth Rushgrove
  88. 88. Vagrant hypervisor
  89. 89. VSphere hypervisor
  90. 90. Helpers to install puppet and modules Gareth Rushgrove
  91. 91. install_puppet Gareth Rushgrove
  92. 92. puppet('module', 'install', 'puppetlabs-stdlib') Gareth Rushgrove
  93. 93. Test that Puppet runs without errors Gareth Rushgrove
  94. 94. context 'default parameters' do it 'should work with no errors' do pp = "class { 'sample': }" apply_manifest(pp, :catch_failures => true) end end Gareth Rushgrove
  95. 95. Test runs are idempotent Gareth Rushgrove
  96. 96. context 'default parameters' do it 'should work with no errors' do pp = "class { 'sample': }" apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) end end Gareth Rushgrove
  97. 97. Test that the module installs packages, run services, etc. Gareth Rushgrove
  98. 98. serverspec.org Gareth Rushgrove
  99. 99. 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
  100. 100. Other useful tools (and what we’re still missing)
  101. 101. Fixtures, matchers
  102. 102. Gareth Rushgrove
  103. 103. Nice continuous integration
  104. 104. Test pull request branches too
  105. 105. --- 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
  106. 106. Official! ruby support
  107. 107. 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
  108. 108. Experimental code coverage support in rspec-puppet master Gareth Rushgrove
  109. 109. at_exit { RSpec::Puppet::Coverage.report! } Gareth Rushgrove
  110. 110. 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-archive-keyring' 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 nginx] Anchor[apt::key 7BD9BF62 present] File[nginx.list] Exec[apt_update] Gareth Rushgrove
  111. 111. A puppet module skeleton with everything working out of the box Gareth Rushgrove
  112. 112. puppet module skeleton
  113. 113. puppet module generate sample Gareth Rushgrove
  114. 114. A pretty complete example (The Docker module)
  115. 115. Gareth Rushgrove
  116. 116. Gareth Rushgrove Featured on the Forge
  117. 117. Gareth Rushgrove 100 pull request and counting
  118. 118. Gareth Rushgrove Contributing guidelines
  119. 119. Gareth Rushgrove
  120. 120. Gareth Rushgrove Currently has 121 tests
  121. 121. 6 classes, 2 defines, 413 lines of puppet code, 387 lines of test code Gareth Rushgrove
  122. 122. Take away (If all you remember is…)
  123. 123. Infrastructure as code Gareth Rushgrove
  124. 124. The first test is the hardest Gareth Rushgrove
  125. 125. Politely demand tests for module contributions Gareth Rushgrove
  126. 126. Test the interface not the implementation Gareth Rushgrove
  127. 127. Questions? (And thanks for listening)

×