+ All Categories
Home > Technology > Test Driven Development with Puppet - PuppetConf 2014

Test Driven Development with Puppet - PuppetConf 2014

Date post: 28-Nov-2014
Category:
Upload: puppet-labs
View: 852 times
Download: 3 times
Share this document with a friend
Description:
Test Driven Development with Puppet - Gareth Rushgrove, Puppet Labs
128
Test Driven Development for Puppet Puppet needs software development Gareth Rushgrove
Transcript
Page 1: Test Driven Development with Puppet - PuppetConf 2014

Test Driven Development!for Puppet!Puppet needs software developmentGareth Rushgrove

Page 2: Test Driven Development with Puppet - PuppetConf 2014

Who (Who is this person?)

Page 3: Test Driven Development with Puppet - PuppetConf 2014

@garethr

Page 4: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Page 5: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Page 6: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Page 7: Test Driven Development with Puppet - PuppetConf 2014

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

Page 8: Test Driven Development with Puppet - PuppetConf 2014

Who here is a software developer?

Gareth Rushgrove

Page 9: Test Driven Development with Puppet - PuppetConf 2014

If you’re writing Puppet code you’re a software developer

Gareth Rushgrove

Page 10: Test Driven Development with Puppet - PuppetConf 2014

As a software developer it’s your job to learn software engineering practices

Gareth Rushgrove

Page 11: Test Driven Development with Puppet - PuppetConf 2014

What is Test Driven Development

(And why should you care)

Page 12: Test Driven Development with Puppet - PuppetConf 2014

A common practice in software engineering

Gareth Rushgrove

Page 13: Test Driven Development with Puppet - PuppetConf 2014

Not just testing

Gareth Rushgrove

Page 14: Test Driven Development with Puppet - PuppetConf 2014

Encourages simple designs and inspires confidence

Gareth Rushgrove

Page 15: Test Driven Development with Puppet - PuppetConf 2014

First write an (initially failing) automated test case

Gareth Rushgrove

Page 16: Test Driven Development with Puppet - PuppetConf 2014

Then produce the minimum amount of code to pass that test

Gareth Rushgrove

Page 17: Test Driven Development with Puppet - PuppetConf 2014

And finally refactor the new code to acceptable standards

Gareth Rushgrove

Page 18: Test Driven Development with Puppet - PuppetConf 2014

Test Driven Design

Gareth Rushgrove

Page 19: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Page 20: Test Driven Development with Puppet - PuppetConf 2014

Unit testing with RSpec and Guard (Not puppet specific)

Page 21: Test Driven Development with Puppet - PuppetConf 2014

A unit is the smallest testable part of an application

Gareth Rushgrove

Page 22: Test Driven Development with Puppet - PuppetConf 2014

Testing puppet requires a little Ruby knowledge so we’ll use Ruby examples

Gareth Rushgrove

Page 23: Test Driven Development with Puppet - PuppetConf 2014

class Person def say(word) end end

Gareth Rushgrove

Page 24: Test Driven Development with Puppet - PuppetConf 2014

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

Gareth Rushgrove

Page 25: Test Driven Development with Puppet - PuppetConf 2014

require 'person' !

describe Person, "#say" do it "should say something" do !

end end

Gareth Rushgrove

Page 26: Test Driven Development with Puppet - PuppetConf 2014

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

Page 27: Test Driven Development with Puppet - PuppetConf 2014

Now lets run our test. It should fail

Gareth Rushgrove

Page 28: Test Driven Development with Puppet - PuppetConf 2014

rspec

Gareth Rushgrove

Page 29: Test Driven Development with Puppet - PuppetConf 2014

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

Page 30: Test Driven Development with Puppet - PuppetConf 2014

Now lets write the implementation

Gareth Rushgrove

Page 31: Test Driven Development with Puppet - PuppetConf 2014

class Person def say(word) word + " everyone" end end

Gareth Rushgrove

