Transcript
Page 1: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

How to develop Puppet ModulesFrom source to the Forge with zero clicks

Carlos Sanchez@csanchezhttp://csanchez.orghttp://maestrodev.com

Page 3: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Modules

Page 4: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 5: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 6: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

We use 50 modules

Page 7: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 8: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

DEV QA OPS

Modules ARE software

Page 9: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

oh my

VersionsDependenciesIncompabilities

Page 10: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 11: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Specs

Page 12: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

RSpec-Puppet

Page 13: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Gemfile

source 'https://rubygems.org'

group :rake do gem 'puppet' gem 'rake' gem 'puppet-lint' gem 'rspec-puppet'end

Page 14: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

modules

{module}/ spec/ spec_helper.rb classes/ {class}_spec.pp definitions/ fixtures/ hosts/

Page 15: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

maven::maven

class maven::maven( $version = '3.0.5', $repo = { #url => 'http://repo1.maven.org/maven2', #username => '', #password => '', } ) {

if "x${repo['url']}x" != 'xx' { wget::authfetch { 'fetch-maven': source => "${repo['url']}/.../$version/apache-maven-${version}-bin.tar.gz", destination => $archive, user => $repo['username'], password => $repo['password'], before => Exec['maven-untar'], } } else { wget::fetch { 'fetch-maven': source => "http://archive.apache.org/.../apache-maven-${version}-bin.tar.gz", destination => $archive, before => Exec['maven-untar'], } }

Page 16: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

rspec-puppet

require 'spec_helper'

describe 'maven::maven' do

context "when downloading maven from another repo" do let(:params) { { :repo => { 'url' => 'http://repo1.maven.org/maven2', 'username' => 'u', 'password' => 'p' } } }

it 'should fetch maven with username and password' do should contain_wget__authfetch('fetch-maven').with( 'source' => 'http://repo1.maven.org/...ven-3.0.5-bin.tar.gz', 'user' => 'u', 'password' => 'p') end endend

Page 17: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

hosts

node 'agent' inherits 'parent' { include wget

include maestro::test::dependencies include maestro_nodes::agentrvm}

Page 18: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

hosts

require 'spec_helper'

describe 'agent' do it do should contain_class('maestro::agent').with( 'agent_name' => 'agent-01', 'stomp_host' => 'maestro.maestrodev.net') end it { should_not contain_service('maestro') } it { should_not contain_service('activemq') } it { should_not contain_service('jenkins') } it { should_not contain_service('postgresqld') } it { should_not contain_service('maestro-test-hub') } it { should_not contain_service('sonar') } it { should_not contain_service('archiva') }end

Page 19: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

rspec-puppet with facts

require 'spec_helper'

describe 'wget' do

context 'running on OS X' do let(:facts) { {:operatingsystem => 'Darwin'} } it { should_not contain_package('wget') } end

context 'running on CentOS' do let(:facts) { {:operatingsystem => 'CentOS'} } it { should contain_package('wget') } end

context 'no version specified' do it { should contain_package('wget').with_ensure('installed') } end

context 'version is 1.2.3' do let(:params) { {:version => '1.2.3'} } it { should contain_package('wget').with_ensure('1.2.3') } endend

Page 20: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

shared_context

shared_context :centos do

let(:facts) {{ :operatingsystem => 'CentOS', :kernel => 'Linux', :osfamily => 'RedHat' }}

end

describe 'maestro::maestro' do include_context :centos ...end

Page 21: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

extending for reuse

describe 'maestro::maestro' do include_context :centos

let(:facts) { super().merge({ :operatingsystem => 'RedHat' })}end

Page 22: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

shared_examples

require 'spec_helper'

describe 'nginx::package' do

shared_examples 'redhat' do |operatingsystem| let(:facts) {{ :operatingsystem => operatingsystem }} it { should contain_package('nginx') } it { should contain_package('gd') } it { should contain_package('libXpm') } it { should contain_package('libxslt') } it { should contain_yumrepo('nginx-release').with_enabled('1') } end

shared_examples 'debian' do |operatingsystem| let(:facts) {{ :operatingsystem => operatingsystem }} it { should contain_file('/etc/apt/sources.list.d/nginx.list') } end

Page 23: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

shared_examples (cont.)

context 'RedHat' do it_behaves_like 'redhat', 'centos' it_behaves_like 'redhat', 'fedora' it_behaves_like 'redhat', 'rhel' it_behaves_like 'redhat', 'redhat' it_behaves_like 'redhat', 'scientific' end

context 'debian' do it_behaves_like 'debian', 'debian' it_behaves_like 'debian', 'ubuntu' end

context 'other' do let(:facts) {{ :operatingsystem => 'xxx' }} it { expect { subject }.to raise_error(Puppet::Error, /Module nginx is not supported on xxx/) } endend

Page 24: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

puppetlabs_spec_helper

Page 25: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Gemfile

source 'https://rubygems.org'

group :rake do gem 'puppet' gem 'rspec-puppet' gem 'rake' gem 'puppet-lint' gem 'puppetlabs_spec_helper'end

Page 26: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Rakefile

require 'puppetlabs_spec_helper/rake_tasks'

build # Build puppet module package

clean # Clean a built module package

coverage # Generate code coverage information

lint # Check puppet manifests with puppet-lint

spec # Run spec tests in a clean fixtures directory

spec_clean # Clean up the fixtures directory

spec_prep # Create the fixtures directory

spec_standalone # Run spec tests on an existing fixtures directory

Page 27: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

spec/spec_helper.rb

require 'puppetlabs_spec_helper/module_spec_helper'

RSpec.configure do |c|

c.before(:each) do Puppet::Util::Log.level = :warning Puppet::Util::Log.newdestination(:console) end

end

Page 28: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

.fixtures

# bring modules into spec/fixturesfixtures: repositories: firewall: "git://github.com/puppetlabs/puppetlabs-firewall" stdlib: repo: "git://github.com/puppetlabs/puppetlabs-stdlib" ref: "2.6.0" symlinks: my_module: "#{source_dir}"

Page 29: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

librarian-puppet

Page 30: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Gemfile

source 'https://rubygems.org'

group :rake do gem 'puppet' gem 'rspec-puppet' gem 'rake' gem 'puppet-lint' gem 'puppetlabs_spec_helper' gem 'librarian-puppet-maestrodev'end

Page 31: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Puppetfile

forge 'http://forge.puppetlabs.com'

mod 'maestrodev/activemq', '>=1.0'mod 'saz/limits', ">=2.0.1"mod 'maestrodev/maestro_nodes', '>=1.1.0'mod 'maestrodev/maestro_demo', '>=1.0.2'mod 'maestrodev', :path => './private_modules/maestrodev'mod 'nginx', :git => 'https://github.com/jfryman/puppet-nginx.git'

Page 32: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 33: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Puppetfile.lock

FORGE remote: http://forge.puppetlabs.com specs: jfryman/nginx (0.0.2) puppetlabs/stdlib (>= 0.1.6) maestrodev/activemq (1.2.0) maestrodev/wget (>= 1.0.0) maestrodev/android (1.1.0) maestrodev/wget (>= 1.0.0) maestrodev/ant (1.0.4) maestrodev/wget (>= 0.0.1) maestrodev/archiva (1.1.0) maestrodev/wget (>= 1.0.0) maestrodev/git (1.0.1) maestrodev/jenkins (1.0.1) maestrodev/maestro (1.2.13) maestrodev/maven (>= 1.0.0) maestrodev/wget (>= 1.0.0) puppetlabs/postgresql (= 2.0.1) puppetlabs/stdlib (>= 2.5.1) maestrodev/maestro_demo (1.0.5) maestrodev/android (>= 1.1.0) maestrodev/maestro (>= 1.2.0) puppetlabs/postgresql (= 2.0.1) maestrodev/maestro_nodes (1.3.0) jfryman/nginx (>= 0.0.0) maestrodev/activemq (>= 1.0.0) maestrodev/ant (>= 1.0.3) maestrodev/archiva (>= 1.0.0) maestrodev/git (>= 1.0.0) maestrodev/jenkins (>= 1.0.0) maestrodev/maestro (>= 1.1.0) maestrodev/maven (>= 0.0.2) maestrodev/rvm (>= 1.0.0) maestrodev/sonar (>= 1.0.0) maestrodev/ssh_keygen (>= 1.0.0) maestrodev/statsd (>= 0.0.0) maestrodev/svn (>= 1.0.0)

puppetlabs/java (>= 0.3.0) puppetlabs/mongodb (>= 0.1.0) puppetlabs/nodejs (>= 0.3.0) puppetlabs/ntp (>= 0.0.0) stahnma/epel (>= 0.0.0) maestrodev/maven (1.1.2) maestrodev/wget (>= 1.0.0) maestrodev/rvm (1.1.5) maestrodev/sonar (1.0.0) maestrodev/maven (>= 0.0.2) maestrodev/wget (>= 0.0.1) puppetlabs/stdlib (>= 2.3.0) maestrodev/ssh_keygen (1.0.0) maestrodev/statsd (1.0.3) puppetlabs/nodejs (>= 0.2.0) maestrodev/svn (1.1.0) maestrodev/wget (1.2.0) puppetlabs/apt (1.2.0) puppetlabs/stdlib (>= 2.2.1) puppetlabs/firewall (0.4.0) puppetlabs/java (1.0.1) puppetlabs/stdlib (>= 0.1.6) puppetlabs/mongodb (0.1.0) puppetlabs/apt (>= 0.0.2) puppetlabs/nodejs (0.3.0) puppetlabs/apt (>= 0.0.3) puppetlabs/stdlib (>= 2.0.0) puppetlabs/ntp (1.0.1) puppetlabs/stdlib (>= 0.1.6) puppetlabs/postgresql (2.0.1) puppetlabs/apt (< 2.0.0, >= 1.1.0) puppetlabs/firewall (>= 0.0.4) puppetlabs/stdlib (< 4.0.0, >= 3.2.0) puppetlabs/stdlib (3.2.0) saz/limits (2.0.1) stahnma/epel (0.0.5)

GIT remote: https://github.com/jfryman/puppet-nginx.git ref: master sha: fd4e3c5a3719132bacabe6238ad2ad31fa3ba48c specs: nginx (0.0.2) puppetlabs/stdlib (>= 0.1.6)

PATH remote: ./private_modules/maestrodev specs: maestrodev (0.0.1)

DEPENDENCIES maestrodev (>= 0) maestrodev/activemq (>= 1.0) maestrodev/maestro_demo (>= 1.0.2) maestrodev/maestro_nodes (>= 1.1.0) nginx (>= 0) saz/limits (>= 2.0.1)

Page 34: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

librarian-puppet

clean # Cleans out the cache and install paths.init # Initializes the current directoryinstall # Resolves and installs all of the dependencies you specifyoutdated # Lists outdated dependencies.package # Cache the puppet modules in vendor/puppet/cacheshow # Shows dependenciesupdate # Updates and installs the dependencies you specify

Page 35: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

librarian-puppet for fixtures

# use librarian-puppet to manage fixtures instead of .fixtures.yml. Offers more possibilities like explicit version management, forge downloads,...

task :librarian_spec_prep do sh "librarian-puppet install --path=spec/fixtures/modules/"end

task :spec_prep => :librarian_spec_prep

Page 36: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

.fixtures

fixtures: symlinks: my_module: "#{source_dir}"

Page 37: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Vagrant

Page 38: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

tests/init.pp

stage { 'epel': before => Stage['rvm-install']}

class { 'epel': stage => 'epel' } ->class { 'rvm': }

Page 39: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Vagrantfile

Vagrant.configure("2") do |config|

config.vm.synced_folder ".", "/etc/puppet/modules/rvm"

# install the epel module needed for rvm in CentOS config.vm.provision :shell, :inline => "test -d /etc/puppet/modules/epel || puppet module install stahnma/epel -v 0.0.3"

config.vm.provision :puppet do |puppet| puppet.manifests_path = "tests" puppet.manifest_file = "init.pp" end

config.vm.define :centos63 do |config| config.vm.box = "CentOS-6.3-x86_64-minimal" config.vm.box_url = "https://repo.maestrodev.com/archiva/repository/public-releases/com/maestrodev/vagrant/CentOS/6.3/CentOS-6.3-x86_64-minimal.box" end

config.vm.define :centos64 do |config| config.vm.box = "CentOS-6.4-x86_64-minimal" config.vm.box_url = "https://repo.maestrodev.com/archiva/repository/public-releases/com/maestrodev/vagrant/CentOS/6.4/CentOS-6.4-x86_64-minimal.box" end

end

Page 40: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Rakefile

desc "Integration test with Vagrant"task :integration do sh %{vagrant destroy --force} sh %{vagrant up} sh %{vagrant destroy --force}end

Page 41: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Rakefile

# start one at a timedesc "Integration test with Vagrant"task :integration do sh %{vagrant destroy --force} ["centos63", "centos64"].each do |vm| sh %{vagrant up #{vm}} sh %{vagrant destroy --force #{vm}} end sh %{vagrant destroy --force}end

Page 42: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 43: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Blacksmith

Page 44: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

gem 'puppet-blacksmith'

Page 45: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 46: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Rakefile

require 'puppet_blacksmith/rake_tasks'

Page 47: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Rake

module:bump # Bump module version to the next minormodule:bump_commit # Bump version and git commitmodule:clean # Runs clean againmodule:push # Push module to the Puppet Forgemodule:release # Release the Puppet module, doing a clean, build, tag, push, bump_commit and git pushmodule:tag # Git tag with the current module version

Page 48: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

~/.puppetforge.yml

---forge: https://forge.puppetlabs.comusername: myusernamepassword: mypassword

Page 49: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

just remember

create project in the Forge first(not yet implemented)

2.0.0 is built as a library to be reused

Page 50: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

All together

Page 51: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Maven module

http://github.com/maestrodev/puppet-maven

Page 52: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Modulefile

name 'maestrodev-maven'version '1.1.3'

author 'maestrodev'license 'Apache License, Version 2.0'project_page 'http://github.com/maestrodev/puppet-maven'source 'http://github.com/maestrodev/puppet-maven'summary 'Apache Maven module for Puppet'description 'A Puppet module to download artifacts from Maven repositories'

dependency 'maestrodev/wget', '>=1.0.0'

Page 53: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Gemfile

source 'https://rubygems.org'

group :rake do gem 'puppet', '>=2.7.20' gem 'rspec-puppet', '>=0.1.3' gem 'rake', '>=0.9.2.2' gem 'puppet-lint', '>=0.1.12' gem 'puppetlabs_spec_helper' gem 'puppet-blacksmith', '>=1.0.5' gem 'librarian-puppet-maestrodev', '>=0.9.8'end

Page 54: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Rakefile

require 'bundler'Bundler.require(:rake)require 'rake/clean'

CLEAN.include('spec/fixtures/', 'doc', 'pkg')CLOBBER.include('.tmp', '.librarian')

require 'puppetlabs_spec_helper/rake_tasks'require 'puppet_blacksmith/rake_tasks'

task :librarian_spec_prep do sh "librarian-puppet install --path=spec/fixtures/modules/"endtask :spec_prep => :librarian_spec_prep

task :default => [:clean, :spec]

Page 55: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Rakefile (cont.)

desc "Integration test with Vagrant"task :integration do sh %{vagrant destroy --force} failed = [] ["centos64", "debian6"].each do |vm| sh %{vagrant up #{vm}} do |ok| if ok sh %{vagrant destroy --force #{vm}} else failed << vm end end end fail("Machines failed to start: #{failed.join(', ')}")end

Page 56: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

.fixtures.yml

fixtures: symlinks: maven: "#{source_dir}"

Page 57: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Puppetfile

forge 'http://forge.puppetlabs.com'

mod 'maestrodev/wget', '>=1.0.0'

Page 58: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Integrating modules

Page 59: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 60: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks
Page 61: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

modules

PREVIEW

code

DEV

DEMO

EVAL

CLIENT

modulesmodulesmodules

codecodecode

manifests

Page 62: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Automate!

librarian-puppet to fetch modulesVagrant box

Integration testscucumber

junitselenium

...

Page 63: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Vagrant integration tests

Page 64: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Use local Puppet files and modules

config.vm.share_folder "puppet", "/etc/puppet", ".", :create => true, :owner => "puppet", :group => "puppet"

Page 65: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Share logs

config.vm.share_folder "jenkins-logs", "/var/log/jenkins", "target/logs/jenkins", :create => true, :extra => "dmode=777,fmode=666"

Page 66: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Save downloaded files in host

config.vm.share_folder "repo2", "/var/lib/jenkins/.m2/repository",

File.expand_path("~/.m2/repository"), :extra => "dmode=777,fmode=666"

config.vm.share_folder "yum", "/var/cache/yum", File.expand_path("~/.maestro/yum"), :owner => "root", :group => "root

Page 67: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Provision

config.vm.provision :puppet do |puppet| puppet.manifests_path = "manifests" puppet.manifest_file = "site.pp" puppet.pp_path = "/etc/puppet" puppet.options = ["--verbose"] puppet.facter = {} end

Page 68: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Run!

vagrant destroy --force vagrant uprake integration

Page 69: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Forward looking

Page 70: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Auto update

Automatically update all the modules and tell me if it’s broken

bonus point: automatically edit the Gemfile, Puppetfile, Modulefile constraints

Page 71: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

[email protected]@apache.org@csanchez

Thanks!

http://csanchez.orghttp://maestrodev.com

Page 72: How to Develop Puppet Modules: From Source to the Forge With Zero Clicks

Photo Credits

Brick wall - Luis Argerichhttp://www.flickr.com/photos/lrargerich/4353397797/

Agile vs. Iterative flow - Christopher Littlehttp://en.wikipedia.org/wiki/File:Agile-vs-iterative-flow.jpg

DevOps - Rajiv.Panthttp://en.wikipedia.org/wiki/File:Devops.png

Pimientos de Padron - Howard Walfishhttp://www.flickr.com/photos/h-bomb/4868400647/

Compiling - XKCDhttp://xkcd.com/303/

Printer in 1568 - Meggs, Philip Bhttp://en.wikipedia.org/wiki/File:Printer_in_1568-ce.png

Relativity - M. C. Escherhttp://en.wikipedia.org/wiki/File:Escher%27s_Relativity.jpg

Teacher and class - Herald Posthttp://www.flickr.com/photos/heraldpost/5169295832/


Recommended