Test Driven Development with Puppet

129
Test Driven Development for Puppet Puppet needs software development Gareth Rushgrove

description

Presented at Puppet Camp London 2014 "Test Driven Development with Puppet" by Gareth Rushgrove, Government Digital Service

Transcript of Test Driven Development with Puppet

Page 1: Test Driven Development with Puppet

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

Page 2: Test Driven Development with Puppet

Who (Who is this person?)

Page 3: Test Driven Development with Puppet

@garethr

Page 4: Test Driven Development with Puppet

UK Government Digital Service

Page 5: Test Driven Development with Puppet
Page 6: Test Driven Development with Puppet
Page 7: Test Driven Development with Puppet
Page 8: Test Driven Development with Puppet

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

Page 9: Test Driven Development with Puppet

Who here is a software developer?

Gareth Rushgrove

Page 10: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 11: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 12: Test Driven Development with Puppet

What is Test Driven Development

(And why should you care)

Page 13: Test Driven Development with Puppet

A common practice in software engineering

Gareth Rushgrove

Page 14: Test Driven Development with Puppet

Not just testing

Gareth Rushgrove

Page 15: Test Driven Development with Puppet

Encourages simple designs and inspires confidence

Gareth Rushgrove

Page 16: Test Driven Development with Puppet

First write an (initially failing) automated test case

Gareth Rushgrove

Page 17: Test Driven Development with Puppet

Then produce the minimum amount of code to pass that test

Gareth Rushgrove

Page 18: Test Driven Development with Puppet

And finally refactor the new code to acceptable standards

Gareth Rushgrove

Page 19: Test Driven Development with Puppet

Test Driven Design

Gareth Rushgrove

Page 20: Test Driven Development with Puppet

Gareth Rushgrove

Page 21: Test Driven Development with Puppet

Unit testing with RSpec and Guard (Not puppet specific)

Page 22: Test Driven Development with Puppet

A unit is the smallest testable part of an application

Gareth Rushgrove

Page 23: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 24: Test Driven Development with Puppet

class Person def say(word) end end

Gareth Rushgrove

Page 25: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 26: Test Driven Development with Puppet

require 'person' !describe Person, "#say" do it "should say something" do ! end end

Gareth Rushgrove

Page 27: Test Driven Development with Puppet

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 28: Test Driven Development with Puppet

Now lets run our test. It should fail

Gareth Rushgrove

Page 29: Test Driven Development with Puppet

rspec

Gareth Rushgrove

Page 30: Test Driven Development with Puppet

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 31: Test Driven Development with Puppet

Now lets write the implementation

Gareth Rushgrove

Page 32: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 33: Test Driven Development with Puppet

And run our test again

Gareth Rushgrove

Page 34: Test Driven Development with Puppet

Person#say should say something !Finished in 0.00199 seconds 1 example, 0 failures

Gareth Rushgrove

Page 35: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 36: Test Driven Development with Puppet

That’s what Guard does

Gareth Rushgrove

Page 37: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 38: Test Driven Development with Puppet

guard

Gareth Rushgrove

Page 39: Test Driven Development with Puppet

Lets see a quick demo

Gareth Rushgrove

Page 40: Test Driven Development with Puppet

Why test puppet code at all

(Testing declarative languages)

Page 41: Test Driven Development with Puppet

Modules increasingly contain logic

Gareth Rushgrove

Page 42: Test Driven Development with Puppet

Modules increasingly take arguments

Gareth Rushgrove

Page 43: Test Driven Development with Puppet

Modules increasingly have interfaces with other modules

Gareth Rushgrove

Page 44: Test Driven Development with Puppet

Modules increasingly used in many operating system and version combinations

Gareth Rushgrove

Page 45: Test Driven Development with Puppet

Modules increasingly used in many Ruby and Puppet version combinations

Gareth Rushgrove

Page 46: Test Driven Development with Puppet

Unit testing puppet with

rspec-puppet (Finally some puppet code)

Page 47: Test Driven Development with Puppet

Unit testing for Puppet

Page 48: Test Driven Development with Puppet

A very simple puppet class

Gareth Rushgrove

Page 49: Test Driven Development with Puppet

class sample { }

Gareth Rushgrove

Page 50: Test Driven Development with Puppet

First write the test

Gareth Rushgrove

Page 51: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 52: Test Driven Development with Puppet

Then run the test

Gareth Rushgrove

Page 53: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 54: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 55: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 56: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 57: Test Driven Development with Puppet

Lets run the tests automatically whenever you change anything

Gareth Rushgrove

Page 58: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 59: Test Driven Development with Puppet

Lets see a quick demo of that too

Gareth Rushgrove

Page 60: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 61: Test Driven Development with Puppet

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

Page 62: Test Driven Development with Puppet

puppet-lint

Gareth Rushgrove

Page 63: Test Driven Development with Puppet

Puppet!style guide