Page 32: Test Driven Development with Puppet - PuppetConf 2014

And run our test again

Gareth Rushgrove

Page 33: Test Driven Development with Puppet - PuppetConf 2014

Person#say should say something !

Finished in 0.00199 seconds 1 example, 0 failures

Gareth Rushgrove

Page 34: Test Driven Development with Puppet - PuppetConf 2014

Why not have tests automatically run whenever you change the code?

Gareth Rushgrove

Page 35: Test Driven Development with Puppet - PuppetConf 2014

That’s what Guard does

Gareth Rushgrove

Page 36: Test Driven Development with Puppet - PuppetConf 2014

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

Gareth Rushgrove

Page 37: Test Driven Development with Puppet - PuppetConf 2014

guard

Gareth Rushgrove

Page 38: Test Driven Development with Puppet - PuppetConf 2014

Lets see a quick demo

Gareth Rushgrove

Page 39: Test Driven Development with Puppet - PuppetConf 2014

Why test puppet code at all

(Testing declarative languages)

Page 40: Test Driven Development with Puppet - PuppetConf 2014

Modules increasingly contain logic

Gareth Rushgrove

Page 41: Test Driven Development with Puppet - PuppetConf 2014

Modules increasingly take arguments

Gareth Rushgrove

Page 42: Test Driven Development with Puppet - PuppetConf 2014

Modules increasingly have interfaces with other modules

Gareth Rushgrove

Page 43: Test Driven Development with Puppet - PuppetConf 2014

Modules increasingly used in many operating system and version combinations

Gareth Rushgrove

Page 44: Test Driven Development with Puppet - PuppetConf 2014

Modules increasingly used in many Ruby and Puppet version combinations

Gareth Rushgrove

Page 45: Test Driven Development with Puppet - PuppetConf 2014

Unit testing puppet with

rspec-puppet (Finally some puppet code)

Page 46: Test Driven Development with Puppet - PuppetConf 2014

Unit testing for Puppet

Page 47: Test Driven Development with Puppet - PuppetConf 2014

A very simple puppet class

Gareth Rushgrove

Page 48: Test Driven Development with Puppet - PuppetConf 2014

class sample { }

Gareth Rushgrove

Page 49: Test Driven Development with Puppet - PuppetConf 2014

First write the test

Gareth Rushgrove

Page 50: Test Driven Development with Puppet - PuppetConf 2014

require 'spec_helper' !

describe "sample" do it { should create_file('/tmp/sample')} end

Gareth Rushgrove

Page 51: Test Driven Development with Puppet - PuppetConf 2014

Then run the test

Gareth Rushgrove

Page 52: Test Driven Development with Puppet - PuppetConf 2014

sample should contain File[/tmp/sample] (FAILED - 1) !

Finished in 0.4584 seconds 1 example, 1 failure

Gareth Rushgrove

Page 53: Test Driven Development with Puppet - PuppetConf 2014

And then write the (puppet) code to make the test pass

Gareth Rushgrove

Page 54: Test Driven Development with Puppet - PuppetConf 2014

class sample { file { "/tmp/sample": ensure => present, } }

Gareth Rushgrove

Page 55: Test Driven Development with Puppet - PuppetConf 2014

sample should contain File[/tmp/sample] !

Finished in 0.3881 seconds 1 example, 0 failures

Gareth Rushgrove

Page 56: Test Driven Development with Puppet - PuppetConf 2014

Lets run the tests automatically whenever you change anything

Gareth Rushgrove

Page 57: Test Driven Development with Puppet - PuppetConf 2014

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

Gareth Rushgrove

Page 58: Test Driven Development with Puppet - PuppetConf 2014

Lets see a quick demo of that too

Gareth Rushgrove

Page 59: Test Driven Development with Puppet - PuppetConf 2014

You can also test hosts, defines, facts, functions, hieradata

Gareth Rushgrove

