72
How to develop Puppet Modules From source to the Forge with zero clicks Carlos Sanchez @csanchez http://csanchez.org http://maestrodev.com

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

Embed Size (px)

DESCRIPTION

Puppet Modules are a great way to reuse code, share your development with other people and take advantage of the hundreds of modules already available in the community. But how to create, test and publish them as easily as possible? now that infrastructure is defined as code, we need to use development best practices to build, test, deploy and use Puppet modules themselves. Three steps for a fully automated process * Continuous Integration of Puppet Modules * Automatic release and upload to the Puppet Forge * Deploy to Puppet master Carlos Sanchez Architect, MaestroDev Carlos Sanchez is specialized in automation and quality of software development, QA and operations processes, from build tools and continuous integration to deployment automation, speaking on the subject in several conferences around the world. Involved in Open Source for over ten years, he is a member of the Apache Software Foundation amongst other open source groups, contributing to several projects, like Apache Maven. Currently works as Architect at MaestroDev, a company focusing on development and DevOps tools, from his home in Spain.

Citation preview

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/