Your SlideShare is downloading. ×
20140406 loa days-tdd-with_puppet_tutorial
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

20140406 loa days-tdd-with_puppet_tutorial

774
views

Published on

Published in: Technology, Spiritual

1 Comment
6 Likes
Statistics
Notes
No Downloads
Views
Total Views
774
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
34
Comments
1
Likes
6
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. TDD with Puppet ! LOADays 2014-04-06 Antwerp, BE! ! Garrett Honeycutt @learnpuppet gh@learnpuppet.com http://learnpuppet.com
  • 2. # whoami
  • 3. Where are we going? • Why test? • What makes a good module • Tools • SetupVM • Hack • Travis-ci • More Hacking 3
  • 4. LearnPuppet.com • Training • 3 day Intro course • 2 day advanced course • Consulting • Auditing 4
  • 5. Why test? 5 • Confidence to change things
  • 6. Why test? 6 • Confidence to change things • Know when you break something before deploying it
  • 7. Why test? 7 • Confidence to change things • Know when you break something before deploying it • Quickly test a matrix of Puppet and Ruby versions
  • 8. Why test? 8 • Confidence to change things • Know when you break something before deploying it • Quickly test a matrix of Puppet and Ruby versions • Test all OS’s without having to deploy it everywhere
  • 9. Why test? 9 • Confidence to change things • Know when you break something before deploying it • Quickly test a matrix of Puppet and Ruby versions • Test all OS’s without having to deploy it everywhere • Fast feedback
  • 10. Why test? 10 • Confidence to change things • Know when you break something before deploying it • Quickly test a matrix of Puppet and Ruby versions • Test all OS’s without having to deploy it everywhere • Prevent regression of old problems • Fast feedback
  • 11. Why test? 11 • Confidence to change things • Know when you break something before deploying it • Quickly test a matrix of Puppet and Ruby versions • Test all OS’s without having to deploy it everywhere • Prevent regression of old problems • Fast feedback • Even in an agile world, we still have design specs.
  • 12. Why test first? • Puts a focus on what you want to accomplish. • Documents the functionality that you care about. • Makes you think about your design. • Save time by building the minimum viable product first. • You can refactor later. 12
  • 13. What to test? • Each parameter • Each resource • Ensure that failure occurs when that’s expected • Conditional logic 13
  • 14. What is actually tested? • Catalog is compiled with inputs such as setting values for facts and parameters • We test that things are or are not in the catalog • Simple :) 14
  • 15. Semver • Explanation of semantic versioning - semver.org 15
  • 16. What is 1.0.0 • README explains all parameters • Passes lint • Works with at least Ruby 1.8.7, 1.9.3, and 2.0.0 • Validates params • Tests all params • Tests all flows in logic 16
  • 17. approach to writing modules • Write the README first, explaining all of your parameters and their valid values and their default values. • Add all of the parameters to your manifests with default values from the README. • Write the tests from the README. • Write just enough code to get your tests to pass. • Refactor as necessary. 17
  • 18. 18
  • 19. Get VM • Install VirtualBox - https://www.virtualbox.org/ • Install Vagrant - http://www.vagrantup.com/ • git clone https://github.com/ghoneycutt/learnpuppet-tdd-vagrant • cd learnpuppet-tdd-vagrant • vagrant up 21
  • 20. Testing tools • Only if you are not using the providedVM $ sudo gem install -V puppet-lint rspec rspec-puppet puppetlabs_spec_helper --no-ri --no-rdoc • https://github.com/puppetlabs/puppet-syntax-vim • https://github.com/puppetlabs/puppet-syntax-emacs 22
  • 21. RVM http://www.rvm.io/ ! • Allows you to easily switch between multiple versions of Ruby 23
  • 22. Ruby Versions • 1.8.7 • 1.9.3 • 2.0.0 • 2.1.0 (Coming with Puppet v3.5.0) 24
  • 23. rspec-puppet http://rspec-puppet.com/ Thanks,Tim! 25
  • 24. Puppet Module Skeleton • $ git clone https://github.com/ghoneycutt/puppet- module-skeleton • $ mkdir -p `puppet config print vardir`/puppet- module/skeleton/ • $ rsync -avp --exclude .git puppet-module- skeleton/ `puppet config print vardir`/puppet- module/skeleton/ 26
  • 25. Create a module • generate motd module $ puppet module generate forgename-motd 27
  • 26. Components ghoneycutt-motd ghoneycutt-motd/.fixtures.yml ghoneycutt-motd/.gitignore ghoneycutt-motd/.travis.yml ghoneycutt-motd/Gemfile ghoneycutt-motd/LICENSE ghoneycutt-motd/Modulefile ghoneycutt-motd/README.md ghoneycutt-motd/Rakefile ghoneycutt-motd/manifests ghoneycutt-motd/manifests/init.pp ghoneycutt-motd/spec ghoneycutt-motd/spec/classes ghoneycutt-motd/spec/classes/init_spec.rb ghoneycutt-motd/spec/fixtures ghoneycutt-motd/spec/fixtures/manifests ghoneycutt-motd/spec/fixtures/manifests/site.pp ghoneycutt-motd/spec/fixtures/modules ghoneycutt-motd/spec/spec_helper.rb 28
  • 27. Components ghoneycutt-motd ghoneycutt-motd/.fixtures.yml ghoneycutt-motd/.gitignore ghoneycutt-motd/.travis.yml ghoneycutt-motd/Gemfile ghoneycutt-motd/LICENSE ghoneycutt-motd/Modulefile ghoneycutt-motd/README.md ghoneycutt-motd/Rakefile ghoneycutt-motd/manifests ghoneycutt-motd/manifests/init.pp ghoneycutt-motd/spec ghoneycutt-motd/spec/classes ghoneycutt-motd/spec/classes/init_spec.rb ghoneycutt-motd/spec/fixtures ghoneycutt-motd/spec/fixtures/manifests ghoneycutt-motd/spec/fixtures/manifests/site.pp ghoneycutt-motd/spec/fixtures/modules ghoneycutt-motd/spec/spec_helper.rb 29
  • 28. .fixtures.yml • List all of your dependencies from Modulefile 30
  • 29. Gemfile • Used by Bundler 31
  • 30. .travis.yml • Configure travis-ci.org 32
  • 31. spec_helper.rb • Code that is run before your spec tests. • Configures the spec testing environment. 33
  • 32. Rakefile • Validate syntax rake validate ! • Validate style rake lint 34
  • 33. Rakefile • show all tasks rake -T 35
  • 34. rake spec • rake spec calls • rake spec_prep • rake spec_standalone • rake spec_clean 36
  • 35. run tests $ SPEC_OPTS="--format documentation" rake spec_standalone 37
  • 36. first test it { should contain_file('motd').with({ 'ensure' => 'file', 'path' => '/etc/motd', 'owner' => 'root', 'group' => 'root', 'mode' => '0644', 'content' => nil, }) } 38
  • 37. run tests • It fails! Now let’s fill in the code. 39
  • 38. testing params • Each attribute of the file resource should be configurable through params. • Let’s test for values that should should work as well as what should produce an error. 40
  • 39. testing paramsdescribe 'with path specified' do context 'as a valid path' do let(:params) { { :path => '/usr/local/etc/motd' } } ! it { should contain_file('motd').with({ 'path' => '/usr/local/etc/motd', }) } end ! context 'as an invalid path' do let(:params) { { :path => 'invalid/path' } } ! it 'should fail' do expect { should contain_class('motd') }.to raise_error(Puppet::Error) end end 41
  • 40. testing file content describe 'with content parameter specified' do let(:params) { { :content => "Welcome to puppet.learnpuppet.comnnHave Fun!n" } } ! ! it { should contain_file('motd').with_content( %{Welcome to puppet.learnpuppet.com ! Have Fun! }) } end 42
  • 41. reading tests $ grep -ie describe -e context spec/classes/init_spec.rb describe 'motd' do context 'with default values for all parameters' do describe 'with motd_file parameter specified' do context 'as a valid path' do context 'as an invalid path' do describe 'with motd_content parameter specified' do 43
  • 42. Exercise Test all params • All attributes of file resource should be configurable. • Write tests first. • Then add code to the module. 44
  • 43. four digit mode describe 'with motd_mode specified' do context 'as a valid four digit entry' do let(:params) { { :mode => '0755' } } ! it { should contain_file('motd').with({ 'mode' => '0755', }) } end ! context 'as an invalid three digit entry' do let(:params) { { :mode => '755' } } ! it 'should fail' do expect { should contain_class('motd') }.to raise_error(Puppet::Error,/^motd::mode must be a four digit string./) end end end 45
  • 44. for loops ['666','66666','invalid',true].each do |mode| context "as invalid value #{mode}" do let(:params) { { :motd_mode => mode } } ! it 'should fail' do expect { should contain_class('motd') }.to raise_error(Puppet::Error,/^motd::mode must be a four digit string./) end end end 46
  • 45. Exercise Validate mode • Validate mode with validate_re() https://github.com/puppetlabs/puppetlabs-stdlib/tree/3.2.0#validate_re • Test your regex at http://rubular.com/ 47
  • 46. resource relationships# package it { should contain_package('ntp_package').with({ ... }) } ! # file it { should contain_file('ntp_config').with({ ... 'require' => 'Package[ntp]', }) } ! # service it { should contain_service('ntp_service').with({ ... 'subscribe' => 'File[ntp_config]', }) } 48
  • 47. file content # check for a specific line ! it { should contain_file('ntp_conf').with_content(/^tinker panic 0$/) } 49
  • 48. file content # what if the whole line is optional? # in this case we test that it is not present ! it { should_not contain_file('ntp_conf').with_content(/^tinker panic 0$/) } 50
  • 49. Exercise ntp module • Use the last few slides to guide you on a module for NTP • Do the minimum amount of work to get the tests to pass. • Copy /etc/ntp.conf to your module as a starting place 51
  • 50. specify facts context 'with default values for parameters on EL 6' do let(:facts) do { :osfamily => 'RedHat', :lsbmajdistrelease => '6', } end end 52
  • 51. Exercise add OS to ntp • Add support for another OS.This OS should have at least a different name for the package or service. 53
  • 52. 54 GitHub HowTo
  • 53. Travis-ci.org • Free! • Matrix testing • Integrates with GitHub • Tests every pull request automatically • Free! 55
  • 54. .travis.yml--- env: - PUPPET_VERSION=3.3.2 - PUPPET_VERSION=3.4.2 notifications: email: false rvm: - 1.8.7 - 1.9.3 - 2.0.0 language: ruby before_script: "gem install --no-ri --no-rdoc bundler" script: 'bundle exec rake validate && bundle exec rake lint && SPEC_OPTS="--format documentation" bundle exec rake spec' gemfile: Gemfile 56
  • 55. 57 Integrate with Travis
  • 56. Test functions # lib/puppet/parser/functions/yell.rb module Puppet::Parser::Functions newfunction(:yell, :type => :rvalue, :doc => <<-EOS Takes one argument, a string to be capitalized. Returns the string in all caps. EOS ) do |args| raise(Puppet::ParseError, "yell(): Wrong number of arguments " + "given (#{args.size} for 1)") if args.size != 1 args[0].upcase end end 58
  • 57. Test functions # spec/functions/yell_spec.rb require 'spec_helper' describe 'yell' do it 'should run with correct number of arguments (1)' do should run.with_params('hello world').and_return('HELLO WORLD') end ! it 'should fail with no arguments' do should run.with_params().and_raise_error(Puppet::ParseError) end ! it 'should fail with more than one argument (2)' do should run.with_params('too','many').and_raise_error(Puppet::ParseError) end end 59
  • 58. Defines# spec/defines/mkdir_p_spec.rb require 'spec_helper' describe 'common::mkdir_p' do context 'should create new directory' do let(:title) { '/some/dir/structure' } ! it { should contain_exec('mkdir_p-/some/dir/structure').with({ 'command' => 'mkdir -p /some/dir/structure', 'unless' => 'test -d /some/dir/structure', }) } end ! context 'should fail with a path that is not absolute' do let(:title) { 'not/a/valid/absolute/path' } ! it do expect { should contain_exec('mkdir_p-not/a/valid/absolute/path').with({ 'command' => 'mkdir -p not/a/valid/absolute/path', 'unless' => 'test -d not/a/valid/absolute/path', }) }.to raise_error(Puppet::Error) end end end 60
  • 59. Exercise Defines • Create a define,‘say’, that takes a param,‘msg’ or if msg is not sent, use the title and pass that to a notify{} resource. • Write tests first, then write the define. • Bonus to create your own function to run on the msg, such as making it all lower case or l33t sp34k. 61
  • 60. Hashes • https://github.com/ghoneycutt/puppet-module-vim/blob/master/ spec/classes/init_spec.rb 62
  • 61. Exercise refactor ntp • Refactor ntp module to use a hash to specify differences between OS’s 63
  • 62. TDD with Puppet ! LOADays 2014-04-06 Antwerp, BE! ! Garrett Honeycutt @learnpuppet gh@learnpuppet.com http://learnpuppet.com