Page 60: Test Driven Development with Puppet - PuppetConf 2014

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

Page 61: Test Driven Development with Puppet - PuppetConf 2014

puppet-lint

Gareth Rushgrove

Page 62: Test Driven Development with Puppet - PuppetConf 2014

Puppet!style guide

Page 63: Test Driven Development with Puppet - PuppetConf 2014

Available!as a gem

Page 64: Test Driven Development with Puppet - PuppetConf 2014

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

Page 65: Test Driven Development with Puppet - PuppetConf 2014

puppet-syntax

Gareth Rushgrove

Page 66: Test Driven Development with Puppet - PuppetConf 2014

Validate Puppet and ERB syntax

Page 67: Test Driven Development with Puppet - PuppetConf 2014

require 'puppet-syntax/tasks/puppet-syntax'

Gareth Rushgrove

Page 68: Test Driven Development with Puppet - PuppetConf 2014

rake syntax ---> syntax:manifests ---> syntax:templates ---> syntax:hiera:yaml

Gareth Rushgrove

Page 69: Test Driven Development with Puppet - PuppetConf 2014

What is Rake and why do we use it

(Still no puppet)

Page 70: Test Driven Development with Puppet - PuppetConf 2014

Rake is a Ruby!build tool

Gareth Rushgrove

Page 71: Test Driven Development with Puppet - PuppetConf 2014

It’s like Make but in Ruby

Gareth Rushgrove

Page 72: Test Driven Development with Puppet - PuppetConf 2014

It’s very easy to distribute Rake tasks as Ruby gems

Gareth Rushgrove

Page 73: Test Driven Development with Puppet - PuppetConf 2014

rake

Gareth Rushgrove

Page 74: Test Driven Development with Puppet - PuppetConf 2014

rake <command>

Gareth Rushgrove

Page 75: Test Driven Development with Puppet - PuppetConf 2014

rake -T

Gareth Rushgrove

Page 76: Test Driven Development with Puppet - PuppetConf 2014

Lets make a command to run lint, syntax and spec

Gareth Rushgrove

Page 77: Test Driven Development with Puppet - PuppetConf 2014

task :test => [ :syntax, :lint, :spec, ]

Gareth Rushgrove

Page 78: Test Driven Development with Puppet - PuppetConf 2014

rake test

Gareth Rushgrove

Page 79: Test Driven Development with Puppet - PuppetConf 2014

Acceptance testing with

beaker (Living on the edge)

Page 80: Test Driven Development with Puppet - PuppetConf 2014

Acceptance test against real systems

Gareth Rushgrove

Page 81: Test Driven Development with Puppet - PuppetConf 2014

Test what actually happens, not what is meant to happen

Gareth Rushgrove

Page 82: Test Driven Development with Puppet - PuppetConf 2014

Build by

Gareth Rushgrove

Page 83: Test Driven Development with Puppet - PuppetConf 2014

Very new

Gareth Rushgrove

Page 84: Test Driven Development with Puppet - PuppetConf 2014

Test against different operating systems

Gareth Rushgrove

Page 85: Test Driven Development with Puppet - PuppetConf 2014

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-12042-x64-vbox4210-nocm.box hypervisor: vagrant !CONFIG: log_level: verbose type: foss

Gareth Rushgrove

Page 86: Test Driven Development with Puppet - PuppetConf 2014

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-vbox4210-nocm.box hypervisor : vagrant !CONFIG: log_level: verbose type: foss

Gareth Rushgrove

Page 87: Test Driven Development with Puppet - PuppetConf 2014

Supports multiple hypervisors

Gareth Rushgrove

Page 88: Test Driven Development with Puppet - PuppetConf 2014

Vagrant hypervisor

Page 89: Test Driven Development with Puppet - PuppetConf 2014

VSphere hypervisor

Page 90: Test Driven Development with Puppet - PuppetConf 2014

Helpers to install puppet and modules

Gareth Rushgrove