Page 64: Test Driven Development with Puppet

Available!as a gem

Page 65: Test Driven Development with Puppet

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 66: Test Driven Development with Puppet

puppet-syntax

Gareth Rushgrove

Page 67: Test Driven Development with Puppet

Validate Puppet and ERB syntax

Page 68: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 69: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 70: Test Driven Development with Puppet

What is Rake and why do we use it

(Still no puppet)

Page 71: Test Driven Development with Puppet

Rake is a Ruby!build tool

Gareth Rushgrove

Page 72: Test Driven Development with Puppet

It’s like Make but in Ruby

Gareth Rushgrove

Page 73: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 74: Test Driven Development with Puppet

rake

Gareth Rushgrove

Page 75: Test Driven Development with Puppet

rake <command>

Gareth Rushgrove

Page 76: Test Driven Development with Puppet

rake -T

Gareth Rushgrove

Page 77: Test Driven Development with Puppet

Lets make a command to run lint, syntax and spec

Gareth Rushgrove

Page 78: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 79: Test Driven Development with Puppet

rake test

Gareth Rushgrove

Page 80: Test Driven Development with Puppet

Acceptance testing with

beaker (Living on the edge)

Page 81: Test Driven Development with Puppet

Acceptance test against real systems

Gareth Rushgrove

Page 82: Test Driven Development with Puppet

Test what actually happens, not what is meant to happen

Gareth Rushgrove

Page 83: Test Driven Development with Puppet

Build by

Gareth Rushgrove

Page 84: Test Driven Development with Puppet

Very new

Gareth Rushgrove

Page 85: Test Driven Development with Puppet

Test against different operating systems

Gareth Rushgrove

Page 86: Test Driven Development with Puppet

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 87: Test Driven Development with Puppet

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 88: Test Driven Development with Puppet

Supports multiple hypervisors

Gareth Rushgrove

Page 89: Test Driven Development with Puppet

Vagrant hypervisor

Page 90: Test Driven Development with Puppet

VSphere hypervisor

Page 91: Test Driven Development with Puppet

Helpers to install puppet and modules

Gareth Rushgrove

Page 92: Test Driven Development with Puppet

install_puppet

Gareth Rushgrove

Page 93: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 94: Test Driven Development with Puppet

Test that Puppet runs without errors

Gareth Rushgrove

Page 95: Test Driven Development with Puppet

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

Page 96: Test Driven Development with Puppet

Test runs are idempotent

Gareth Rushgrove

Page 97: Test Driven Development with Puppet

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

Page 98: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 99: Test Driven Development with Puppet

Gareth Rushgrove

Page 100: Test Driven Development with Puppet

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 101: Test Driven Development with Puppet

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

Page 102: Test Driven Development with Puppet

Fixtures, matchers

Page 103: Test Driven Development with Puppet

Gareth Rushgrove

Page 104: Test Driven Development with Puppet

Nice continuous integration

Page 105: Test Driven Development with Puppet
Page 106: Test Driven Development with Puppet

Test pull request branches too

Page 107: Test Driven Development with Puppet

--- 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 108: Test Driven Development with Puppet

Official!ruby support

Page 109: Test Driven Development with Puppet

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 110: Test Driven Development with Puppet

Experimental code coverage support in rspec-puppet master

Gareth Rushgrove

Page 111: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 112: Test Driven Development with Puppet

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 113: Test Driven Development with Puppet

A puppet module skeleton with everything working out of the box

Gareth Rushgrove

Page 114: Test Driven Development with Puppet

puppet module skeleton

Page 115: Test Driven Development with Puppet

puppet module generate sample

Gareth Rushgrove

Page 116: Test Driven Development with Puppet

A pretty complete example

(The Docker module)

Page 117: Test Driven Development with Puppet

Gareth Rushgrove

Page 118: Test Driven Development with Puppet

Gareth Rushgrove

Featured on the Forge

Page 119: Test Driven Development with Puppet

Gareth Rushgrove

50 pull request and counting

Page 120: Test Driven Development with Puppet

Gareth Rushgrove

Contributing guidelines

Page 121: Test Driven Development with Puppet

Gareth Rushgrove

Page 122: Test Driven Development with Puppet

Gareth Rushgrove

Currently has 121 tests

Page 123: Test Driven Development with Puppet

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

Gareth Rushgrove

Page 124: Test Driven Development with Puppet

Take away (If all you remember is…)

Page 125: Test Driven Development with Puppet

Infrastructure as code

Gareth Rushgrove

Page 126: Test Driven Development with Puppet

The first test is the hardest

Gareth Rushgrove

Page 127: Test Driven Development with Puppet

Politely demand tests for contributions

Gareth Rushgrove

Page 128: Test Driven Development with Puppet

Test the interface not the implementation

Gareth Rushgrove

Page 129: Test Driven Development with Puppet

Questions? (And thanks for listening)