Page 91: Test Driven Development with Puppet - PuppetConf 2014

install_puppet

Gareth Rushgrove

Page 92: Test Driven Development with Puppet - PuppetConf 2014

puppet('module', 'install', 'puppetlabs-stdlib')

Gareth Rushgrove

Page 93: Test Driven Development with Puppet - PuppetConf 2014

Test that Puppet runs without errors

Gareth Rushgrove

Page 94: Test Driven Development with Puppet - PuppetConf 2014

context 'default parameters' do it 'should work with no errors' do pp = "class { 'sample': }" apply_manifest(pp, :catch_failures => true) end end

Gareth Rushgrove

Page 95: Test Driven Development with Puppet - PuppetConf 2014

Test runs are idempotent

Gareth Rushgrove

Page 96: Test Driven Development with Puppet - PuppetConf 2014

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

Page 97: Test Driven Development with Puppet - PuppetConf 2014

Test that the module installs packages, run services, etc.

Gareth Rushgrove

Page 98: Test Driven Development with Puppet - PuppetConf 2014

serverspec.org

Gareth Rushgrove

Page 99: Test Driven Development with Puppet - PuppetConf 2014

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

Page 100: Test Driven Development with Puppet - PuppetConf 2014

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

Page 101: Test Driven Development with Puppet - PuppetConf 2014

Fixtures, matchers

Page 102: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Page 103: Test Driven Development with Puppet - PuppetConf 2014

Nice continuous integration

Page 104: Test Driven Development with Puppet - PuppetConf 2014
Page 105: Test Driven Development with Puppet - PuppetConf 2014

Test pull request branches too

Page 106: Test Driven Development with Puppet - PuppetConf 2014

--- 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

Page 107: Test Driven Development with Puppet - PuppetConf 2014

Official!ruby support

Page 108: Test Driven Development with Puppet - PuppetConf 2014

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

Page 109: Test Driven Development with Puppet - PuppetConf 2014

Experimental code coverage support in rspec-puppet master

Gareth Rushgrove

Page 110: Test Driven Development with Puppet - PuppetConf 2014

at_exit { RSpec::Puppet::Coverage.report! }

Gareth Rushgrove

Page 111: Test Driven Development with Puppet - PuppetConf 2014

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' for nginx] 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

Page 112: Test Driven Development with Puppet - PuppetConf 2014

A puppet module skeleton with everything working out of the box

Gareth Rushgrove

Page 113: Test Driven Development with Puppet - PuppetConf 2014

puppet module skeleton

Page 114: Test Driven Development with Puppet - PuppetConf 2014

puppet module generate sample

Gareth Rushgrove

Page 115: Test Driven Development with Puppet - PuppetConf 2014

A pretty complete example

(The Docker module)

Page 116: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Page 117: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Featured on the Forge

Page 118: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

100 pull request and counting

Page 119: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Contributing guidelines

Page 120: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Page 121: Test Driven Development with Puppet - PuppetConf 2014

Gareth Rushgrove

Currently has 121 tests

Page 122: Test Driven Development with Puppet - PuppetConf 2014

6 classes, 2 defines, 413 lines of puppet code, 387 lines of test code

Gareth Rushgrove

Page 123: Test Driven Development with Puppet - PuppetConf 2014

Take away (If all you remember is…)

Page 124: Test Driven Development with Puppet - PuppetConf 2014

Infrastructure as code

Gareth Rushgrove

Page 125: Test Driven Development with Puppet - PuppetConf 2014

The first test is the hardest

Gareth Rushgrove

Page 126: Test Driven Development with Puppet - PuppetConf 2014

Politely demand tests for module contributions

Gareth Rushgrove

Page 127: Test Driven Development with Puppet - PuppetConf 2014

Test the interface not the implementation

Gareth Rushgrove

Page 128: Test Driven Development with Puppet - PuppetConf 2014

Questions? (And thanks for listening)


Recommended