├── tests ├── init.pp ├── certname.rb ├── server.pp ├── dashboard.pp ├── agent_cron.pp ├── agent_noapt.pp ├── passenger.pp ├── unicorn.pp ├── storedconfigs.pp ├── standalone.pp └── unicorn_with_puppetdb.pp ├── .rspec ├── spec ├── classes │ ├── coverage_spec.rb │ ├── agent_config_spec.rb │ ├── server_config_spec.rb │ ├── puppet_spec.rb │ ├── server_spec.rb │ └── agent_spec.rb ├── spec_helper.rb ├── acceptance │ ├── nodesets │ │ ├── centos-59-x64.yml │ │ ├── centos-64-x64.yml │ │ ├── default.yml │ │ ├── centos-65-x64.yml │ │ ├── debian-607-x64.yml │ │ ├── debian-73-x64.yml │ │ ├── sles-11sp1-x64.yml │ │ ├── debian-73-i386.yml │ │ ├── debian-70rc1-x64.yml │ │ ├── ubuntu-server-1404-x64.yml │ │ ├── ubuntu-server-1310-x64.yml │ │ ├── centos-64-x64-pe.yml │ │ ├── ubuntu-server-10044-x64.yml │ │ └── ubuntu-server-12042-x64.yml │ ├── unicorn_server_spec.rb │ ├── passenger_server_spec.rb │ ├── webrick_server_spec.rb │ └── agent_spec.rb └── spec_helper_acceptance.rb ├── .gitignore ├── Guardfile ├── templates ├── tagmail.conf.erb ├── reports │ ├── graphite.yaml.erb │ ├── irccat.yaml.erb │ └── xmpp.yaml.erb ├── vhost │ └── nginx │ │ ├── unicorn.conf.erb │ │ └── base.conf.erb ├── agent_service.erb ├── com.puppetlabs.puppet.plist.erb └── config.ru │ └── 99-run-3.0.erb ├── lib ├── facter │ ├── certname.rb │ ├── puppet_path.rb │ ├── puppet_semantic_version.rb │ └── confenv.rb └── puppet │ └── parser │ └── functions │ └── validate_environment.rb ├── manifests ├── package.pp ├── storeconfig │ ├── sqlite.pp │ ├── puppetdb.pp │ ├── postgresql.pp │ └── mysql.pp ├── agent │ ├── cron.pp │ ├── service.pp │ └── config.pp ├── server │ ├── standalone.pp │ ├── rack.pp │ ├── passenger.pp │ ├── unicorn.pp │ └── config.pp ├── package │ ├── repository.pp │ └── gentoo.pp ├── reports │ ├── graphite.pp │ ├── irccat.pp │ └── xmpp.pp ├── config.pp ├── init.pp ├── storeconfig.pp ├── agent.pp ├── params.pp └── server.pp ├── CHANGELOG ├── Gemfile ├── .fixtures.yml ├── files ├── unicorn.conf └── reports │ ├── graphite.rb │ ├── xmpp.rb │ └── irccat.rb ├── .travis.yml ├── Rakefile ├── metadata.json ├── Vagrantfile ├── CONTRIBUTING.md └── README.md /tests/init.pp: -------------------------------------------------------------------------------- 1 | include puppet 2 | -------------------------------------------------------------------------------- /tests/certname.rb: -------------------------------------------------------------------------------- 1 | notify{$certname:} 2 | -------------------------------------------------------------------------------- /tests/server.pp: -------------------------------------------------------------------------------- 1 | include puppet::server 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /tests/dashboard.pp: -------------------------------------------------------------------------------- 1 | include puppet::dashboard 2 | -------------------------------------------------------------------------------- /spec/classes/coverage_spec.rb: -------------------------------------------------------------------------------- 1 | at_exit { RSpec::Puppet::Coverage.report! } 2 | -------------------------------------------------------------------------------- /tests/agent_cron.pp: -------------------------------------------------------------------------------- 1 | class { 'puppet::agent': 2 | method => 'cron', 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw? 2 | pkg 3 | spec/fixtures 4 | .rspec_system 5 | .vagrant 6 | log 7 | Gemfile.lock 8 | -------------------------------------------------------------------------------- /tests/agent_noapt.pp: -------------------------------------------------------------------------------- 1 | class { 'puppet::agent': 2 | method => 'cron', 3 | manage_repos => false, 4 | } 5 | -------------------------------------------------------------------------------- /tests/passenger.pp: -------------------------------------------------------------------------------- 1 | class { 'puppet::server': 2 | servertype => 'passenger', 3 | ca => true, 4 | } 5 | 6 | -------------------------------------------------------------------------------- /tests/unicorn.pp: -------------------------------------------------------------------------------- 1 | class { 'puppet::server': 2 | servertype => 'unicorn', 3 | ca => true, 4 | } 5 | 6 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | notification :off 2 | 3 | guard 'rake', :task => 'test' do 4 | watch(%r{^manifests\/(.+)\.pp$}) 5 | end 6 | -------------------------------------------------------------------------------- /templates/tagmail.conf.erb: -------------------------------------------------------------------------------- 1 | <% @tagmail.each_pair do |tags,destinations| -%> 2 | <%= tags %>: <%= destinations %> 3 | <% end -%> 4 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'puppet_spec_facts' 2 | include PuppetSpecFacts 3 | require 'puppetlabs_spec_helper/module_spec_helper' 4 | -------------------------------------------------------------------------------- /lib/facter/certname.rb: -------------------------------------------------------------------------------- 1 | Facter.add("certname") do 2 | setcode do 3 | require 'puppet' 4 | Puppet[:certname] 5 | end 6 | end 7 | 8 | -------------------------------------------------------------------------------- /templates/reports/graphite.yaml.erb: -------------------------------------------------------------------------------- 1 | --- 2 | :graphite_server: <%= @server %> 3 | :graphite_port: <%= @port %> 4 | :graphite_prefix: <%= @prefix %> 5 | -------------------------------------------------------------------------------- /tests/storedconfigs.pp: -------------------------------------------------------------------------------- 1 | $mysql_root_pw='password' 2 | $rails_version = '2.3.5' 3 | $puppet_db_user='puppet' 4 | $puppet_db_password='password' 5 | include puppet::storedconfigs 6 | -------------------------------------------------------------------------------- /templates/reports/irccat.yaml.erb: -------------------------------------------------------------------------------- 1 | --- 2 | :irccathost: <%= @host %> 3 | :githuburl: <%= @githuburl %> 4 | :dashboard: <%= @dashboard %> 5 | <% if @ignore_hosts -%> 6 | :ignore_hosts: [<%= @ignore_hosts.join(', ') %>] 7 | <% end %> 8 | -------------------------------------------------------------------------------- /templates/reports/xmpp.yaml.erb: -------------------------------------------------------------------------------- 1 | --- 2 | :xmpp_jid: <%= @jid %> 3 | :xmpp_password: <%= @password %> 4 | :xmpp_target: [<%= @target.join(', ') %>] 5 | :dashboard: <%= @dashboard %> 6 | :ignore_hosts: [<%= @ignore_hosts.join(', ') %>] 7 | -------------------------------------------------------------------------------- /templates/vhost/nginx/unicorn.conf.erb: -------------------------------------------------------------------------------- 1 | # nginx configuration for a puppetmaster running in unicorn 2 | 3 | upstream puppetmaster_upstream { 4 | server unix:/var/run/puppet/puppetmaster_unicorn.sock max_fails=0 fail_timeout=0s; 5 | } 6 | 7 | <%# YO DAWG %> 8 | <%= scope.function_template(['puppet/vhost/nginx/base.conf.erb']) %> 9 | -------------------------------------------------------------------------------- /spec/classes/agent_config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | openbsdfacts = {:operatingsystem => 'OpenBSD', :kernel => 'OpenBSD'} 4 | 5 | describe "puppet::agent::service" do 6 | context "on OpenBSD" do 7 | let(:facts) { openbsdfacts } 8 | 9 | it { should_not contain_file('puppet_agent_service_conf') } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/facter/puppet_path.rb: -------------------------------------------------------------------------------- 1 | Facter.add(:puppet_path) do 2 | setcode do 3 | puppet_paths = [ 4 | '/usr/bin/puppet', 5 | '/usr/local/bin/puppet', 6 | '/opt/puppet/bin/puppet', 7 | '/opt/csw/bin/puppet' 8 | ] 9 | 10 | puppet_paths.find { |puppet_path| File.executable_real? puppet_path } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-59-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-59-x64: 3 | roles: 4 | - master 5 | platform: el-5-x86_64 6 | box : centos-59-x64-vbox4210-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-64-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-64-x64: 3 | roles: 4 | - master 5 | platform: el-6-x86_64 6 | box : centos-64-x64-vbox4210-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/default.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-73-x64: 3 | roles: 4 | - master 5 | platform: debian-7-amd64 6 | box : debian-73-x64-virtualbox-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-73-x64-virtualbox-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-65-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-65-x64: 3 | roles: 4 | - master 5 | platform: el-6-x86_64 6 | box : centos-65-x64-virtualbox-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-65-x64-virtualbox-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-607-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-607-x64: 3 | roles: 4 | - master 5 | platform: debian-6-amd64 6 | box : debian-607-x64-vbox4210-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-607-x64-vbox4210-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-73-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-73-x64: 3 | roles: 4 | - master 5 | platform: debian-7-amd64 6 | box : debian-73-x64-virtualbox-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-73-x64-virtualbox-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/sles-11sp1-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | sles-11sp1-x64: 3 | roles: 4 | - master 5 | platform: sles-11-x86_64 6 | box : sles-11sp1-x64-vbox4210-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-73-i386.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-73-i386: 3 | roles: 4 | - master 5 | platform: debian-7-i386 6 | box : debian-73-i386-virtualbox-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-73-i386-virtualbox-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-70rc1-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-70rc1-x64: 3 | roles: 4 | - master 5 | platform: debian-7-amd64 6 | box : debian-70rc1-x64-vbox4210-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-server-1404-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-server-1404-x64: 3 | roles: 4 | - master 5 | platform: ubuntu-14.04-amd64 6 | box : puppetlabs/ubuntu-14.04-64-nocm 7 | box_url : https://vagrantcloud.com/puppetlabs/ubuntu-14.04-64-nocm 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level : debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-server-1310-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-server-1310-x64: 3 | roles: 4 | - master 5 | platform: ubuntu-13.10-amd64 6 | box : ubuntu-server-1310-x64-vbox4210-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-1310-x64-virtualbox-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level : debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-64-x64-pe.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-64-x64: 3 | roles: 4 | - master 5 | - database 6 | - dashboard 7 | platform: el-6-x86_64 8 | box : centos-64-x64-vbox4210-nocm 9 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box 10 | hypervisor : vagrant 11 | CONFIG: 12 | log_level: debug 13 | type: pe 14 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-server-10044-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-server-10044-x64: 3 | roles: 4 | - master 5 | platform: ubuntu-10.04-amd64 6 | box : ubuntu-server-10044-x64-vbox4210-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu-server-12042-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-server-12042-x64: 3 | roles: 4 | - master 5 | platform: ubuntu-12.04-amd64 6 | box : ubuntu-server-12042-x64-vbox4210-nocm 7 | box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box 8 | hypervisor : vagrant 9 | CONFIG: 10 | log_level: debug 11 | type: git 12 | -------------------------------------------------------------------------------- /spec/classes/server_config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | openbsdfacts = {:operatingsystem => 'OpenBSD'} 4 | 5 | describe "puppet::server::config" do 6 | context "on OpenBSD" do 7 | let(:facts) { openbsdfacts } 8 | 9 | it { should contain_ini_setting('group').with_value('_puppet') } 10 | it { should contain_ini_setting('user').with_value('_puppet') } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /manifests/package.pp: -------------------------------------------------------------------------------- 1 | # Private class 2 | class puppet::package { 3 | 4 | if $puppet::agent::manage_repos { 5 | include puppet::package::repository 6 | } 7 | 8 | if $::operatingsystem == 'gentoo' { 9 | include puppet::package::gentoo 10 | } 11 | 12 | package { $puppet::agent::package: 13 | ensure => $puppet::agent::ensure, 14 | notify => Service['puppet_agent'], 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | 0.11.0 5 | ------ 6 | 7 | 2013-02-01 8 | 9 | * Remove use of dynamic scoping from passenger code 10 | * Add support for ploperations/unicorn 1.0.0 11 | 12 | 0.10.0 13 | ------ 14 | 15 | 2013-01-25 16 | 17 | * support for ENC configuration in puppet.conf 18 | * Updated module dependencies 19 | * `interval` function removed 20 | * variable support for Gentoo 21 | * 'puppet::deploy' deprecated 22 | * misc. cleanup and refactors 23 | -------------------------------------------------------------------------------- /tests/standalone.pp: -------------------------------------------------------------------------------- 1 | class { 'puppet::server': 2 | directoryenvs => true, 3 | basemodulepath => '$confdir/modules:$confdir/secure', 4 | environmentpath => '$confdir/environments', 5 | default_manifest => 'site/site.pp', 6 | manage_puppetdb => true, 7 | reporturl => "https://${::fqdn}/reports", 8 | servertype => 'standalone', 9 | ca => true, 10 | reports => [ 11 | 'https', 12 | 'store', 13 | 'puppetdb', 14 | ], 15 | } 16 | -------------------------------------------------------------------------------- /lib/puppet/parser/functions/validate_environment.rb: -------------------------------------------------------------------------------- 1 | Puppet::Parser::Functions.newfunction(:validate_environment) do 2 | 3 | confenv = scope.lookupvar('::confenv') 4 | modulepath = scope.lookupvar('::settings::modulepath') 5 | server = scope.lookupvar('::server') 6 | 7 | modulepath.split(':').each do |path| 8 | unless File.directory? path 9 | function_fail(["Invalid environment #{confenv} on server #{server}: module path #{path} does not exist."]) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | group :test do 4 | gem "rake" 5 | gem "puppet", ENV['PUPPET_VERSION'] || '~> 3.8.7' 6 | gem "puppet-lint" 7 | gem "rspec-puppet" 8 | gem "puppet-syntax" 9 | gem "puppetlabs_spec_helper" 10 | gem "metadata-json-lint" 11 | gem "puppet_spec_facts", '>= 0.2.1' 12 | end 13 | 14 | group :development do 15 | gem "travis" 16 | gem "beaker" 17 | gem "beaker-rspec" 18 | gem "vagrant-wrapper" 19 | gem "puppet-blacksmith" 20 | gem "guard-rake" 21 | end 22 | -------------------------------------------------------------------------------- /manifests/storeconfig/sqlite.pp: -------------------------------------------------------------------------------- 1 | class puppet::storeconfig::sqlite { 2 | 3 | include puppet::params 4 | 5 | Ini_setting { 6 | ensure => 'present', 7 | section => 'master', 8 | path => $puppet::params::puppet_conf, 9 | } 10 | 11 | ini_setting { 12 | 'dbadapter': 13 | setting => 'dbadapter', 14 | value => 'sqlite3'; 15 | 'dbmigrate': 16 | setting => 'dbmigrate', 17 | value => true; 18 | } 19 | 20 | # --- 21 | # Seems like this should install a gem or something useful 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lib/facter/puppet_semantic_version.rb: -------------------------------------------------------------------------------- 1 | semantic_version_values = [ 2 | :puppet_major_version, 3 | :puppet_minor_version, 4 | :puppet_patch_version 5 | ] 6 | 7 | semantic_version_values.each_index do |index| 8 | fact_name = semantic_version_values[index] 9 | Facter.add(fact_name) do 10 | setcode do 11 | if Facter.value(:puppetversion) =~ / / 12 | Integer(Facter.value(:puppetversion).split(' ').first.split('.')[index]) 13 | else 14 | Integer(Facter.value(:puppetversion).split('.')[index]) 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /tests/unicorn_with_puppetdb.pp: -------------------------------------------------------------------------------- 1 | class { '::puppet::server': 2 | modulepath => [ 3 | '$confdir/modules', 4 | '$confdir/environments/$environment/modules/site', 5 | '$confdir/environments/$environment/modules/site/dist', 6 | '$confdir/environments/$environment/modules/site/dist', 7 | ], 8 | manage_puppetdb => true, 9 | reporturl => "https://${::fqdn}/reports", 10 | servertype => 'unicorn', 11 | manifest => '$confdir/environments/$environment/site.pp', 12 | ca => true, 13 | reports => [ 14 | 'https', 15 | 'store', 16 | 'puppetdb', 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /.fixtures.yml: -------------------------------------------------------------------------------- 1 | fixtures: 2 | forge_modules: 3 | stdlib: "puppetlabs/stdlib" 4 | concat: "puppetlabs/concat" 5 | ruby: "puppetlabs/ruby" 6 | apt: 7 | repo: "puppetlabs/apt" 8 | ref: "1.8.0" 9 | puppetlabs_yum: "stahnma/puppetlabs_yum" 10 | puppetlabs_apt: "ploperations/puppetlabs_apt" 11 | mysql: "puppetlabs/mysql" 12 | unicorn: "ploperations/unicorn" 13 | rack: "ploperations/rack" 14 | bundler: "ploperations/bundler" 15 | nginx: "jfryman/nginx" 16 | inifile: "puppetlabs/inifile" 17 | apache: "puppetlabs/apache" 18 | portage: "gentoo/portage" 19 | puppetdb: "puppetlabs/puppetdb" 20 | symlinks: 21 | puppet: "#{source_dir}" 22 | -------------------------------------------------------------------------------- /manifests/agent/cron.pp: -------------------------------------------------------------------------------- 1 | # Private class 2 | class puppet::agent::cron ( 3 | $enable = true, 4 | $run_noop = false, 5 | $minute = fqdn_rand(60), 6 | ) { 7 | include puppet::params 8 | 9 | if $enable { 10 | $ensure = present 11 | } else { 12 | $ensure = absent 13 | } 14 | 15 | if $run_noop { 16 | $cmd = "${puppet::params::puppet_cmd} agent --confdir ${puppet::params::puppet_confdir} --onetime --no-daemonize --noop >/dev/null" 17 | } else { 18 | $cmd = "${puppet::params::puppet_cmd} agent --confdir ${puppet::params::puppet_confdir} --onetime --no-daemonize >/dev/null" 19 | } 20 | 21 | cron { 'puppet agent': 22 | ensure => $ensure, 23 | command => $cmd, 24 | minute => $minute, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /templates/agent_service.erb: -------------------------------------------------------------------------------- 1 | <% if @osfamily == 'Debian' -%> 2 | # Only used on debian. Defaults for puppet - sourced by /etc/init.d/puppet 3 | 4 | # Start puppet on boot? 5 | <% if @enable -%> 6 | START=yes 7 | <% else -%> 8 | START=no 9 | <% end -%> 10 | <% elsif @osfamily == 'RedHat' or @osfamily == 'Suse' -%> 11 | # Startup options 12 | DAEMON_OPTS="" 13 | # The puppetmaster server 14 | #PUPPET_SERVER=puppet 15 | 16 | # If you wish to specify the port to connect to do so here 17 | #PUPPET_PORT=8140 18 | 19 | # Where to log to. Specify syslog to send log messages to the system log. 20 | #PUPPET_LOG=/var/log/puppet/puppet.log 21 | 22 | # You may specify other parameters to the puppet client here 23 | #PUPPET_EXTRA_OPTS=--waitforcert=500 24 | <% end -%> 25 | -------------------------------------------------------------------------------- /manifests/server/standalone.pp: -------------------------------------------------------------------------------- 1 | class puppet::server::standalone ( 2 | $enabled = true 3 | ) { 4 | 5 | include puppet 6 | include puppet::server 7 | 8 | $service_ensure = $enabled? { 9 | true => running, 10 | default => stopped, 11 | } 12 | 13 | service { $puppet::params::master_service: 14 | ensure => $service_ensure, 15 | enable => $enabled, 16 | hasstatus => true, 17 | require => Class['puppet::server::config']; 18 | } 19 | 20 | if ! $enabled and $::lsbdistid == 'Ubuntu' { 21 | file_line { '/etc/default/puppetmaster START': 22 | path => '/etc/default/puppetmaster', 23 | line => 'START=no', 24 | match => '^START=', 25 | require => Package[$puppet::params::master_package], 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spec/acceptance/unicorn_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'server', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do 4 | context 'running on unicorn', :servertype => 'unicorn', :webserver => 'nginx' do 5 | it 'should run with no errors' do 6 | pp = <<-EOS 7 | class { 'puppet::server': 8 | servertype => 'unicorn', 9 | ca => true, 10 | } 11 | EOS 12 | 13 | # Run it twice and test for idempotency 14 | apply_manifest(pp, :catch_failures => true) 15 | expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero 16 | end 17 | 18 | it_behaves_like "basic working puppetmaster" 19 | it_behaves_like "nginx-based webserver" 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /manifests/package/repository.pp: -------------------------------------------------------------------------------- 1 | # == Class: puppet::package::repository 2 | # 3 | # Add Puppet Labs package repositories 4 | # 5 | # == Parameters 6 | # 7 | # [*devel*] 8 | # Include development repositories for bleeding edge releases. 9 | # Default: false 10 | # 11 | # == Requirements 12 | # 13 | # If used on apt based distributions, this requires the puppetlabs/apt module. 14 | # If used on yum based distributions, this requires the puppetlabs/yum module. 15 | # 16 | class puppet::package::repository($devel = false) { 17 | 18 | case $::osfamily { 19 | 'Redhat': { $repo_class = 'puppetlabs_yum' } 20 | 'Debian': { $repo_class = 'puppetlabs_apt' } 21 | default: { fail("Puppetlabs does not offer a package repository for ${::osfamily}") } 22 | } 23 | 24 | class { $repo_class: 25 | enable_devel => $devel, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /files/unicorn.conf: -------------------------------------------------------------------------------- 1 | # /etc/puppet/unicorn.con 2 | # This is the configuration for running puppet with unicorn. 3 | # See original here: http://projects.puppetlabs.com/projects/1/wiki/Using_Unicorn 4 | 5 | worker_processes 4 6 | working_directory "/etc/puppet" 7 | listen '/var/run/puppet/puppetmaster_unicorn.sock', :backlog => 512 8 | timeout 120 9 | pid "/var/run/puppet/puppetmaster_unicorn.pid" 10 | 11 | preload_app true 12 | if GC.respond_to?(:copy_on_write_friendly=) 13 | GC.copy_on_write_friendly = true 14 | end 15 | 16 | before_fork do |server, worker| 17 | old_pid = "#{server.config[:pid]}.oldbin" 18 | if File.exists?(old_pid) && server.pid != old_pid 19 | begin 20 | Process.kill("QUIT", File.read(old_pid).to_i) 21 | rescue Errno::ENOENT, Errno::ESRCH 22 | # someone else did our job for us 23 | end 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | bundler_args: --without development 4 | before_install: rm Gemfile.lock || true 5 | rvm: 6 | - 2.0.0 7 | - 2.1.6 8 | script: bundle exec rake test 9 | env: 10 | - PUPPET_VERSION="~> 3.1.0" 11 | - PUPPET_VERSION="~> 3.2.0" 12 | - PUPPET_VERSION="~> 3.3.0" 13 | - PUPPET_VERSION="~> 3.4.0" 14 | - PUPPET_VERSION="~> 3.7.5" 15 | - PUPPET_VERSION="~> 3.8.7" 16 | - PUPPET_VERSION="~> 3.8.7" FUTURE_PARSER=yes 17 | - PUPPET_VERSION="~> 3.8.7" STRINGIFY_FACTS=no 18 | - PUPPET_VERSION="~> 3.8.7" STRICT_VARIABLES=yes 19 | - PUPPET_VERSION="~> 3.8.7" ORDERING=random 20 | matrix: 21 | exclude: 22 | - rvm: 2.0.0 23 | env: PUPPET_VERSION="~> 3.1.0" 24 | - rvm: 2.1.6 25 | env: PUPPET_VERSION="~> 3.1.0" 26 | - rvm: 2.1.6 27 | env: PUPPET_VERSION="~> 3.2.0" 28 | - rvm: 2.1.6 29 | env: PUPPET_VERSION="~> 3.3.0" 30 | allow_failures: 31 | - env: PUPPET_VERSION="~> 3.8.7" STRICT_VARIABLES=yes 32 | -------------------------------------------------------------------------------- /manifests/reports/graphite.pp: -------------------------------------------------------------------------------- 1 | # A report plugin to send metrics to graphite 2 | # From https://github.com/nareshov/puppet-graphite 3 | # 4 | class puppet::reports::graphite($server, $port, $prefix) { 5 | 6 | # This is a little bit dirty, as it just throws it straight in the 7 | # rubylib, but it's better than messing with libdir on the master. 8 | # See https://projects.puppetlabs.com/issues/4345 for mild 9 | # discussion. 10 | file{ 11 | "/${puppet::server::report_dir}/graphite.rb": 12 | ensure => present, 13 | owner => 'root', 14 | group => 'root', 15 | mode => '0644', 16 | source => 'puppet:///modules/puppet/reports/graphite.rb'; 17 | '/etc/puppet/graphite.yaml': 18 | ensure => present, 19 | owner => 'root', 20 | group => 'puppet', 21 | mode => '0440', 22 | content => template('puppet/reports/graphite.yaml.erb'), 23 | } 24 | 25 | # We could, at this, tell Puppet we need to bounce it, but I think 26 | # would be bold. 27 | } 28 | -------------------------------------------------------------------------------- /lib/facter/confenv.rb: -------------------------------------------------------------------------------- 1 | Facter.add("confenv") do 2 | setcode do 3 | env = nil 4 | 5 | if path = Facter.value(:puppet_path) 6 | # If we're looking at PE, then this will have a version string like this: 7 | # 8 | # 2.7.12 (Puppet Enterprise 2.5.1) 9 | # 10 | # We scan for things looking like semantic version strings then grab the first one. 11 | version = "#{Facter.value("puppet_major_version")}.#{Facter.value("puppet_minor_version")}" 12 | case version 13 | when "3.1" 14 | cmd = %{#{path} agent --configprint environment} 15 | when "3.0" 16 | cmd = %{#{path} config print environment --run_mode agent} 17 | when "2.7" 18 | cmd = %{#{path} config print environment --mode agent} 19 | when "2.6" 20 | cmd = %{#{path} agent --configprint environment} 21 | end 22 | 23 | if cmd and (output = Facter::Util::Resolution.exec(cmd)) 24 | env = output.chomp 25 | end 26 | end 27 | 28 | env 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /manifests/package/gentoo.pp: -------------------------------------------------------------------------------- 1 | # == Class: puppet::package::gentoo 2 | # 3 | # Additional configuration needed for Gentoo. This class is private. Its 4 | # parameters should be set in hieradata 5 | # 6 | # == Parameters 7 | # 8 | # [*keywords*] 9 | # ACCEPT_KEYWORDS for the puppet package 10 | # 11 | # [*use*] 12 | # USE flags for the puppet package 13 | # 14 | class puppet::package::gentoo ( 15 | $keywords, 16 | $use, 17 | ) { 18 | 19 | package_use { 'sys-apps/net-tools': 20 | use => 'old-output', 21 | target => 'puppet', 22 | before => Package[$puppet::agent::package], 23 | } 24 | 25 | if $keywords { 26 | package_keywords { $puppet::agent::package: 27 | keywords => $keywords, 28 | target => 'puppet', 29 | before => Package[$puppet::agent::package], 30 | } 31 | } 32 | 33 | if $use { 34 | package_use { $puppet::agent::package: 35 | use => $use, 36 | target => 'puppet', 37 | before => Package[$puppet::agent::package], 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /templates/com.puppetlabs.puppet.plist.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.puppetlabs.puppet 7 | OnDemand 8 | 9 | ProgramArguments 10 | 11 | /usr/bin/puppet 12 | agent 13 | --no-daemonize 14 | --logdest 15 | syslog 16 | --color 17 | false 18 | 19 | RunAtLoad 20 | 21 | ServiceDescription 22 | Puppet agent service 23 | ServiceIPC 24 | 25 | StandardErrorPath 26 | /var/log/puppet/puppet.err 27 | StandardOutPath 28 | /var/log/puppet/puppet.out 29 | 30 | 31 | -------------------------------------------------------------------------------- /manifests/server/rack.pp: -------------------------------------------------------------------------------- 1 | # == Class: puppet::server::rack 2 | # 3 | # This class creates the config.ru filr that is necessary for rack based 4 | # application servers. 5 | # 6 | # Application server classes that depend on this config.ru should include this 7 | # class. 8 | # 9 | class puppet::server::rack { 10 | 11 | include puppet 12 | 13 | file { [ 14 | "${puppet::confdir}/rack", 15 | "${puppet::confdir}/rack/public/", 16 | "${puppet::confdir}/rack/tmp" 17 | ]: 18 | ensure => directory, 19 | owner => $puppet::user, 20 | group => $puppet::group, 21 | } 22 | 23 | # Template variables for concat fragment 24 | $puppet_confdir = $puppet::confdir 25 | $puppet_vardir = $puppet::vardir 26 | 27 | concat { "${puppet::confdir}/config.ru": 28 | owner => 'puppet', 29 | group => 'puppet', 30 | mode => '0644', 31 | } 32 | 33 | concat::fragment { 'run-puppet-master': 34 | order => '99', 35 | target => "${puppet::confdir}/config.ru", 36 | content => template('puppet/config.ru/99-run-3.0.erb'), 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /manifests/reports/irccat.pp: -------------------------------------------------------------------------------- 1 | # A report plugin to send irccat to IRC. 2 | # 3 | class puppet::reports::irccat($host, $githuburl, $dashboard, $ignore_hosts = []) { 4 | 5 | Package{ provider => 'gem', ensure => present } 6 | 7 | package{ 8 | 'json':; 9 | 'httparty':; 10 | } 11 | 12 | # This is a little bit dirty, as it just throws it straight in the 13 | # rubylib, but it's better than messing with libdir on the master. 14 | # See https://projects.puppetlabs.com/issues/4345 for mild 15 | # discussion. 16 | file{ 17 | "${puppet::server::report_dir}/irccat.rb": 18 | ensure => present, 19 | owner => 'root', 20 | group => 'root', 21 | mode => '0644', 22 | source => 'puppet:///modules/puppet/reports/irccat.rb', 23 | notify => Class['puppet::server']; 24 | '/etc/puppet/irccat.yaml': 25 | ensure => present, 26 | owner => 'root', 27 | group => 'puppet', 28 | mode => '0440', 29 | content => template('puppet/reports/irccat.yaml.erb'), 30 | notify => Class['puppet::server']; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /manifests/storeconfig/puppetdb.pp: -------------------------------------------------------------------------------- 1 | # Install the puppetdb terminus. Puppetdb configuration should occur elsewhere. 2 | class puppet::storeconfig::puppetdb( 3 | $server = 'localhost', 4 | $port = '8081', 5 | ) { 6 | include puppet::params 7 | 8 | # --- 9 | # PupeptDB backend settings 10 | Ini_setting { 11 | notify => Class['puppet::server'], 12 | } 13 | 14 | ini_setting { 'storeconfigs_backend': 15 | ensure => 'present', 16 | path => $puppet::params::puppet_conf, 17 | section => 'master', 18 | setting => 'storeconfigs_backend', 19 | value => 'puppetdb', 20 | } 21 | 22 | ini_setting { 'puppetdb_conf_server': 23 | ensure => 'present', 24 | path => "${::puppet::params::puppet_confdir}/puppetdb.conf", 25 | section => 'main', 26 | setting => 'server', 27 | value => $server, 28 | } 29 | 30 | ini_setting { 'puppetdb_conf_port': 31 | ensure => 'present', 32 | path => "${::puppet::params::puppet_confdir}/puppetdb.conf", 33 | section => 'main', 34 | setting => 'port', 35 | value => $port, 36 | } 37 | 38 | package { 'puppetdb-terminus': 39 | ensure => present, 40 | notify => Class['puppet::server'], 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /manifests/reports/xmpp.pp: -------------------------------------------------------------------------------- 1 | # A report plugin to send XMPP to people when nodes fail. 2 | # See http://www.kartar.net/2011/06/puppet-xmpp/ and 3 | # https://github.com/barn/puppet-xmpp 4 | # 5 | class puppet::reports::xmpp($jid, $password, $dashboard, $target = [], $ignore_hosts = []) { 6 | 7 | Package{ provider => 'gem', ensure => present } 8 | 9 | package{ 10 | 'xmpp4r':; 11 | 'json':; 12 | 'httparty':; 13 | } 14 | 15 | # This is a little bit dirty, as it just throws it straight in the 16 | # rubylib, but it's better than messing with libdir on the master. 17 | # See https://projects.puppetlabs.com/issues/4345 for mild 18 | # discussion. 19 | file{ 20 | "${puppet::server::report_dir}/xmpp.rb": 21 | ensure => present, 22 | owner => 'root', 23 | group => 'root', 24 | mode => '0644', 25 | source => 'puppet:///modules/puppet/reports/xmpp.rb', 26 | notify => Class['puppet::server']; 27 | '/etc/puppet/xmpp.yaml': 28 | ensure => present, 29 | owner => 'root', 30 | group => 'puppet', 31 | mode => '0440', 32 | source => 'puppet:///modules/puppet/reports/xmpp.yaml', 33 | notify => Class['puppet::server']; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /spec/acceptance/passenger_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'passenger server', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do 4 | context 'running on passenger', :servertype => 'passenger', :webserver => 'apache' do 5 | it 'should run with no errors' do 6 | pp = <<-EOS 7 | class { 'puppet::server': 8 | servertype => 'passenger', 9 | ca => true, 10 | servername => $::hostname, 11 | } 12 | EOS 13 | 14 | # Run it twice and test for idempotency 15 | apply_manifest(pp, :catch_failures => true) 16 | expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero 17 | end 18 | it_behaves_like 'basic working puppetmaster' 19 | 20 | # sanity checks to ensure the passenger setup doesn't bring in other services 21 | describe service('nginx') do 22 | it { should_not be_enabled } 23 | it { should_not be_running } 24 | end 25 | describe service('puppetmaster') do 26 | it { should_not be_enabled } 27 | it { should_not be_running } 28 | end 29 | describe service('puppetserver') do 30 | it { should_not be_enabled } 31 | it { should_not be_running } 32 | end 33 | 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /manifests/config.pp: -------------------------------------------------------------------------------- 1 | # Private class 2 | class puppet::config { 3 | 4 | include puppet::agent 5 | 6 | Ini_setting { 7 | path => $puppet::conf, 8 | ensure => 'present', 9 | section => 'main', 10 | notify => Service['puppet_agent'], 11 | } 12 | 13 | ini_setting { 'logdir': 14 | setting => 'logdir', 15 | value => $puppet::logdir, 16 | } 17 | 18 | ini_setting { 'vardir': 19 | setting => 'vardir', 20 | value => $puppet::vardir, 21 | } 22 | 23 | ini_setting { 'ssldir': 24 | setting => 'ssldir', 25 | value => $puppet::ssldir, 26 | } 27 | 28 | ini_setting { 'rundir': 29 | setting => 'rundir', 30 | value => $puppet::rundir, 31 | } 32 | 33 | $srv_ensure = $puppet::use_srv_records ? { 34 | true => 'present', 35 | false => 'absent', 36 | } 37 | 38 | ini_setting { 'use_srv_records': 39 | ensure => $srv_ensure, 40 | setting => 'use_srv_records', 41 | value => $puppet::use_srv_records, 42 | } 43 | 44 | ini_setting { 'srv_domain': 45 | ensure => $srv_ensure, 46 | setting => 'srv_domain', 47 | value => $puppet::srv_domain, 48 | } 49 | 50 | ini_setting { 'stringify_facts': 51 | setting => 'stringify_facts', 52 | value => $puppet::stringify_facts, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'puppetlabs_spec_helper/rake_tasks' 2 | require 'puppet-lint/tasks/puppet-lint' 3 | require 'puppet-syntax/tasks/puppet-syntax' 4 | 5 | # These two gems aren't always present, for instance 6 | # on Travis with --without development 7 | begin 8 | require 'puppet_blacksmith/rake_tasks' 9 | rescue LoadError 10 | end 11 | 12 | PuppetLint.configuration.send("disable_80chars") 13 | PuppetLint.configuration.log_format = "%{path}:%{linenumber}:%{check}:%{KIND}:%{message}" 14 | PuppetLint.configuration.fail_on_warnings = false 15 | 16 | # Forsake support for Puppet 2.6.2 for the benefit of cleaner code. 17 | # http://puppet-lint.com/checks/class_parameter_defaults/ 18 | PuppetLint.configuration.send('disable_class_parameter_defaults') 19 | # http://puppet-lint.com/checks/class_inherits_from_params_class/ 20 | PuppetLint.configuration.send('disable_class_inherits_from_params_class') 21 | 22 | exclude_paths = [ 23 | "pkg/**/*", 24 | "vendor/**/*", 25 | "spec/**/*", 26 | ] 27 | PuppetLint.configuration.ignore_paths = exclude_paths 28 | PuppetSyntax.exclude_paths = exclude_paths 29 | 30 | desc "Run acceptance tests" 31 | RSpec::Core::RakeTask.new(:acceptance) do |t| 32 | t.pattern = 'spec/acceptance' 33 | end 34 | 35 | desc "Run syntax, lint, and spec tests." 36 | task :test => [ 37 | :syntax, 38 | :lint, 39 | :spec, 40 | :metadata, 41 | ] 42 | -------------------------------------------------------------------------------- /files/reports/graphite.rb: -------------------------------------------------------------------------------- 1 | require 'puppet' 2 | require 'yaml' 3 | require 'socket' 4 | require 'time' 5 | 6 | Puppet::Reports.register_report(:graphite) do 7 | 8 | configfile = File.join([File.dirname(Puppet.settings[:config]), "graphite.yaml"]) 9 | raise(Puppet::ParseError, "Graphite report config file #{configfile} not readable") unless File.exist?(configfile) 10 | config = YAML.load_file(configfile) 11 | GRAPHITE_SERVER = config[:graphite_server] 12 | GRAPHITE_PORT = config[:graphite_port] 13 | GRAPHITE_PREFIX = config[:graphite_prefix] 14 | 15 | desc <<-DESC 16 | Send notification of failed reports to a Graphite server via socket. 17 | DESC 18 | 19 | def send_metric payload 20 | socket = TCPSocket.new(GRAPHITE_SERVER, GRAPHITE_PORT) 21 | socket.puts payload 22 | socket.close 23 | end 24 | 25 | def process 26 | Puppet.debug "Sending status for #{self.host} to Graphite server at #{GRAPHITE_SERVER}" 27 | prefix = GRAPHITE_PREFIX + '.' + self.host.split(".").reverse.join(".") 28 | epochtime = Time.now.utc.to_i 29 | payload = self.metrics.map { |metric,data| 30 | data.values.map { |val| 31 | name = "#{prefix}.puppet.#{val[0]}_#{metric}" 32 | value = val[2] 33 | 34 | "#{name} #{value} #{epochtime}" 35 | } 36 | } 37 | 38 | send_metric payload.flatten.join("\n") 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /templates/config.ru/99-run-3.0.erb: -------------------------------------------------------------------------------- 1 | # a config.ru, for use with every rack-compatible webserver. 2 | # SSL needs to be handled outside this, though. 3 | 4 | # if puppet is not in your RUBYLIB: 5 | # $LOAD_PATH.unshift('/opt/puppet/lib') 6 | 7 | $0 = "master" 8 | 9 | # if you want debugging: 10 | # ARGV << "--debug" 11 | 12 | ARGV << "--rack" 13 | ARGV << "--color" << "false" 14 | 15 | # Rack applications typically don't start as root. Set --confdir to prevent 16 | # reading configuration from ~/.puppet/puppet.conf 17 | ARGV << "--confdir" << "<%= @puppet_confdir %>" 18 | ARGV << "--vardir" << "<%= @puppet_vardir %>" 19 | 20 | # NOTE: it's unfortunate that we have to use the "CommandLine" class 21 | # here to launch the app, but it contains some initialization logic 22 | # (such as triggering the parsing of the config file) that is very 23 | # important. We should do something less nasty here when we've 24 | # gotten our API and settings initialization logic cleaned up. 25 | # 26 | # Also note that the "$0 = master" line up near the top here is 27 | # the magic that allows the CommandLine class to know that it's 28 | # supposed to be running master. 29 | # 30 | # --cprice 2012-05-22 31 | 32 | require 'puppet/util/command_line' 33 | # we're usually running inside a Rack::Builder.new {} block, 34 | # therefore we need to call run *here*. 35 | run Puppet::Util::CommandLine.new.execute 36 | -------------------------------------------------------------------------------- /manifests/storeconfig/postgresql.pp: -------------------------------------------------------------------------------- 1 | class puppet::storeconfig::postgresql ( 2 | $dbuser, 3 | $dbpassword 4 | ) { 5 | 6 | include puppet::params 7 | 8 | # --- 9 | # PostgreSQL backend settings 10 | Ini_setting { 11 | ensure => 'present', 12 | section => 'master', 13 | path => $puppet::params::puppet_conf, 14 | } 15 | 16 | ini_setting { 17 | 'dbadapter': 18 | setting => 'dbadapter', 19 | value => 'postgresql'; 20 | 'dbmigrate': 21 | setting => 'dbmigrate', 22 | value => true; 23 | 'dbuser': 24 | setting => 'dbuser', 25 | value => $dbuser; 26 | 'dbpassword': 27 | setting => 'dbpassword', 28 | value => $dbpassword; 29 | 'dbserver': 30 | setting => 'dbserver', 31 | value => $puppet::storeconfig::dbserver; 32 | 'dbname': 33 | setting => 'dbname', 34 | value => 'puppet'; 35 | } 36 | 37 | # --- 38 | # Install the pg gem 39 | $package_name = $::operatingsystem ? { 40 | 'FreeBSD' => 'databases/rubygem-pg', 41 | default => 'pg', 42 | } 43 | $package_provider = $::operatingsystem ? { 44 | 'FreeBSD' => undef, 45 | default => gem, 46 | } 47 | package { 'pg': 48 | ensure => installed, 49 | name => $package_name, 50 | provider => $package_provider, 51 | } 52 | 53 | # --- 54 | # Database setup -- Something we don't have for Postgresql 55 | 56 | } 57 | -------------------------------------------------------------------------------- /spec/acceptance/webrick_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'server', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do 4 | context 'running on webrick/standalone', :server => 'webrick', :webserver => 'builtin' do 5 | it 'should run with no errors' do 6 | pp = <<-EOS 7 | class { 'puppet::server': 8 | servertype => 'standalone', 9 | ca => true, 10 | } 11 | EOS 12 | 13 | # Run it twice and test for idempotency 14 | apply_manifest(pp, :catch_failures => true) 15 | expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero 16 | end 17 | 18 | describe service('puppetmaster') do 19 | it { should be_enabled } 20 | it { should be_running } 21 | end 22 | 23 | # sanity checks to ensure the webrick setup doesn't bring in other services 24 | describe service('nginx') do 25 | it { should_not be_enabled } 26 | it { should_not be_running } 27 | end 28 | describe service('apache2') do 29 | it { should_not be_enabled } 30 | it { should_not be_running } 31 | end 32 | describe service('httpd') do 33 | it { should_not be_enabled } 34 | it { should_not be_running } 35 | end 36 | describe service('puppetserver') do 37 | it { should_not be_enabled } 38 | it { should_not be_running } 39 | end 40 | 41 | it_behaves_like 'basic working puppetmaster' 42 | 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /manifests/server/passenger.pp: -------------------------------------------------------------------------------- 1 | # Private class 2 | class puppet::server::passenger { 3 | class { 'puppet::server::standalone': enabled => false } 4 | 5 | if $::kernel == 'Darwin' { 6 | fail('puppet::server::passenger not supported on OS X') 7 | } 8 | 9 | include apache 10 | include apache::mod::ssl 11 | include apache::mod::passenger 12 | include puppet::server::rack 13 | 14 | apache::vhost { 'puppetmaster': 15 | servername => $puppet::server::servername, 16 | ip => $puppet::server::bindaddress, 17 | port => '8140', 18 | priority => '10', 19 | docroot => '/etc/puppet/rack/public/', 20 | ssl => true, 21 | ssl_cipher => $puppet::server::ssl_ciphers, 22 | ssl_protocol => $puppet::server::ssl_protocols, 23 | ssl_cert => "${puppet::ssldir}/certs/${puppet::server::servername}.pem", 24 | ssl_key => "${puppet::ssldir}/private_keys/${puppet::server::servername}.pem", 25 | ssl_chain => "${puppet::ssldir}/certs/ca.pem", 26 | ssl_ca => "${puppet::ssldir}/ca/ca_crt.pem", 27 | ssl_crl => "${puppet::ssldir}/ca/ca_crl.pem", 28 | ssl_verify_client => 'optional', 29 | ssl_verify_depth => '1', 30 | ssl_options => ['+StdEnvVars', '+ExportCertData'], 31 | request_headers => [ 32 | 'set X-SSL-Subject %{SSL_CLIENT_S_DN}e', 33 | 'set X-Client-DN %{SSL_CLIENT_S_DN}e', 34 | 'set X-Client-Verify %{SSL_CLIENT_VERIFY}e', 35 | ], 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /manifests/agent/service.pp: -------------------------------------------------------------------------------- 1 | # Private class 2 | class puppet::agent::service ( 3 | $enable = true 4 | ) { 5 | 6 | include puppet::params 7 | 8 | if $enable { 9 | $ensure = running 10 | } else { 11 | $ensure = stopped 12 | } 13 | 14 | # ---- 15 | # Puppet agent management 16 | service { 'puppet_agent': 17 | ensure => $ensure, 18 | name => $puppet::params::agent_service, 19 | enable => $enable, 20 | hasstatus => true, 21 | hasrestart => true, 22 | } 23 | 24 | # ---- 25 | # Special things for special kernels 26 | case $::kernel { 27 | darwin: { 28 | file { 'com.puppetlabs.puppet.plist': 29 | owner => 'root', 30 | group => '0', 31 | mode => '0644', 32 | content => template('puppet/com.puppetlabs.puppet.plist.erb'), 33 | path => '/Library/LaunchDaemons/com.puppetlabs.puppet.plist', 34 | before => Service['puppet_agent'], 35 | replace => false, 36 | } 37 | } 38 | default: { 39 | 40 | if $puppet::params::agent_service_conf { 41 | $file_ensure = $puppet::params::agent_service_conf ? { 42 | undef => 'absent', 43 | default => 'present', 44 | } 45 | 46 | file { 'puppet_agent_service_conf': 47 | ensure => $file_ensure, 48 | mode => '0644', 49 | owner => 'root', 50 | group => 'root', 51 | content => template('puppet/agent_service.erb'), 52 | path => $puppet::params::agent_service_conf, 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /templates/vhost/nginx/base.conf.erb: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 8140; 4 | <% if has_variable?( 'ipaddress6' ) -%> 5 | # Listen on an ipv6 version of this port too, but only do v6 on it, so we 6 | # don't get strange v4 mapped IPs in v6. 7 | listen [::]:8140 default ipv6only=on; 8 | <% end -%> 9 | 10 | client_max_body_size 16m; # Increase max body size for dashboard reports. 11 | 12 | proxy_connect_timeout 300s; # 5 minutes a piece, as puppet runs can take a while. 13 | proxy_read_timeout 300s; # See http://wiki.nginx.org/NginxHttpProxyModule#proxy_read_timeout 14 | 15 | ssl on; 16 | ssl_certificate <%= scope.lookupvar('puppet::params::puppet_ssldir') %>/certs/<%= @clientcert %>.pem; 17 | ssl_certificate_key <%= scope.lookupvar('puppet::params::puppet_ssldir') %>/private_keys/<%= @clientcert %>.pem; 18 | ssl_client_certificate <%= scope.lookupvar('puppet::params::puppet_ssldir') %>/certs/ca.pem; 19 | ssl_crl <%= scope.lookupvar('puppet::params::puppet_ssldir') %>/crl.pem; 20 | ssl_session_cache shared:SSL:8m; 21 | ssl_session_timeout 5m; 22 | ssl_ciphers HIGH:+MEDIUM; 23 | ssl_verify_client optional; 24 | 25 | root /usr/share/empty; 26 | 27 | location / { 28 | proxy_pass http://puppetmaster_upstream; 29 | proxy_redirect off; 30 | proxy_set_header Host $host; 31 | proxy_set_header X-Real-IP $remote_addr; 32 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 33 | proxy_set_header X-Client-Verify $ssl_client_verify; 34 | proxy_set_header X-Client-DN $ssl_client_s_dn; 35 | proxy_set_header X-SSL-Subject $ssl_client_s_dn; 36 | proxy_set_header X-SSL-Issuer $ssl_client_i_dn; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /spec/acceptance/agent_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'agent', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do 4 | context 'with default parameters', :agent_method => 'default' do 5 | it 'should run with no errors', :agent do 6 | pp = <<-EOS 7 | class { 'puppet::agent': } 8 | EOS 9 | 10 | # Run it twice and test for idempotency 11 | apply_manifest(pp, :catch_failures => true) 12 | expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero 13 | end 14 | end 15 | 16 | context 'running as cron', :agent_method => 'cron' do 17 | it 'should run with no errors' do 18 | pp = <<-EOS 19 | class { 'puppet::agent': 20 | method => 'cron', 21 | } 22 | EOS 23 | 24 | # Run it twice and test for idempotency 25 | apply_manifest(pp, :catch_failures => true) 26 | expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero 27 | end 28 | 29 | describe service('puppet') do 30 | it { 31 | should be_disabled 32 | } 33 | it { 34 | should_not be_running 35 | } 36 | end 37 | end 38 | context 'running as service', :agenttype => 'service' do 39 | it 'should run with no errors' do 40 | pp = <<-EOS 41 | class { 'puppet::agent': 42 | method => 'service', 43 | } 44 | EOS 45 | 46 | # Run it twice and test for idempotency 47 | apply_manifest(pp, :catch_failures => true) 48 | expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero 49 | end 50 | 51 | describe service('puppet') do 52 | it { 53 | should be_enabled 54 | } 55 | it { 56 | should be_running 57 | } 58 | end 59 | end 60 | 61 | end 62 | -------------------------------------------------------------------------------- /spec/classes/puppet_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'puppet' do 4 | let(:facts) { 5 | { 6 | :operatingsystem => 'OpenBSD', 7 | :domain => 'example.com', 8 | } 9 | } 10 | 11 | context "with srv records enabled" do 12 | let(:params) { {:use_srv_records => true} } 13 | 14 | it { should contain_ini_setting('use_srv_records').with_value(true) } 15 | it { should contain_ini_setting('srv_domain').with_value('example.com') } 16 | end 17 | 18 | context "with srv records disabled" do 19 | let(:params) { {:use_srv_records => false} } 20 | 21 | it { should contain_ini_setting('use_srv_records').with_ensure('absent') } 22 | it { should contain_ini_setting('srv_domain').with_ensure('absent') } 23 | end 24 | 25 | PuppetSpecFacts.facts_for_platform_by_name([ 26 | "Debian_wheezy_7.7_amd64_3.7.2_structured", 27 | "Ubuntu_precise_12.04_amd64_PE-3.3.2_stringified", 28 | "Ubuntu_trusty_14.04_amd64_PE-3.3.2_stringified"]).each do |name, facthash| 29 | 30 | context 'supported operating systems' do 31 | ['Debian', 'RedHat', 'CentOS'].each do |operatingsystem| 32 | describe "puppet class without any parameters on #{operatingsystem}" do 33 | let(:params) {{ }} 34 | let(:facts) { facthash } 35 | 36 | it { should compile.with_all_deps } 37 | it { should contain_class('puppet::params') } 38 | end 39 | end 40 | end 41 | 42 | context 'unsupported operating system' do 43 | describe 'puppet class without any parameters on OpenVMS' do 44 | let(:facts) {{ 45 | :operatingsystem => 'OpenVMS', 46 | }} 47 | 48 | it { expect { should contain_package('puppet') }.to raise_error(Puppet::Error, /Sorry, OpenVMS is not supported/) } 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /manifests/storeconfig/mysql.pp: -------------------------------------------------------------------------------- 1 | class puppet::storeconfig::mysql ( 2 | $dbuser, 3 | $dbpassword 4 | ){ 5 | 6 | include puppet::params 7 | 8 | # --- 9 | # MySQL backend settings 10 | Ini_setting { 11 | ensure => 'present', 12 | section => 'master', 13 | path => $puppet::params::puppet_conf, 14 | } 15 | 16 | ini_setting { 17 | 'dbadapter': 18 | setting => 'dbadapter', 19 | value => 'mysql'; 20 | 'dbmigrate': 21 | setting => 'dbmigrate', 22 | value => true; 23 | 'dbuser': 24 | setting => 'dbuser', 25 | value => $dbuser; 26 | 'dbpassword': 27 | setting => 'dbpassword', 28 | value => $dbpassword; 29 | 'dbserver': 30 | setting => 'dbserver', 31 | value => $puppet::storeconfig::dbserver; 32 | 'dbsocket': 33 | setting => 'dbsocket', 34 | value => $puppet::storeconfig::dbsocket; 35 | } 36 | 37 | # --- 38 | # Install the mysql gem 39 | $package_name = $::operatingsystem ? { 40 | 'Debian' => 'libmysql-ruby', 41 | default => mysql, 42 | } 43 | $package_provider = $::operatingsystem ? { 44 | 'Debian' => 'apt', 45 | default => 'gem', 46 | } 47 | 48 | package { 'gem-mysql': 49 | ensure => installed, 50 | name => $package_name, 51 | provider => $package_provider, 52 | } 53 | 54 | # --- 55 | # Database setup 56 | database{ 'puppet': 57 | ensure => present, 58 | charset => 'utf8', 59 | } 60 | 61 | database_user{ "${dbuser}@localhost": 62 | ensure => present, 63 | password_hash => mysql_password($dbpassword), 64 | require => Database['puppet'], 65 | } 66 | 67 | database_grant { 'puppet@localhost/puppet': 68 | privileges => [all], 69 | require => [ Database['puppet'], Database_user['puppet@localhost'] ], 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ploperations-puppet", 3 | "version": "1.0.2", 4 | "source": "https://github.com/puppetlabs-operations/puppet-puppet", 5 | "author": "Puppet Labs Operations", 6 | "license": "Apache-2.0", 7 | "summary": "Install and manage Puppet open source", 8 | "description": "UNKNOWN", 9 | "project_page": "https://github.com/puppetlabs-operations/puppet-puppet", 10 | "dependencies": [ 11 | { 12 | "name": "stahnma/puppetlabs_yum", 13 | "version_requirement": ">= 0.1.0 <1.0.0" 14 | }, 15 | { 16 | "name": "ploperations/puppetlabs_apt", 17 | "version_requirement": ">= 0.0.1 <1.0.0" 18 | }, 19 | { 20 | "name": "ploperations/unicorn", 21 | "version_requirement": ">= 1.0.0 <2.0.0" 22 | }, 23 | { 24 | "name": "puppetlabs/inifile", 25 | "version_requirement": ">= 1.0.0 <2.0.0" 26 | }, 27 | { 28 | "name": "puppetlabs/apache", 29 | "version_requirement": ">= 0.9.0 <2.0.0" 30 | }, 31 | { 32 | "name": "gentoo/portage", 33 | "version_requirement": ">= 2.1.0 <3.0.0" 34 | }, 35 | { 36 | "name": "jfryman/nginx", 37 | "version_requirement": ">= 0.2.0 <1.0.0" 38 | }, 39 | { 40 | "name": "puppetlabs/puppetdb", 41 | "version_requirement": ">= 4.1.0 <5.0.0" 42 | }, 43 | { 44 | "name": "puppetlabs/stdlib", 45 | "version_requirement": ">= 4.2.2 <5.0.0" 46 | } 47 | ], 48 | "operatingsystem_support": [ 49 | { 50 | "operatingsystem": "Debian", 51 | "operatingsystemrelease": [ 52 | "6", 53 | "7" 54 | ] 55 | } 56 | ], 57 | "requirements": [ 58 | { 59 | "name": "pe", 60 | "version_requirement": "3.x" 61 | }, 62 | { 63 | "name": "puppet", 64 | "version_requirement": "3.x" 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /manifests/init.pp: -------------------------------------------------------------------------------- 1 | # == Class: puppet 2 | # 3 | # == Description 4 | # 5 | # Sets up the /etc/puppet ownership and various puppet.conf variables under the 6 | # [main] section 7 | # 8 | # This class should not be directly included. Its parameters should be defined 9 | # in hieradata. 10 | # 11 | # == Parameters 12 | # 13 | # [*logdir*] 14 | # The logdir variable in puppet.conf. 15 | # Default: platform dependent 16 | # 17 | # [*vardir*] 18 | # The vardir variable in puppet.conf. 19 | # Default: platform dependent 20 | # 21 | # [*ssldir*] 22 | # The ssldir variable in puppet.conf. 23 | # Default: platform dependent 24 | # 25 | # [*rundir*] 26 | # The rundir variable in puppet.conf. 27 | # Default: platform dependent 28 | # 29 | # [*confdir*] 30 | # The confdir variable in puppet.conf. 31 | # Default: platform dependent 32 | # 33 | # [*user*] 34 | # The owner of /etc/puppet directory 35 | # Default: platform dependent 36 | # 37 | # [*group*] 38 | # The group of /etc/puppet directory 39 | # Default: platform dependent 40 | # 41 | # [*conf*] 42 | # The path of the puppet.conf file 43 | # Default: platform dependent 44 | # 45 | # [*use_srv_records*] 46 | # The use_srv_records variable in puppet.conf. 47 | # Default: false 48 | # 49 | # [*srv_domain*] 50 | # The srv_domain variable in puppet.conf. 51 | # Default: $::domain 52 | # 53 | class puppet ( 54 | $logdir = $puppet::params::puppet_logdir, 55 | $vardir = $puppet::params::puppet_vardir, 56 | $ssldir = $puppet::params::puppet_ssldir, 57 | $rundir = $puppet::params::puppet_rundir, 58 | $confdir = $puppet::params::puppet_confdir, 59 | $user = $puppet::params::puppet_user, 60 | $group = $puppet::params::puppet_group, 61 | $conf = $puppet::params::puppet_conf, 62 | $use_srv_records = false, 63 | $srv_domain = $::domain, 64 | $stringify_facts = false 65 | ) inherits puppet::params { 66 | 67 | validate_bool($use_srv_records) 68 | validate_bool($stringify_facts) 69 | 70 | include puppet::config 71 | 72 | file { $confdir: 73 | ensure => 'directory', 74 | owner => $user, 75 | group => $group, 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /manifests/agent/config.pp: -------------------------------------------------------------------------------- 1 | class puppet::agent::config { 2 | 3 | Ini_setting { 4 | path => $puppet::conf, 5 | section => 'agent', 6 | ensure => 'present', 7 | notify => Service['puppet_agent'], 8 | } 9 | 10 | ini_setting { 'server': 11 | section => 'main', 12 | setting => 'server', 13 | value => $puppet::agent::server, 14 | } 15 | 16 | if $puppet::agent::ca_server { 17 | $real_ca_server = $puppet::agent::ca_server 18 | } else { 19 | $real_ca_server = $puppet::agent::server 20 | } 21 | 22 | ini_setting { 'ca_server': 23 | section => 'main', 24 | setting => 'ca_server', 25 | value => $real_ca_server, 26 | } 27 | 28 | if $puppet::agent::report_server { 29 | $real_report_server = $puppet::agent::report_server 30 | } else { 31 | $real_report_server = $puppet::agent::server 32 | } 33 | 34 | ini_setting { 'report_server': 35 | section => 'main', 36 | setting => 'report_server', 37 | value => $real_report_server, 38 | } 39 | 40 | ini_setting { 'pluginsync': 41 | setting => 'pluginsync', 42 | value => $puppet::agent::pluginsync, 43 | } 44 | 45 | ini_setting { 'certname': 46 | setting => 'certname', 47 | value => $puppet::agent::certname, 48 | } 49 | 50 | ini_setting { 'report': 51 | setting => 'report', 52 | value => $puppet::agent::report, 53 | } 54 | 55 | ini_setting { 'environment': 56 | setting => 'environment', 57 | value => $puppet::agent::environment, 58 | } 59 | 60 | ini_setting { 'show_diff': 61 | setting => 'show_diff', 62 | value => $puppet::agent::show_diff, 63 | } 64 | 65 | ini_setting { 'splay': 66 | setting => 'splay', 67 | value => $puppet::agent::splay, 68 | } 69 | 70 | ini_setting { 'configtimeout': 71 | setting => 'configtimeout', 72 | value => $puppet::agent::configtimeout, 73 | } 74 | 75 | ini_setting { 'usecacheonfailure': 76 | setting => 'usecacheonfailure', 77 | value => $puppet::agent::usecacheonfailure, 78 | } 79 | 80 | if ! empty( $puppet::agent::runinterval ) 81 | { 82 | ini_setting { 'runinterval': 83 | setting => 'runinterval', 84 | value => $puppet::agent::runinterval, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /manifests/storeconfig.pp: -------------------------------------------------------------------------------- 1 | # Class: puppet::storeconfig 2 | # 3 | # This class installs and configures Puppet's stored configuration capability 4 | # 5 | # Parameters: 6 | # 7 | # Actions: 8 | # 9 | # Requires: 10 | # 11 | # Sample Usage: 12 | # 13 | class puppet::storeconfig ( 14 | $backend = '', 15 | $dbuser = 'puppet', 16 | $dbpassword = '', 17 | $dbserver = 'localhost', 18 | $dbsocket = '' 19 | ) { 20 | 21 | include puppet 22 | include puppet::params 23 | 24 | #$puppet::storeconfigs = 'true' # called from puppet::server only if storeconfigs is on 25 | 26 | Ini_setting { 27 | ensure => 'present', 28 | section => 'master', 29 | path => $puppet::params::puppet_conf, 30 | } 31 | 32 | # if no backend was selected 33 | $thin_enable = $backend ? { 34 | '' => true, 35 | default => false, 36 | } 37 | $thin_ensure = $backend ? { 38 | '' => 'present', 39 | default => 'absent', 40 | } 41 | 42 | # use thin_storageconfigs 43 | ini_setting { 44 | 'storeconfigs': 45 | setting => 'storeconfigs', 46 | value => $thin_enable; 47 | 'thin_storeconfigs': 48 | ensure => $thin_ensure, 49 | setting => 'thin_storeconfigs', 50 | value => $thin_enable; 51 | } 52 | 53 | case $backend { 54 | 'mysql','postgresql','sqlite': { 55 | 56 | # this is not pretty, and could be put into params.. 57 | 58 | $package_name = $::operatingsystem ? { 59 | 'Debian' => 'libactiverecord-ruby', 60 | default => 'activerecord', 61 | } 62 | $package_provider = $::operatingsystem ? { 63 | 'Debian' => 'apt', 64 | 'Darwin' => 'macports', 65 | default => 'gem', 66 | } 67 | 68 | package { 'gem-activerecord': 69 | name => $package_name, 70 | provider => $package_provider, 71 | } 72 | } 73 | } 74 | 75 | case $backend { 76 | 'sqlite3': { 77 | include puppet::storeconfig::sqlite 78 | } 79 | 'mysql': { 80 | class { 'puppet::storeconfig::mysql': 81 | dbuser => $dbuser, 82 | dbpassword => $dbpassword, 83 | } 84 | } 85 | 'postgresql': { 86 | class { 'puppet::storeconfig::postgresql': 87 | dbuser => $dbuser, 88 | dbpassword => $dbpassword, 89 | } 90 | } 91 | 'puppetdb': { 92 | class {'::puppet::storeconfig::puppetdb': } 93 | } 94 | default: { err('Target storeconfigs backend "$backend" not implemented') } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | VAGRANTFILE_API_VERSION = "2" 5 | 6 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 7 | ### Define options for all VMs ### 8 | # Using vagrant-cachier improves performance if you run repeated yum/apt updates 9 | if defined? VagrantPlugins::Cachier 10 | config.cache.auto_detect = true 11 | end 12 | config.ssh.forward_agent = true 13 | 14 | config.vm.provider :virtualbox do |vb| 15 | vb.customize ["modifyvm", :id, "--memory", "512", "--cpus", "4", "--ioapic", "on"] 16 | end 17 | # hack to avoid ubuntu/debian-specific 'stdin: is not a tty' error on startup 18 | config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'" 19 | 20 | # distro-agnostic puppet install script from https://github.com/danieldreier/puppet-installer 21 | config.vm.provision "shell", inline: "curl getpuppet.whilefork.com | bash" 22 | 23 | PUPPETMASTER_IP = '192.168.37.23' 24 | 25 | config.vm.define :package_install do |node| 26 | node.vm.box = 'puppetlabs/debian-7.6-64-nocm' 27 | node.vm.hostname = 'debian7.boxnet' 28 | node.vm.network :private_network, ip: PUPPETMASTER_IP 29 | 30 | # hack to avoid ubuntu/debian-specific 'stdin: is not a tty' error on startup 31 | node.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'" 32 | 33 | # distro-agnostic puppet install script from https://github.com/danieldreier/puppet-installer 34 | node.vm.provision "shell", inline: "curl getpuppet.whilefork.com | bash" 35 | 36 | # use a packaged version of puppet-puppet to install dependencies via the forge 37 | # rm removes symlink from next step for vagrant provision idempotency 38 | node.vm.provision "shell", 39 | inline: "rm -rf /etc/puppet/modules/puppet" 40 | node.vm.provision "shell", 41 | inline: "if ls /vagrant/pkg/ploperations-puppet-*.tar.gz ; then puppet module install /vagrant/pkg/ploperations-puppet-*.tar.gz; fi" 42 | end 43 | 44 | config.vm.define :shared_folder do |node| 45 | node.vm.box = 'puppetlabs/debian-7.4-64-nocm' 46 | node.vm.hostname = 'debian7.boxnet' 47 | node.vm.network :private_network, ip: PUPPETMASTER_IP 48 | 49 | # use a packaged version of puppet-puppet to install dependencies via the forge 50 | # rm removes symlink from next step for vagrant provision idempotency 51 | node.vm.provision "shell", 52 | inline: "rm -rf /etc/puppet/modules/puppet" 53 | node.vm.provision "shell", 54 | inline: "if ls /vagrant/pkg/ploperations-puppet-*.tar.gz ; then puppet module install /vagrant/pkg/ploperations-puppet-*.tar.gz; rm -rf /etc/puppet/modules/puppet; fi" 55 | 56 | # if this was done as a vagrant shared folder, the previous step either 57 | # wouldn't run or would overwrite files this approach allows using 58 | # puppet's facilities for installing dependencies while also keeping a 59 | # shared folder to simplify development 60 | node.vm.provision "shell", 61 | inline: "ln -s /vagrant /etc/puppet/modules/puppet" 62 | end 63 | 64 | config.vm.define :agent do |node| 65 | node.vm.box = 'puppetlabs/debian-7.4-64-nocm' 66 | node.vm.hostname = 'debian7agent.boxnet' 67 | node.vm.network :private_network, ip: "192.168.37.25" 68 | node.vm.provision "shell", 69 | inline: "if ! grep #{PUPPETMASTER_IP} /etc/hosts; then echo '#{PUPPETMASTER_IP} puppet' >> /etc/hosts; fi" 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/classes/server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | shared_examples_for "all puppet master types" do 4 | it { should compile.with_all_deps } 5 | it { should contain_class('puppet::server') } 6 | it { should contain_class('puppet::params') } 7 | it { should contain_class('puppet::server::config') } 8 | end 9 | 10 | shared_examples_for "basic puppetmaster config" do 11 | it { should contain_ini_setting('ca').with_value(true) } 12 | it { should contain_ini_setting('modulepath').with_value('/etc/puppet/environments/production/modules') } 13 | it { should contain_ini_setting('environmentpath').with_ensure('absent') } 14 | end 15 | 16 | shared_examples_for "basic puppetmaster environment config" do 17 | it { should contain_ini_setting('ca').with_value(true) } 18 | it { should contain_ini_setting('environmentpath').with_value('$confdir/environments') } 19 | it { should contain_ini_setting('basemodulepath').with_value('/etc/puppet/modules:/usr/share/puppet/modules') } 20 | it { should contain_ini_setting('modulepath').with_ensure('absent') } 21 | end 22 | 23 | PuppetSpecFacts.facts_for_platform_by_name(["Debian_wheezy_7.7_amd64_3.7.2_structured", "Ubuntu_precise_12.04_amd64_PE-3.3.2_stringified", "Ubuntu_trusty_14.04_amd64_PE-3.3.2_stringified"]).each do |name, facthash| 24 | describe "puppet::server" do 25 | let(:facts) { facthash } 26 | 27 | context "running on #{name}" do 28 | ['standalone','passenger','unicorn'].each do |server_type| 29 | context "servertype => #{server_type}" do 30 | let(:params) {{ 31 | :servertype => server_type, 32 | :storeconfigs => 'puppetdb', 33 | :environmentpath => '$confdir/environments', 34 | :basemodulepath => ['/etc/puppet/modules', '/usr/share/puppet/modules'], 35 | :default_manifest => 'site.pp', 36 | :ca => true, 37 | }} 38 | 39 | case facthash['osfamily'] 40 | when 'RedHat' 41 | puppetmaster_package = 'puppet-server' 42 | when 'Debian' 43 | puppetmaster_package = 'puppetmaster' 44 | 45 | end 46 | it_behaves_like "all puppet master types" 47 | it_behaves_like "basic puppetmaster environment config" 48 | 49 | case server_type 50 | when 'standalone' 51 | it { 52 | should contain_class('puppet::server::standalone') 53 | should contain_service('puppetmaster').with({ :ensure => "running" }) 54 | should contain_package(puppetmaster_package) 55 | } 56 | 57 | when 'passenger' 58 | it { 59 | should contain_class('puppet::server::passenger') 60 | should contain_class('apache') 61 | should contain_class('apache::mod::passenger') 62 | } 63 | 64 | when 'unicorn' 65 | it { 66 | should contain_class('puppet::server::unicorn') 67 | should contain_service('puppetmaster').with({:ensure => "stopped"}) 68 | should contain_service('nginx').with({:ensure => "running"}) 69 | should contain_service('unicorn_puppetmaster').with({:ensure => "running"}) 70 | } 71 | end 72 | end 73 | end 74 | 75 | context "manage_package => false" do 76 | let(:params) {{ :manage_package => false }} 77 | case facthash['osfamily'] 78 | when 'RedHat' 79 | it { should_not contain_package('puppet-server') } 80 | when 'Debian' 81 | it { should_not contain_package('puppetmaster') } 82 | end 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Testing 2 | ======= 3 | 4 | Puppet-puppet is tested via beaker and rspec-puppet. Rspec-puppet tests are run 5 | automatically for pull requests via travis-ci, or can be triggered manually 6 | by running `rake spec` after doing a `bundle install`. 7 | 8 | Beaker tests are run slightly differently for puppet-puppet than for other 9 | beaker-tested projects because the test suite should be run independently for 10 | each test case to prevent contamination between environments. For example, 11 | running the apache-passenger based puppet master test case will cause obvious 12 | conflicts if the nginx-unicorn puppet master is subsequently built on the same 13 | virtual machine. Rspec tags are used to accomplish this in the test cases. 14 | 15 | Running beaker tests for unicorn puppetmaster on the default nodeset (debian 7): 16 | ```bash 17 | rspec . --tag servertype:unicorn --pattern "spec/acceptance/**/*_spec.rb" 18 | ``` 19 | 20 | Same as above, but only only destroying the VM if the build is successful, to 21 | help troubleshooting: 22 | ``` 23 | BEAKER_destroy=onpass BEAKER_provision=yes rspec . --tag servertype:unicorn --pattern "spec/acceptance/**/*_spec.rb" 24 | ``` 25 | 26 | Same as above, but on a different nodeset (see `spec/acceptance/nodesets` for 27 | options): 28 | ```bash 29 | BEAKER_set=sles-11sp1-x64 BEAKER_destroy=onpass BEAKER_provision=yes rspec . --tag servertype:unicorn --pattern "spec/acceptance/**/*_spec.rb" 30 | ``` 31 | 32 | The presence of an OS/distro in the nodeset list does not imply support. The 33 | SLES example above is expected to fail most test cases but is included to lower 34 | the bar for future contributors who want to add support for additional distros. 35 | 36 | The rake command includes acceptance testing tasks, but these should not be 37 | used because they will run all of the acceptance tests on the same VM, which 38 | is expected to fail. 39 | 40 | If a beaker test fails, you can SSH into the environment if you use BEAKER_PROVISION=onpass. 41 | The path of the vagrantfile will be `.vagrant/beaker_vagrant_files/debian-73-x64.yml` 42 | if you followed the above instructions, and slightly different if you used a 43 | different nodeset. `cd` to that directory and `vagrant ssh` to access the VM. 44 | The tests that ran are in /tmp with randomly generated filenames. 45 | 46 | #### rspec-puppet tests 47 | (note that these are run automatically by travis CI on pull requests) 48 | ``` 49 | rake spec 50 | ``` 51 | 52 | Vagrant 53 | ======= 54 | 55 | This project includes a Vagrantfile to facilitate development. The purpose of 56 | the Vagrantfile is to give you an easy way to interactively edit code and run 57 | tests without polluting your workstation, and without having to worry about 58 | deploying code constantly to test minor changes. 59 | 60 | The recommended way to use this for iteratively working out changes is: 61 | ``` 62 | rm -rf pkg 63 | rake build 64 | vagrant up shared_folder 65 | vagrant ssh shared_folder 66 | ``` 67 | 68 | This will build a new package you could upload to puppetforge in the pkg 69 | directory. Vagrant will use that to install dependencies, but will mount the 70 | repository to /etc/puppet/modules/puppet so your changes take effect 71 | immediately. 72 | 73 | A second vagrant environment exists for testing packages prior to puppetforge 74 | release. A typical workflow might be: 75 | ```bash 76 | rm -rf pkg 77 | rake build 78 | vagrant up package_install 79 | vagrant ssh package_install 80 | ``` 81 | 82 | Once in one of these systems, the tests in /vagrant/tests may be helpful for 83 | testing during development. 84 | 85 | Finally, an agent VM is provided to help test client-server interaction: 86 | 87 | ```bash 88 | vagrant up agent 89 | vagrant ssh agent 90 | sudo puppet agent --test --waitforcert 10 --server puppet 91 | ``` 92 | -------------------------------------------------------------------------------- /spec/classes/agent_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | shared_examples 'agent examples' do 4 | it { should contain_class('puppet::agent') } 5 | it { should compile.with_all_deps } 6 | it { should contain_ini_setting('report_server').with_value('puppet_reports.example.com') } 7 | it { should contain_ini_setting('server').with_value('puppet.example.com') } 8 | it { should contain_ini_setting('pluginsync').with_value(true) } 9 | it { should contain_package('puppet') } 10 | end 11 | 12 | PuppetSpecFacts.facts_for_platform_by_name(["Debian_wheezy_7.7_amd64_3.7.2_structured", "CentOS_5.11_x86_64_3.7.1_structured", "FreeBSD_10.0-RELEASE_amd64_3.6.2_structured"]).each do |name, facthash| 13 | describe "puppet::agent" do 14 | let(:facts) { facthash } 15 | 16 | context "running on #{name}" do 17 | [true, false].each do |manage_repos| 18 | 19 | context "when manage_repos => #{manage_repos}" do 20 | let(:params) {{ 21 | :server => 'puppet.example.com', 22 | :report_server => 'puppet_reports.example.com', 23 | :manage_repos => manage_repos, 24 | }} 25 | 26 | case facthash['osfamily'] 27 | when 'RedHat' 28 | it_behaves_like "agent examples" 29 | it { 30 | is_expected.to contain_class('Puppet::Package::Repository') if manage_repos == true 31 | is_expected.to contain_yumrepo('puppetlabs-products') if manage_repos == true 32 | is_expected.to_not contain_yumrepo('puppetlabs-products') if manage_repos == false 33 | is_expected.to_not contain_apt__source('puppetlabs') 34 | } 35 | when 'Debian' 36 | it_behaves_like "agent examples" 37 | it { is_expected.to contain_class('Puppet::Package::Repository') if manage_repos == true } 38 | it { should_not contain_class('puppetlabs_yum') } 39 | if manage_repos == true 40 | it { 41 | is_expected.to contain_class('puppetlabs_apt') 42 | is_expected.to contain_apt__source('puppetlabs') 43 | } 44 | elsif manage_repos == false 45 | it { is_expected.to_not contain_class('puppetlabs_apt') } 46 | end 47 | when 'FreeBSD' 48 | if manage_repos == false 49 | it { 50 | is_expected.to_not contain_class('puppetlabs_apt') 51 | is_expected.to_not contain_class('puppetlabs_yum') 52 | is_expected.to compile.with_all_deps 53 | } 54 | end 55 | end 56 | end 57 | end 58 | ['service','cron','only_service'].each do |agent_method| 59 | manage_repos = false 60 | context "method => #{agent_method}" do 61 | describe "agent configuration on #{facthash["osfamily"]}" do 62 | let(:params) {{ 63 | :server => 'puppet.example.com', 64 | :report_server => 'puppet_reports.example.com', 65 | :method => agent_method, 66 | :manage_repos => manage_repos, 67 | }} 68 | 69 | it_behaves_like "agent examples" 70 | case agent_method 71 | when 'service' 72 | it { should contain_service('puppet_agent').with({ :ensure => "running" }) } 73 | it { should contain_cron('puppet agent') } 74 | when 'cron' 75 | it { should contain_service('puppet_agent').with({ :ensure => "stopped" }) } 76 | it { should contain_cron('puppet agent') } 77 | when 'only_service' 78 | it { should contain_service('puppet_agent').with({ :ensure => "running" }) } 79 | it { should_not contain_cron('puppet agent') } 80 | end 81 | end 82 | end 83 | end 84 | end 85 | end 86 | end 87 | 88 | -------------------------------------------------------------------------------- /manifests/server/unicorn.pp: -------------------------------------------------------------------------------- 1 | # Private class 2 | class puppet::server::unicorn { 3 | 4 | include puppet 5 | include puppet::server::rack 6 | include nginx 7 | 8 | class { 'puppet::server::standalone': 9 | enabled => false, 10 | before => [ 11 | Nginx::Resource::Vhost['puppetmaster'], 12 | Unicorn::App['puppetmaster'], 13 | ], 14 | } 15 | 16 | $unicorn_socket = "unix:${puppet::rundir}/puppetmaster_unicorn.sock" 17 | 18 | nginx::resource::vhost { 'puppetmaster': 19 | server_name => [$puppet::server::servername], 20 | listen_ip => $puppet::server::bindaddress, 21 | ssl => true, 22 | ssl_port => '8140', 23 | listen_port => '8140', # force ssl_only by matching ssl_port 24 | ssl_cert => "${puppet::ssldir}/certs/${puppet::server::servername}.pem", 25 | ssl_key => "${puppet::ssldir}/private_keys/${puppet::server::servername}.pem", 26 | ssl_ciphers => $puppet::server::ssl_ciphers, 27 | ssl_protocols => $puppet::server::ssl_protocols, 28 | use_default_location => false, 29 | vhost_cfg_append => { 30 | ssl_crl => "${puppet::ssldir}/crl.pem", 31 | ssl_client_certificate => "${puppet::ssldir}/certs/ca.pem", 32 | ssl_verify_client => 'optional', 33 | proxy_set_header => [ 'Host $host', 34 | 'X-Real-IP $remote_addr', 35 | 'X-Forwarded-For $proxy_add_x_forwarded_for', 36 | 'X-Client-Verify $ssl_client_verify', 37 | 'X-Client-DN $ssl_client_s_dn', 38 | 'X-SSL-Issuer $ssl_client_i_dn'], 39 | root => '/usr/share/empty', 40 | } 41 | } 42 | nginx::resource::location { 'unicorn_upstream': 43 | ensure => present, 44 | location => '/', 45 | vhost => 'puppetmaster', 46 | proxy_set_header => [], 47 | location_custom_cfg => { 48 | proxy_pass => 'http://puppetmaster_unicorn', 49 | proxy_redirect => 'off', 50 | proxy_connect_timeout => '90', 51 | proxy_read_timeout => '300', 52 | }, 53 | # this priority sets concat order so that the location is created inside 54 | # the server block. This works around a possible bug in jfryman/nginx. 55 | priority => 701, 56 | } 57 | nginx::resource::upstream { 'puppetmaster_unicorn': 58 | members => [ 59 | $unicorn_socket 60 | ], 61 | } 62 | 63 | if ! empty( $::puppet::server::external_ca ) 64 | { 65 | nginx::resource::location { 'external_certificate_authority_proxy': 66 | ensure => present, 67 | location => '~ ^/.*/certificate.*', 68 | vhost => 'puppetmaster', 69 | proxy_set_header => [], 70 | location_custom_cfg => { 71 | proxy_pass => $puppet::server::external_ca, 72 | proxy_redirect => 'off', 73 | proxy_connect_timeout => '90', 74 | proxy_read_timeout => '300', 75 | }, 76 | # this priority sets concat order so that the location is created inside 77 | # the server block. This works around a possible bug in jfryman/nginx. 78 | priority => 701, 79 | } 80 | } 81 | 82 | unicorn::app { 'puppetmaster': 83 | approot => $puppet::confdir, 84 | config_file => "${puppet::confdir}/unicorn.conf", 85 | pidfile => "${puppet::rundir}/puppetmaster_unicorn.pid", 86 | socket => $unicorn_socket, 87 | logdir => $puppet::logdir, 88 | user => $puppet::user, 89 | group => $puppet::group, 90 | before => Service['nginx'], 91 | # export_home => $::confdir, # uncomment pending https://github.com/puppetlabs-operations/puppet-unicorn/pull/14 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | require 'beaker-rspec/spec_helper' 2 | require 'beaker-rspec/helpers/serverspec' 3 | 4 | UNSUPPORTED_PLATFORMS = ['Suse','windows','AIX','Solaris'] 5 | 6 | unless ENV['RS_PROVISION'] == 'no' 7 | hosts.each do |host| 8 | if host['platform'] =~ /debian/ 9 | # debian 6 doesn't have gems in path by default 10 | on host, 'echo \'export PATH=/var/lib/gems/1.8/bin/:${PATH}\' >> ~/.bashrc' 11 | end 12 | if host.is_pe? 13 | install_pe 14 | else 15 | install_puppet 16 | on host, "mkdir -p #{host['distmoduledir']}" 17 | end 18 | end 19 | end 20 | 21 | #agents = hosts_as :agent 22 | #agents.each do |host| 23 | # on host, 'touch /tmp/agent_test' 24 | #end 25 | 26 | masters = hosts_as :master 27 | masters.each do |host| 28 | 29 | on host, 'touch /etc/puppet/hiera.yaml' 30 | # Install r10k for installing modules from github 31 | # This is a bit of a hack but necessary because these dependencies do not 32 | # have recent versions available on puppetforge 33 | install_package host, 'rubygems' 34 | install_package host, 'git' 35 | on host, 'hash r10k || gem install r10k --no-ri --no-rdoc' 36 | on host, 'echo "$(facter ipaddress) puppet" >> /etc/hosts' 37 | 38 | puppetfile = <<-EOS 39 | mod 'stdlib', :git => 'git://github.com/puppetlabs/puppetlabs-stdlib.git' 40 | mod 'apt', :git => 'git://github.com/puppetlabs/puppetlabs-apt.git' 41 | mod 'concat', :git => 'git://github.com/puppetlabs/puppetlabs-concat.git' 42 | mod 'ruby', 43 | :git => 'git://github.com/puppetlabs/puppetlabs-ruby.git', 44 | :ref => '0.1.1' 45 | mod 'puppetlabs_yum', :git => 'git://github.com/stahnma/puppet-module-puppetlabs_yum' 46 | mod 'puppetlabs_apt', :git => 'git://github.com/puppetlabs-operations/puppet-puppetlabs_apt.git' 47 | mod 'interval', :git => 'git://github.com/puppetlabs-operations/puppet-interval.git' 48 | mod 'unicorn', :git => 'git://github.com/puppetlabs-operations/puppet-unicorn.git' 49 | mod 'rack', :git => 'git://github.com/puppetlabs-operations/puppet-rack.git' 50 | mod 'bundler', :git => 'git://github.com/puppetlabs-operations/puppet-bundler.git' 51 | mod 'nginx', :git => 'git://github.com/jfryman/puppet-nginx.git', :ref => 'v0.0.10' 52 | mod 'inifile', :git => 'git://github.com/puppetlabs/puppetlabs-inifile.git' 53 | mod 'apache', :git => 'git://github.com/puppetlabs/puppetlabs-apache.git' 54 | mod 'portage', :git => 'git://github.com/gentoo/puppet-portage.git' 55 | 56 | 57 | EOS 58 | on host, "echo \"#{puppetfile}\" > /etc/puppet/Puppetfile" 59 | on host, "cd /etc/puppet; r10k puppetfile install" 60 | on host, "mkdir -p /etc/puppet/environments/production/modules" 61 | on host, "puppet config set --section master environmentpath '$confdir/environments'" 62 | on host, "puppet config set --section master basemodulepath '$confdir/modules'" 63 | 64 | proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) 65 | puppet_module_install(:source => proj_root, :module_name => 'puppet') 66 | 67 | # Generate CA cert because it's necessary for puppet masters but not 68 | # within the scope of the puppet module 69 | on host, "rm -rf /var/lib/puppet/ssl; puppet cert --generate $HOSTNAME" 70 | 71 | end 72 | 73 | shared_examples_for "basic working puppetmaster" do 74 | describe command('puppet agent --test --server puppet') do 75 | its(:exit_status) { should eq 0 } 76 | its(:stderr) { should_not match /Forbidden request:/ } 77 | its(:stderr) { should_not match /Error:/ } 78 | end 79 | describe port(8140) do 80 | it { 81 | should be_listening 82 | } 83 | end 84 | end 85 | 86 | shared_examples_for "nginx-based webserver" do 87 | describe package('nginx') do 88 | it { should be_installed } 89 | end 90 | 91 | describe service('nginx') do 92 | it { should be_enabled } 93 | it { should be_running } 94 | end 95 | 96 | describe service('puppetmaster') do 97 | it { should_not be_enabled } 98 | it { should_not be_running } 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /manifests/agent.pp: -------------------------------------------------------------------------------- 1 | # == Class: puppet::agent 2 | # 3 | # Install, configure, and run a puppet agent instance. 4 | # 5 | # == Parameters 6 | # 7 | # [*ensure*] 8 | # The package ensure value. 9 | # Default: present 10 | # 11 | # [*server*] 12 | # The puppet server to use for fetching catalogs. 13 | # Default: puppet 14 | # 15 | # [*ca_server*] 16 | # The puppet server to use for certificate requests and similar actions. 17 | # Default: puppet::agent::server 18 | # 19 | # [*report*] 20 | # The report variable in puppet.conf, whether to send reports or not 21 | # Default: true 22 | # 23 | # [*report_server*] 24 | # The puppet server to send reports. 25 | # Default: puppet::agent::server 26 | # 27 | # [*manage_repos*] 28 | # Whether to manage Puppet Labs APT or YUM package repos. 29 | # Default: false 30 | # 31 | # [*environment*] 32 | # What environment the agent should be part of. 33 | # Default: $::environment 34 | # 35 | # [*pluginsync*] 36 | # The pluginsync variable in puppet.conf 37 | # Default: true 38 | # 39 | # [*certname*] 40 | # The certname variable in puppet.conf 41 | # Default: $::clientcert 42 | # 43 | # [*show_diff*] 44 | # The show_diff variable in puppet.conf 45 | # Default: false 46 | # 47 | # [*splay*] 48 | # The splay variable in puppet.conf 49 | # Default: false 50 | # 51 | # [*configtimeout*] 52 | # The configtimeout variable in puppet.conf 53 | # Default: 360 54 | # 55 | # [*usecacheonfailure*] 56 | # The usecacheonfailure variable in puppet.conf 57 | # Default: true 58 | # 59 | # [*runinterval*] 60 | # The runinterval variable in puppet.conf 61 | # Default: 1800 62 | # 63 | # [*method*] 64 | # The mechanism for performing puppet runs. 65 | # Supported methods: [cron, service, only_service, none] 66 | # Default: platform dependent 67 | # 68 | # [*manage_package*] 69 | # Whether to manage the puppet agent package or not 70 | # Default: true 71 | # 72 | # [*package*] 73 | # The puppet agent package name 74 | # Default: platform dependent 75 | # 76 | # == Example: 77 | # 78 | # class { 'puppet::agent': 79 | # server => 'puppet.example.com', 80 | # report_server => 'puppet_reports.example.com', 81 | # method => 'service', 82 | # } 83 | # 84 | class puppet::agent ( 85 | $ensure = 'present', 86 | $server = 'puppet', 87 | $ca_server = undef, 88 | $report = true, 89 | $report_server = undef, 90 | $manage_repos = false, 91 | $environment = $::environment, 92 | $pluginsync = true, 93 | $certname = $::clientcert, 94 | $show_diff = false, 95 | $splay = false, 96 | $configtimeout = 360, 97 | $usecacheonfailure = true, 98 | $runinterval = undef, 99 | $method = $puppet::params::default_method, 100 | $manage_package = true, 101 | $package = $puppet::params::agent_package, 102 | ) inherits puppet::params { 103 | 104 | validate_bool($report) 105 | validate_bool($manage_repos) 106 | validate_bool($pluginsync) 107 | validate_bool($show_diff) 108 | validate_bool($splay) 109 | validate_bool($usecacheonfailure) 110 | validate_bool($manage_package) 111 | 112 | include puppet 113 | 114 | if $manage_package { 115 | include puppet::package 116 | } 117 | 118 | if $ensure != 'absent' { 119 | include puppet::agent::config 120 | } 121 | 122 | case $method { 123 | cron: { 124 | include puppet::agent::cron 125 | class { 'puppet::agent::service': enable => false } 126 | } 127 | service: { 128 | include puppet::agent::service 129 | class { 'puppet::agent::cron': enable => false } 130 | } 131 | only_service: { 132 | include puppet::agent::service 133 | } 134 | none: { 135 | class { 'puppet::agent::service': enable => false } 136 | class { 'puppet::agent::cron': enable => false } 137 | } 138 | default: { 139 | notify { "Agent run method \"${method}\" is not supported by ${module_name}, defaulting to cron": loglevel => warning } 140 | include puppet::agent::cron 141 | class { 'puppet::agent::service': enable => false } 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /manifests/params.pp: -------------------------------------------------------------------------------- 1 | # Default puppet data 2 | class puppet::params { 3 | 4 | case $::operatingsystem { 5 | 'debian', 'ubuntu': { 6 | $os_specific = { 7 | # placeholder 8 | } 9 | } 10 | 'freebsd': { 11 | $os_specific = { 12 | puppet_cmd => '/usr/local/bin/puppet', 13 | master_package => 'puppet', 14 | agent_service_conf => undef, 15 | puppet_conf => '/usr/local/etc/puppet/puppet.conf', 16 | puppet_confdir => '/usr/local/etc/puppet', 17 | puppet_logdir => '/var/log/puppet', 18 | puppet_vardir => '/var/puppet', 19 | puppet_ssldir => '/var/puppet/ssl', 20 | } 21 | } 22 | 'darwin': { 23 | $os_specific = { 24 | puppet_cmd => '/usr/bin/puppet', 25 | agent_service => 'com.puppetlabs.puppet', 26 | master_package => '', 27 | master_service => '', 28 | puppet_ssldir => '/etc/puppet/ssl', 29 | } 30 | } 31 | 'centos', 'redhat', 'fedora', 'sles', 'opensuse', 'OracleLinux': { 32 | $os_specific = { 33 | agent_service_conf => '/etc/sysconfig/puppet', 34 | master_package => 'puppet-server', 35 | } 36 | } 37 | 'gentoo': { 38 | $os_specific = { 39 | agent_package => 'app-admin/puppet', 40 | master_package => 'app-admin/puppet', 41 | } 42 | } 43 | 'openbsd': { 44 | $os_specific = { 45 | puppet_cmd => '/usr/local/bin/puppet', 46 | agent_service => 'puppetd', 47 | agent_service_conf => undef, 48 | master_package => 'puppet', 49 | master_service => 'puppetmasterd', 50 | puppet_logdir => '/var/puppet/log', 51 | puppet_vardir => '/var/puppet', 52 | puppet_ssldir => '/etc/puppet/ssl', 53 | puppet_rundir => '/var/puppet/run', 54 | default_method => 'service', 55 | puppet_user => '_puppet', 56 | puppet_group => '_puppet', 57 | } 58 | } 59 | # This stops the puppet class breaking. But really, we only have very 60 | # limited support for Solaris. And only through OpenCSW 61 | # Taken from: '/opt/csw/bin/puppet config print ...' 62 | 'solaris','sunos': { 63 | $os_specific = { 64 | puppet_cmd => '/opt/csw/bin/puppet', 65 | puppet_ssldir => '/etc/puppet/ssl', 66 | puppet_rundir => '/var/lib/puppet/run/', 67 | agent_service => 'svc:/network/cswpuppetd', 68 | } 69 | } 70 | 'windows': { 71 | $os_specific = { 72 | puppet_cmd => 'C:/Program Files (x86)/Puppet Labs/Puppet/bin/puppet.bat', 73 | agent_package => 'Puppet', 74 | # Note this is only going to work for 2008 and later 75 | # The correct value is %APPDATA% (or something similar) 76 | puppet_conf => 'C:/ProgramData/PuppetLabs/puppet/etc/puppet.conf', 77 | puppet_confdir => 'C:/ProgramData/PuppetLabs/puppet/etc', 78 | puppet_logdir => 'C:/ProgramData/PuppetLabs/puppet/var/log', 79 | puppet_vardir => 'C:/ProgramData/PuppetLabs/puppet/var', 80 | puppet_ssldir => 'C:/ProgramData/PuppetLabs/puppet/etc/ssl', 81 | puppet_rundir => 'C:/ProgramData/PuppetLabs/puppet/var/run', 82 | default_method => 'only_service', 83 | } 84 | } 85 | 'CumulusLinux': { 86 | $os_specific = { 87 | puppet_cmd => '/opt/puppetlabs/bin/puppet', 88 | puppet_conf => '/etc/puppetlabs/puppet/puppet.conf', 89 | puppet_confdir => '/etc/puppetlabs/puppet', 90 | puppet_logdir => '/var/log/puppetlabs/puppet', 91 | puppet_rundir => '/var/run/puppetlabs', 92 | puppet_ssldir => '/opt/puppetlabs/puppet/ssl', 93 | puppet_vardir => '/opt/puppetlabs/puppet/lib', 94 | } 95 | } 96 | default: { fail("Sorry, ${::operatingsystem} is not supported") } 97 | } 98 | 99 | $default_value = { 100 | agent_package => 'puppet', 101 | agent_service => 'puppet', 102 | agent_service_conf => '/etc/default/puppet', 103 | default_method => 'cron', 104 | master_package => 'puppetmaster', 105 | master_service => 'puppetmaster', 106 | puppet_cmd => '/usr/bin/puppet', 107 | puppet_conf => '/etc/puppet/puppet.conf', 108 | puppet_confdir => '/etc/puppet', 109 | puppet_logdir => '/var/log/puppet', 110 | puppet_rundir => '/var/run/puppet', 111 | puppet_ssldir => '/var/lib/puppet/ssl', 112 | puppet_user => 'puppet', 113 | puppet_group => 'puppet', 114 | puppet_vardir => '/var/lib/puppet', 115 | report_dir => '/usr/lib/ruby/vendor_ruby/puppet/reports', 116 | } 117 | 118 | $merged_values = merge($default_value, $os_specific) 119 | 120 | $agent_package = $merged_values[agent_package] 121 | $agent_service = $merged_values[agent_service] 122 | $agent_service_conf = $merged_values[agent_service_conf] 123 | $agent_use = $merged_values[agent_use] 124 | $default_method = $merged_values[default_method] 125 | $master_package = $merged_values[master_package] 126 | $master_service = $merged_values[master_service] 127 | $master_use = $merged_values[master_use] 128 | $puppet_cmd = $merged_values[puppet_cmd] 129 | $puppet_conf = $merged_values[puppet_conf] 130 | $puppet_confdir = $merged_values[puppet_confdir] 131 | $puppet_group = $merged_values[puppet_group] 132 | $puppet_logdir = $merged_values[puppet_logdir] 133 | $puppet_rundir = $merged_values[puppet_rundir] 134 | $puppet_ssldir = $merged_values[puppet_ssldir] 135 | $puppet_user = $merged_values[puppet_user] 136 | $puppet_vardir = $merged_values[puppet_vardir] 137 | 138 | } 139 | -------------------------------------------------------------------------------- /files/reports/xmpp.rb: -------------------------------------------------------------------------------- 1 | require 'puppet' 2 | require 'yaml' 3 | require 'json' 4 | require 'httparty' 5 | require 'time' 6 | require 'socket' 7 | 8 | begin 9 | require 'xmpp4r/client' 10 | include Jabber 11 | rescue LoadError => e 12 | Puppet.info "You need the `xmpp4r` gem to use the XMPP report" 13 | end 14 | 15 | # Dirty class with no timeotus, or config, or pretty much anything really. 16 | class IRCKitten 17 | def self.message( string ) 18 | begin 19 | socket ||= TCPSocket.new('jotunn.puppetlabs.lan', 12345) 20 | socket.send( string , 0 ) 21 | socket.close 22 | rescue => e 23 | Puppet.debug "Failed to IRCCat because of #{e}" 24 | end 25 | end 26 | end 27 | 28 | 29 | 30 | Puppet::Reports.register_report(:xmpp) do 31 | 32 | desc <<-DESC 33 | Send notification of failed reports to an XMPP user. 34 | DESC 35 | 36 | def find_node( node_name , dashboard ) 37 | 38 | url = nil 39 | JSON.parse( HTTParty.get( "#{dashboard}/nodes.json" ).response.body ).each do |node| 40 | return "#{dashboard}/nodes/#{node['id']}".gsub( /[^:]\/\/+/ , '/' ) if node['name'] == node_name 41 | end 42 | 43 | return false 44 | end 45 | 46 | 47 | def find_report( node_name , dashboard ) 48 | 49 | JSON.parse( HTTParty.get( "#{dashboard}/nodes.json" ).response.body ).each do |node| 50 | return "#{dashboard}/reports/#{node['last_apply_report_id']}" if node['name'] == node_name and node['status'] == 'failed' 51 | end 52 | 53 | # If not, just return the node list. 54 | return "#{dashboard}/nodes/#{node_name}" 55 | end 56 | 57 | 58 | def getconfig 59 | configs = {} 60 | configfile = File.join([File.dirname(Puppet.settings[:config]), "xmpp.yaml"]) 61 | raise(Puppet::ParseError, "XMPP report config file #{configfile} not readable") unless File.exist?(configfile) 62 | 63 | config = YAML.load_file(configfile) 64 | 65 | return config 66 | 67 | # not used any more. 68 | # XMPP_JID = config[:xmpp_jid] 69 | # XMPP_PASSWORD = config[:xmpp_password] 70 | # XMPP_TARGET = config[:xmpp_target] 71 | # DASHBOARD_URL = config[:dashboard].chomp('/') # remove trailing slash. 72 | end 73 | 74 | def process 75 | 76 | # If you want to debug this... 77 | Puppet.warning "xmpp-debug: There's a status for #{self.host} to XMPP in env \"#{self.environment}\" which is status #{self.status}" 78 | 79 | # We can get the SHA out of our report (we use the git SHA as the 80 | # version, thanks Cody!) 81 | commit_string = '' 82 | sha = self.configuration_version 83 | if sha =~ /^[0-9a-zA-Z]+$/ 84 | commit_string = " see http://git.io/plmc for #{sha}" 85 | Puppet.debug "xmpp-debug: we has commit string #{sha}" 86 | else 87 | Puppet.warning "xmpp-debug: no usable configuration version string of '#{sha}' for #{self.host}" 88 | end 89 | 90 | # Don't alert on weekends. 91 | day = Time.now.wday 92 | if day == 0 or day == 6 # Sat or Sun 93 | return 94 | end 95 | 96 | # If we ctrl-c'ed then don't bother alerting!! 97 | begin 98 | if self.logs.last.message == 'Caught INT; calling stop' 99 | Puppet.warning "xmpp-debug: Am not telling you about #{self.host} as you CTRL-Ced it." 100 | return 101 | end 102 | rescue NameError => e 103 | # I am here in case it doesn't exist. 104 | end 105 | 106 | # Go through all the log messages and check their message for an 107 | # environment. Sketch.. 108 | # "Could not retrieve catalog from remote server: Error 400 on SERVER: 109 | # validate_re(): wrong number of arguments (3; must be 2) at 110 | # /etc/puppet/environments/doing_ldap_not_badly/sherwood/apacheds/manifests/config.pp:14 111 | # on node yellow.dc1.puppetlabs.net" 112 | # For example. 113 | self.logs.each do |log| 114 | if log.message =~ /Could not retrieve catalog from remote server: Error 400 on SERVER: .* \/etc\/puppet\/environments\/(\w+)\// 115 | env = $1 116 | if env != 'production' 117 | Puppet.warning "xmpp-debug: Ignoring #{self.host} as it's technically in #{env} environment." 118 | return 119 | end 120 | end 121 | end 122 | 123 | 124 | if self.status == 'failed' 125 | 126 | # get the config every time, so we don't have to restart it to add 127 | # users/ignored hosts. 128 | c = self.getconfig 129 | 130 | # Remove any trailing slashes on the URL, so we can join it with 131 | # '/path/' later on. 132 | c[:dashboard].chomp!( '/' ) 133 | 134 | 135 | # If it's an ignored host, don't bother beyond here. 136 | return if c[:ignore_hosts].include? self.host 137 | 138 | 139 | # Now set us up some Jabber 140 | jid = JID::new(c[:xmpp_jid]) 141 | cl = Client::new(jid) 142 | cl.connect 143 | cl.auth(c[:xmpp_password]) 144 | 145 | # host = find_node( self.host , DASHBOARD_URL ) 146 | # host = "#{c[:dashboard].chomp('/')}/nodes/#{self.host}" 147 | dashboard_report_url = find_report( self.host , c[:dashboard] ) 148 | 149 | # Thanks to https://projects.puppetlabs.com/issues/10064 we now have an 150 | # environment to check against. 151 | # 152 | # Need the nil? for things that break before sending their env. 153 | if self.environment.nil? or self.environment == 'production' 154 | 155 | body = "Puppet #{self.status} for #{self.host} #{dashboard_report_url}#{commit_string}" 156 | 157 | c[:xmpp_target].split(',').each do |target| 158 | Puppet.debug "Sending status for #{self.host} to XMMP user #{target}" 159 | m = Message::new(target, body).set_type(:normal).set_id('1').set_subject("Puppet run failed!") 160 | cl.send m 161 | 162 | end 163 | 164 | # Yeah, don't do IRC in the loop, as then you get N messages. 165 | IRCKitten::message( body ) 166 | 167 | end 168 | end 169 | end 170 | 171 | end 172 | -------------------------------------------------------------------------------- /manifests/server/config.pp: -------------------------------------------------------------------------------- 1 | # Private class 2 | class puppet::server::config { 3 | 4 | include puppet 5 | 6 | Ini_setting { 7 | path => $puppet::conf, 8 | ensure => 'present', 9 | section => 'master', 10 | notify => Service[$puppet::server::service], 11 | } 12 | 13 | if $puppet::server::directoryenvs == true { 14 | if $puppet::server::environmentpath { 15 | validate_string($puppet::server::environmentpath) 16 | $environmentpath_ensure = 'present' 17 | } else { 18 | $environmentpath_ensure = 'absent' 19 | } 20 | 21 | if ! empty($puppet::server::basemodulepath) { 22 | $basemodulepath_ensure = 'present' 23 | } else { 24 | $basemodulepath_ensure = 'absent' 25 | } 26 | 27 | if $puppet::server::default_manifest { 28 | validate_string($puppet::server::default_manifest) 29 | $default_manifest_ensure = 'present' 30 | } else { 31 | $default_manifest_ensure = 'absent' 32 | } 33 | $manifest_ensure = 'absent' 34 | $modulepath_ensure = 'absent' 35 | $config_version_ensure = 'absent' 36 | } else { 37 | if $puppet::server::manifest { 38 | validate_string($puppet::server::manifest) 39 | $manifest_ensure = 'present' 40 | } else { 41 | $manifest_ensure = 'absent' 42 | } 43 | 44 | if ! empty($puppet::server::modulepath) { 45 | $modulepath_ensure = 'present' 46 | } else { 47 | $modulepath_ensure = 'absent' 48 | } 49 | 50 | if $puppet::server::config_version { 51 | validate_string($puppet::server::config_version) 52 | $config_version_ensure = 'present' 53 | } else { 54 | $config_version_ensure = 'absent' 55 | } 56 | 57 | $environmentpath_ensure = 'absent' 58 | $basemodulepath_ensure = 'absent' 59 | $default_manifest_ensure = 'absent' 60 | } 61 | 62 | ini_setting { 'environmentpath': 63 | ensure => $environmentpath_ensure, 64 | setting => 'environmentpath', 65 | value => $puppet::server::environmentpath, 66 | } 67 | 68 | ini_setting { 'basemodulepath': 69 | ensure => $basemodulepath_ensure, 70 | setting => 'basemodulepath', 71 | value => join(flatten([$puppet::server::basemodulepath]), ':'), 72 | } 73 | 74 | ini_setting { 'default_manifest': 75 | ensure => $default_manifest_ensure, 76 | setting => 'default_manifest', 77 | value => $puppet::server::default_manifest, 78 | } 79 | 80 | ini_setting { 'modulepath': 81 | ensure => $modulepath_ensure, 82 | setting => 'modulepath', 83 | value => join(flatten([$puppet::server::modulepath]), ':'), 84 | } 85 | 86 | ini_setting { 'manifest': 87 | ensure => $manifest_ensure, 88 | setting => 'manifest', 89 | value => $puppet::server::manifest, 90 | } 91 | 92 | ini_setting { 'config_version': 93 | ensure => $config_version_ensure, 94 | setting => 'config_version', 95 | value => $puppet::server::config_version, 96 | } 97 | 98 | ini_setting { 'user': 99 | setting => 'user', 100 | value => $puppet::user, 101 | } 102 | 103 | ini_setting { 'group': 104 | setting => 'group', 105 | value => $puppet::group, 106 | } 107 | 108 | ini_setting { 'stringify_facts_master': 109 | setting => 'stringify_facts', 110 | section => 'main', 111 | value => $puppet::server::stringify_facts, 112 | } 113 | 114 | ini_setting { 'ca': 115 | setting => 'ca', 116 | value => $puppet::server::ca, 117 | } 118 | 119 | if $puppet::server::servertype == 'standalone' and $puppet::server::bindaddress { 120 | $bindaddress_ensure = 'present' 121 | } else { 122 | $bindaddress_ensure = 'absent' 123 | } 124 | 125 | ini_setting { 'bindaddress': 126 | ensure => $bindaddress_ensure, 127 | setting => 'bindaddress', 128 | value => $puppet::server::bindaddress, 129 | } 130 | 131 | if $puppet::server::ssl_client_header { 132 | $ssl_client_ensure = 'present' 133 | } else { 134 | $ssl_client_ensure = 'absent' 135 | } 136 | 137 | ini_setting { 'ssl_client_header': 138 | ensure => $ssl_client_ensure, 139 | setting => 'ssl_client_header', 140 | value => $puppet::server::ssl_client_header, 141 | } 142 | 143 | ini_setting { 'ssl_client_verify_header': 144 | ensure => $ssl_client_ensure, 145 | setting => 'ssl_client_verify_header', 146 | value => $puppet::server::ssl_client_verify_header, 147 | } 148 | 149 | if ! empty($puppet::server::reports) { 150 | $reports_ensure = 'present' 151 | } else { 152 | $reports_ensure = 'absent' 153 | } 154 | 155 | ini_setting { 'reports': 156 | ensure => $reports_ensure, 157 | setting => 'reports', 158 | value => join(flatten([ $puppet::server::reports ]), ', '), 159 | } 160 | 161 | if $puppet::server::reporturl { 162 | $reporturl_ensure = 'present' 163 | } else { 164 | $reporturl_ensure = 'absent' 165 | } 166 | 167 | ini_setting { 'reporturl': 168 | ensure => $reporturl_ensure, 169 | setting => 'reporturl', 170 | value => $puppet::server::reporturl, 171 | } 172 | 173 | if $puppet::server::reportfrom { 174 | $reportfrom_ensure = 'present' 175 | } else { 176 | $reportfrom_ensure = 'absent' 177 | } 178 | 179 | ini_setting { 'reportfrom': 180 | ensure => $reportfrom_ensure, 181 | setting => 'reportfrom', 182 | value => $puppet::server::reportfrom, 183 | } 184 | 185 | if $puppet::server::enc == 'exec' { 186 | $enc_ensure = 'present' 187 | } else { 188 | $enc_ensure = 'absent' 189 | } 190 | 191 | ini_setting { 'node_terminus': 192 | ensure => $enc_ensure, 193 | setting => 'node_terminus', 194 | value => 'exec', 195 | } 196 | 197 | ini_setting { 'external_nodes': 198 | ensure => $enc_ensure, 199 | setting => 'external_nodes', 200 | value => $puppet::server::enc_exec, 201 | } 202 | 203 | if $puppet::server::parser { 204 | $parser_ensure = 'present' 205 | } else { 206 | $parser_ensure = 'absent' 207 | } 208 | 209 | ini_setting { 'parser': 210 | ensure => $parser_ensure, 211 | setting => 'parser', 212 | value => $puppet::server::parser, 213 | } 214 | 215 | if ! empty($puppet::server::dns_alt_names) { 216 | $dns_alt_names_ensure = 'present' 217 | } else { 218 | $dns_alt_names_ensure = 'absent' 219 | } 220 | 221 | ini_setting { 'dns_alt_names': 222 | ensure => $dns_alt_names_ensure, 223 | setting => 'dns_alt_names', 224 | value => join(flatten([ $puppet::server::dns_alt_names ]), ', ') 225 | } 226 | 227 | if $puppet::server::autosign { 228 | $autosign_ensure = 'present' 229 | } else { 230 | $autosign_ensure = 'absent' 231 | } 232 | 233 | ini_setting { 'autosign': 234 | ensure => $autosign_ensure, 235 | setting => 'autosign', 236 | value => $puppet::server::autosign, 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /files/reports/irccat.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'puppet' 3 | require 'yaml' 4 | require 'json' 5 | require 'httparty' 6 | require 'time' 7 | require 'socket' 8 | 9 | 10 | Puppet::Reports.register_report(:irccat) do 11 | 12 | desc <<-DESC 13 | Send notification of failed reports to an irccat server. 14 | DESC 15 | 16 | def irccatter( string , server , port ) 17 | begin 18 | socket ||= TCPSocket.new( server , port ) 19 | socket.send( string , 0 ) 20 | socket.close 21 | rescue => e 22 | Puppet.debug "Failed to IRCCat because of #{e}" 23 | end 24 | end 25 | 26 | def find_node( node_name , dashboard ) 27 | 28 | JSON.parse( HTTParty.get( "#{dashboard}/nodes.json" ).response.body ).each do |node| 29 | return "#{dashboard}/nodes/#{node['id']}".gsub( /[^:]\/\/+/ , '/' ) if node['name'] == node_name 30 | end 31 | 32 | return false 33 | end 34 | 35 | 36 | def find_report( node_name , dashboard ) 37 | 38 | JSON.parse( HTTParty.get( "#{dashboard}/nodes.json" ).response.body ).each do |node| 39 | return "#{dashboard}/reports/#{node['last_apply_report_id']}" if node['name'] == node_name and node['status'] == 'failed' 40 | end 41 | 42 | # If not, just return the node list. 43 | return "#{dashboard}/nodes/#{node_name}" 44 | end 45 | 46 | 47 | def getconfig 48 | config = {} 49 | configfile = File.join([File.dirname(Puppet.settings[:config]), "irccat.yaml"]) 50 | raise(Puppet::ParseError, "irccat report config file #{configfile} not readable") unless File.exist?(configfile) 51 | 52 | config = YAML.load_file(configfile) 53 | 54 | # Remove any trailing slashes on the URL, so we can join it with 55 | # '/path/' later on. 56 | config[:dashboard].chomp!( '/' ) 57 | 58 | # there are less ugly ways to do defaults. 59 | config[:irccatport] = 12345 unless config[:irccatport] 60 | 61 | return config 62 | end 63 | 64 | def process 65 | 66 | # Some variables we'd like to use later. 67 | puppetdb_host = nil 68 | puppetdb_error = false 69 | git_error = false 70 | git_msg = nil 71 | 72 | # get the config every time, so we don't have to restart it to add 73 | # users/ignored hosts. 74 | c = self.getconfig 75 | 76 | 77 | # If you want to debug this... 78 | Puppet.warning "irccat-debug: There's a status for #{self.host} to irccat in env \"#{self.environment}\" which is status #{self.status}" 79 | 80 | # We can get the SHA out of our report (we use the git SHA as the 81 | # version, thanks Cody!) 82 | if c[:githuburl] and not c[:githuburl].nil? 83 | commit_string = '' 84 | sha = self.configuration_version 85 | if sha =~ /^[0-9a-zA-Z]+$/ 86 | commit_string = " see #{c[:githuburl]} for #{sha}" 87 | Puppet.debug "irccat-debug: we has commit string #{sha}" 88 | else 89 | Puppet.warning "irccat-debug: no usable configuration version string of '#{sha}' for #{self.host}" 90 | end 91 | end 92 | 93 | # Don't alert on weekends. 94 | day = Time.now.wday 95 | if day == 0 or day == 6 # Sat or Sun 96 | return 97 | end 98 | 99 | 100 | # If it's an ignored host, don't bother beyond here. 101 | return if c[:ignore_hosts].include? self.host 102 | 103 | 104 | # If we ctrl-c'ed then don't bother alerting!! 105 | begin 106 | if self.logs.last.message == 'Caught INT; calling stop' 107 | Puppet.warning "irccat-debug: Am not telling you about #{self.host} as you CTRL-Ced it." 108 | return 109 | end 110 | rescue NameError => e 111 | # I am here in case it doesn't exist. 112 | Puppet.warning "Failed to tell you something because of #{e}" 113 | end 114 | 115 | # Go through all the log messages and check their message for an 116 | # environment. Sketch.. 117 | # "Could not retrieve catalog from remote server: Error 400 on SERVER: 118 | # validate_re(): wrong number of arguments (3; must be 2) at 119 | # /etc/puppet/environments/flowers/sherwood/apacheds/manifests/config.pp:14 120 | # on node buttons.puppetlabs.net" 121 | # For example. 122 | self.logs.each do |log| 123 | 124 | # Just keep adding reasons to fail here. 125 | case 126 | when log.message =~ /Could not retrieve catalog from remote server: Error 400 on SERVER: .* \/etc\/puppet\/environments\/(\w+)\// 127 | env = $1 128 | when log.message =~ /change from \w+ to \w+ failed: Could not update: undefined method .* for .* at \/etc\/puppet\/environments\/(\w+)\// 129 | env = $1 130 | when log.message =~ /Could not retrieve catalog from remote server: Error 400 on SERVER: Could not find template .* at \/etc\/puppet\/environments\/(\w+)\// 131 | env = $1 132 | # This is a temp (HAH) hack to save our channel. Too much noise. 133 | when log.message =~ /Could not evaluate: No route to host - connect/ 134 | Puppet.warning "irccat-debug: Ignoring #{self.host} as routing is broken and you know this already." 135 | return 136 | when log.message =~ /eval_generate: getaddrinfo: hostname nor servname provided, or not known/ 137 | Puppet.warning "irccat-debug: Ignoring #{self.host} as resolving DNS is broken and you know this already." 138 | return 139 | when log.message =~ /Could not retrieve catalog from remote server: Error 400 on SERVER: .+ command for .+ to PuppetDB at (.+):\d+: / 140 | puppetdb_host = $1 141 | puppetdb_error = true 142 | Puppet.warning "irccat-debug: I think PuppetDB is broken on #{puppetdb_host}" 143 | break 144 | when log.message =~ /Could not evaluate: Execution of '\/[a-z\/]+\/bin\/git fetch origin' returned 128: (ssh_exchange_identification|fatal): (.*)\Z/ 145 | git_error = true 146 | git_msg = $2 147 | Puppet.warning "irccat-debug: I think GitHub (or our git repo) is broken with #{git_msg} from #{self.host}" 148 | break 149 | else 150 | # Assume it's production otherwise. 151 | env = 'production' 152 | end 153 | 154 | if env != 'production' 155 | Puppet.warning "irccat-debug: Ignoring #{self.host} as it's technically in #{env} environment." 156 | return 157 | end 158 | 159 | end 160 | 161 | 162 | if self.status == 'failed' 163 | 164 | dashboard_report_url = find_report( self.host , c[:dashboard] ) 165 | 166 | # Due to github breaking, I'm now doing the same for the hubs. 167 | if git_error == true 168 | # Trim it, in case it's a long error... 169 | git_msg = git_msg.slice(0..21) + '...' if git_msg.length > 24 170 | body = "I think GitHub (or our git repo) is broken with '#{git_msg}' from #{self.host}. Check #{dashboard_report_url}" 171 | irccatter( body , c[:irccathost] , c[:irccatport] ) 172 | return 173 | end 174 | 175 | 176 | # Is puppetdb possibly down? 177 | if puppetdb_error == true 178 | irccatter( "I think PuppetDB is dead on #{puppetdb_host} for #{self.host}, see #{dashboard_report_url}" , c[:irccathost] , c[:irccatport] ) 179 | return 180 | end 181 | 182 | # Thanks to https://projects.puppetlabs.com/issues/10064 we now have an 183 | # environment to check against. 184 | if self.environment.nil? or self.environment == 'production' 185 | 186 | body = "Puppet #{self.status} for #{self.host} #{dashboard_report_url}#{commit_string}" 187 | 188 | irccatter( body , c[:irccathost] , c[:irccatport] ) 189 | 190 | end 191 | end 192 | end 193 | 194 | end 195 | -------------------------------------------------------------------------------- /manifests/server.pp: -------------------------------------------------------------------------------- 1 | # == Class: puppet::server 2 | # 3 | # This class installs and configures a Puppet master 4 | # 5 | # == Description 6 | # 7 | # This class implements a Puppet master based around the dynamic environments 8 | # workflow descripted in http://puppetlabs.com/blog/git-workflow-and-puppet-environments/ 9 | # 10 | # == Parameters 11 | # 12 | # [*autosign*] 13 | # The autosign variable in puppet.conf 14 | # Default: undef 15 | # 16 | # [*bindaddress*] 17 | # The bindaddress variable in puppet.conf 18 | # Default: 0.0.0.0 19 | # 20 | # [*ca*] 21 | # The ca variable in puppet.conf 22 | # Default: false 23 | # 24 | # [*config_version*] 25 | # The config_version variable in puppet.conf 26 | # Default: /usr/bin/git --git-dir $confdir/environments/$environment/.git rev-parse --short HEAD 2>/dev/null || echo 27 | # 28 | # [*dns_alt_names*] 29 | # The dns_alt_names variable in puppet.conf 30 | # Default: undef 31 | # 32 | # [*enc*] 33 | # ?? 34 | # Default: empty string 35 | # 36 | # [*enc_exec*] 37 | # ?? 38 | # Default: empty string 39 | # 40 | # [*ensure*] 41 | # The ensure value for the puppet master package 42 | # Default: present 43 | # 44 | # [*directoryenvs*] 45 | # Whether we should be using directory environments 46 | # Default: true 47 | # 48 | # [*environmentpath*] 49 | # The environmentpath variable in puppet.conf 50 | # Default: undef 51 | # 52 | # [*basemodulepath*] 53 | # The basemodulepath variable in puppet.conf 54 | # Default: empty list 55 | # 56 | # [*default_manifest*] 57 | # The default_manifest variable in puppet.conf 58 | # Default: undef 59 | # 60 | # [*manage_package*] 61 | # Whether to manage the puppet master package 62 | # Default: true 63 | # 64 | # [*manifest*] 65 | # The manifest variable in puppet.conf 66 | # Default: $confdir/modules/site/site.pp 67 | # 68 | # [*modulepath*] 69 | # The modulepath variable in puppet.conf 70 | # Default: empty list 71 | # 72 | # [*parser*] 73 | # ?? 74 | # Default: undef 75 | # 76 | # [*manage_puppetdb*] 77 | # Whether to manage puppetdb through puppetlabs/puppetdb module 78 | # Default: false 79 | # 80 | # [*report_dir*] 81 | # The report_dir variable in puppet.conf 82 | # Default: platform dependent 83 | # 84 | # [*reportfrom*] 85 | # The reportfrom variable in puppet.conf 86 | # Default: undef 87 | # 88 | # [*reports*] 89 | # The reports variable from puppet.conf 90 | # Default: ['store', 'https'] 91 | # 92 | # [*reporturl*] 93 | # The reporturl variable in puppet.conf 94 | # Default: https://${::fqdn}/reports 95 | # 96 | # [*servername*] 97 | # The Puppet Master's name, used for the web servers that serve puppetmaster 98 | # Default: $::fqdn 99 | # 100 | # [*serverssl_ciphers*] 101 | # SSL ciphers to enable on the web servers that serve puppetmaster 102 | # Default: application dependent 103 | # 104 | # [*serverssl_protos*] 105 | # SSL protocols to enable on the web servers that serve puppetmaster 106 | # Default: application dependent 107 | # 108 | # [*servertype*] 109 | # The web server to choose for serving the puppetmaster 110 | # Default: unicorn 111 | # 112 | # [*storeconfigs*] 113 | # The storeconfigs backend 114 | # Default: undef 115 | # 116 | # [*package*] 117 | # The puppetmaster package name 118 | # Default: platform dependent 119 | # 120 | # == Example 121 | # Sample Usage: 122 | # 123 | # $modulepath = [ 124 | # "/etc/puppet/modules/site", 125 | # "/etc/puppet/modules/dist", 126 | # ] 127 | # 128 | # class { "puppet::server": 129 | # modulepath => inline_template("<%= modulepath.join(':') %>"), 130 | # reporturl => "https://dashboard.puppetlabs.com/reports"; 131 | # } 132 | # 133 | class puppet::server ( 134 | $autosign = undef, 135 | $bindaddress = '0.0.0.0', 136 | $ca = false, 137 | $config_version = '/usr/bin/git --git-dir $confdir/environments/$environment/.git rev-parse --short HEAD 2>/dev/null || echo', 138 | $dns_alt_names = [], 139 | $enc = '', 140 | $enc_exec = '', 141 | $ensure = 'present', 142 | $directoryenvs = true, 143 | $environmentpath = '$confdir/environments', 144 | $basemodulepath = [], 145 | $default_manifest = undef, 146 | $manage_package = true, 147 | $manifest = undef, 148 | $modulepath = [], 149 | $parser = undef, 150 | $manage_puppetdb = false, 151 | $report_dir = $puppet::params::report_dir, 152 | $reportfrom = undef, 153 | $reports = ['store', 'https'], 154 | $reporturl = "https://${::fqdn}/reports", 155 | $servername = $::fqdn, 156 | $serverssl_ciphers = undef, 157 | $serverssl_protos = undef, 158 | $servertype = 'unicorn', 159 | $storeconfigs = undef, 160 | $package = $puppet::params::master_package, 161 | $tagmail = {}, 162 | $external_ca = undef, 163 | ) inherits puppet::params { 164 | 165 | validate_bool($ca) 166 | validate_bool($directoryenvs) 167 | validate_bool($manage_puppetdb) 168 | if $dns_alt_names { validate_array($dns_alt_names) } 169 | if $reports { validate_array($reports) } 170 | if $parser { validate_re($parser, ['custom', 'future']) } 171 | if $tagmail { validate_hash($tagmail) } 172 | 173 | $service = $servertype ? { 174 | 'passenger' => 'httpd', 175 | 'unicorn' => 'unicorn_puppetmaster', 176 | 'standalone' => $puppet::params::master_service, 177 | } 178 | 179 | include puppet 180 | include puppet::server::config 181 | 182 | if $manage_package and ($puppet::agent::package != $package) { 183 | package { $package: 184 | ensure => $ensure, 185 | notify => Service[$service], 186 | } 187 | } 188 | 189 | # --- 190 | # The site.pp is set in the puppet.conf, remove site.pp here to avoid confusion. 191 | # Unless the manifest that was passed in is the default site.pp. 192 | if ($manifest != "${puppet::params::puppet_confdir}/manifests/site.pp") { 193 | file { "${puppet::params::puppet_confdir}/manifests/site.pp": ensure => absent; } 194 | } 195 | 196 | # --- 197 | # Application-server specific SSL configuration 198 | case $servertype { 199 | 'passenger': { 200 | include puppet::server::passenger 201 | $ssl_client_header = 'SSL_CLIENT_S_DN' 202 | $ssl_client_verify_header = 'SSL_CLIENT_VERIFY' 203 | $ssl_protocols = pick($serverssl_protos, '-ALL +TLSv1.2 +TLSv1.1 +TLSv1 +SSLv3') 204 | $ssl_ciphers = pick($serverssl_ciphers, 'ALL:!ADH:!EXP:!LOW:+RC4:+HIGH:+MEDIUM:!SSLv2:+SSLv3:+TLSv1:+eNULL') 205 | } 206 | 'unicorn': { 207 | include puppet::server::unicorn 208 | $ssl_client_header = 'HTTP_X_CLIENT_DN' 209 | $ssl_client_verify_header = 'HTTP_X_CLIENT_VERIFY' 210 | $ssl_protocols = pick($serverssl_protos, 'TLSv1.2 TLSv1.1 TLSv1 SSLv3') 211 | $ssl_ciphers = pick($serverssl_ciphers, 'HIGH:!aNULL:!MD5') 212 | } 213 | 'standalone': { 214 | include puppet::server::standalone 215 | } 216 | default: { 217 | err('Only "passenger", "unicorn" and "standalone" are valid options for servertype') 218 | fail('Servertype "$servertype" not implemented') 219 | } 220 | } 221 | 222 | # --- 223 | # Storeconfigs 224 | if $storeconfigs { 225 | notify { 'storeconfigs is deprecated. Use manage_puppetdb setting.': } 226 | class { 'puppet::storeconfig': 227 | backend => $storeconfigs, 228 | } 229 | } 230 | 231 | # enable basic puppetdb using the puppetlabs-puppetdb module 232 | # this will also install postgresql 233 | # for more detailed control over puppetdb settings, use the puppetdb 234 | # module directly rather than having puppet-puppet include it. 235 | if $manage_puppetdb { 236 | include puppetdb 237 | include puppetdb::master::config 238 | } 239 | 240 | if ! empty($tagmail) { 241 | file { "${puppet::confdir}/tagmail.conf": 242 | ensure => file, 243 | owner => $puppet::user, 244 | group => $puppet::group, 245 | mode => '0644', 246 | content => template('puppet/tagmail.conf.erb'), 247 | } 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Puppet-puppet 2 | [![Build Status](https://travis-ci.org/puppetlabs-operations/puppet-puppet.svg?branch=master)](https://travis-ci.org/puppetlabs-operations/puppet-puppet) 3 | 4 | 100% free range, organic, pesticide free Puppet module for managing Puppet. 5 | 6 | #### Table of Contents 7 | 8 | 1. [Overview](#overview) 9 | 2. [Module Description - What puppet-puppet does and why it is useful](#module-description) 10 | 3. [Setup - getting started with puppet-puppet](#setup) 11 | * [What puppet-puppet affects](#what-puppet-puppet-affects) 12 | * [Setup requirements](#setup-requirements) 13 | 4. [Usage - Configuration options and additional functionality](#usage) 14 | * [Puppet Master](#puppetmaster-setup) 15 | * [Puppet Agent](#puppet-agent-configuration) 16 | 5. [Limitations - OS compatibility, etc.](#limitations) 17 | 6. [Development - Guide for contributing to puppet-puppet](#development) 18 | 19 | ## Overview 20 | 21 | The puppet-puppet module manages puppet masters and agents using puppet. 22 | 23 | ## Module Description 24 | 25 | Puppet masters are frequently the only hand-crafted part of puppet-managed 26 | infrastructure. This module seeks to make the experience of running a puppet 27 | master similar to running Apache, Nginx, or MySQL using puppet. 28 | 29 | ## Setup 30 | 31 | ### What puppet-puppet affects 32 | 33 | Depending on how you use this module, it can touch any of: 34 | * Puppet configuration files 35 | * Nginx or Apache configurations needed for running a master 36 | * Unicorn and Passenger configurations and init scripts 37 | * PuppetDB and PostgreSQL 38 | 39 | As far as possible, this module tries to use other general-purpose modules to 40 | configure required non-puppet systems. 41 | 42 | ### Setup Requirements 43 | 44 | Puppet-puppet does not manage SSL certificates. You can generate the 45 | appropriate puppet SSL certificates by starting the webrick-based puppetmaster 46 | before using puppet-puppet. If you don't generate those SSL certs first, the 47 | resulting master won't work. (but should if you generate the certs; it's not 48 | strictly order dependent) 49 | 50 | This module also doesn't manage [r10k][r10k] or [hiera][hiera-docs]. 51 | Look at [zack/r10k][zack-r10k] or [sharpie/r10k][sharpie-r10k] for r10k, and 52 | the [hunner/hiera][hunner-hiera] module for managing hiera. If this is all 53 | unfamiliar, read the [Shit Gary Says](http://garylarizza.com/) blog, starting 54 | with [Building a Functional Puppet Workflow Part 1: Module Structure][sgs-1]. 55 | 56 | ## Usage 57 | There are two general areas managed with this module: masters and agents. 58 | 59 | ### Puppetmaster Setup 60 | #### Webrick master 61 | 62 | At an absolute minimum, you need the following. 63 | 64 | ```puppet 65 | class { 'puppet::server': 66 | servertype => 'standalone', 67 | manifest => '/etc/puppet/manifests/site.pp', 68 | ca => true, 69 | } 70 | ``` 71 | 72 | This should get you a puppetmaster running under `webrick` which might scale to 73 | about `10` nodes if the wind doesn't blow too hard. 74 | 75 | If, however, the moon is in the next phase then you probably want to use 76 | something that scales a bit more. Your options are nginx/unicorn or 77 | apache/passenger. 78 | 79 | #### Nginx/Unicorn Master 80 | The most basic setup would look something like: 81 | ```puppet 82 | class { 'puppet::server': 83 | servertype => 'unicorn', 84 | ca => true, 85 | } 86 | ``` 87 | 88 | A similar Apache/Passenger server would be: 89 | ```puppet 90 | class { 'puppet::server': 91 | servertype => 'passenger', 92 | ca => true, 93 | } 94 | ``` 95 | 96 | #### Certificate authority proxy configuration 97 | 98 | If you want to automatically relay the certificate requests to an other CA you can do the following : 99 | ```puppet 100 | class { 'puppet::server': 101 | servertype => 'unicorn', 102 | ca => false, 103 | external_ca => 'https://my_puppet_ca_server:8140', 104 | } 105 | ``` 106 | 107 | NB: This is only implemented for Nginx/Unicorn configuration so far. 108 | 109 | #### Master with PuppetDB, PostgreSQL, and reports 110 | Running a puppet master without PuppetDB loses much of the utility of Puppet, 111 | so you probably want it. As a convenience, this module will install puppetdb 112 | and postgresql using the [puppetlabs/puppetdb][puppetlabs-puppetdb] module if 113 | `manage_puppetdb => true` is set. 114 | 115 | If you want a more complex setup with PuppetDB and/or PostgreSQL on a different 116 | server, don't enable that option; use the 117 | [puppetlabs/puppetdb][puppetlabs-puppetdb] module directly because it has many 118 | more configuration options that aren't exposed here. 119 | 120 | ```puppet 121 | class { 'puppet::server': 122 | directoryenvs => true, 123 | basemodulepath => '$confdir/modules:$confdir/secure', 124 | environmentpath => '$confdir/environments', 125 | default_manifest => 'site/site.pp', 126 | manage_puppetdb => true, 127 | reporturl => "https://${::fqdn}/reports", 128 | servertype => 'unicorn', 129 | ca => true, 130 | reports => [ 131 | 'https', 132 | 'store', 133 | 'puppetdb', 134 | ], 135 | } 136 | 137 | # in a real environment, you'll probably populate parameters on these 138 | # report classes from hiera. For this example, it's specified inline so that 139 | # the manifest works as-is. 140 | 141 | class { 'puppet::reports::graphite': 142 | server => $::fqdn, 143 | port => 2003, 144 | prefix => 'puppetmaster' 145 | } 146 | 147 | class { 'puppet::reports::irccat': 148 | host => $::fqdn, 149 | githuburl => 'https://github.com/example/foo', 150 | dashboard => 'https://dashboard.example.com', 151 | } 152 | ``` 153 | 154 | 155 | ### Puppet Agent Configuration 156 | At the most basic, simply: 157 | ```puppet 158 | include puppet::agent 159 | ``` 160 | 161 | That will configure a cron job to run the puppet agent. If that's not what you 162 | want, one of the following may be more to your liking: 163 | 164 | #### Running the agent service instead of via cron 165 | ```puppet 166 | class { 'puppet::agent': 167 | method => 'service', 168 | } 169 | ``` 170 | 171 | #### Agent using cron, with more configuration options set: 172 | Note that although the parameters correspond with puppet configuration file 173 | option names, only a relatively subset can currently be managed with this 174 | module. 175 | 176 | ```puppet 177 | class { 'puppet::agent': 178 | server => 'puppet.example.com', 179 | ca_server => 'puppetca.example.com', 180 | report_server => 'puppet_reports.example.com', 181 | method => 'cron', 182 | configtimeout => 900, 183 | } 184 | ``` 185 | 186 | In a production environment, you should probably use `include puppet::agent` 187 | and populate parameters using [hiera automatic parameter lookup][hiera-lookup] 188 | instead of hardcoding these values into manifests. 189 | 190 | ## Limitations 191 | 192 | This module is (basically) only tested on Debian Wheezy. The maintainers also 193 | care about FreeBSD and OpenBSD support. A token gesture of EL support exists in 194 | `params.pp` but that's about it; this probably won't do much on CentOS/RedHat. 195 | You'll see remnants of support for Windows, Gentoo, Solaris etc in the codebase 196 | but there's no testing or ongoing support for those platforms. They probably 197 | don't work at all. Pull requests welcome if you're interested. 198 | 199 | Bootstrapping an all-in-one (master, puppetdb, postgresql) puppetmaster with 200 | puppet-puppet is relatively straightforward. However, building a usable puppet 201 | infrastructure with it requires additional steps, such as figuring out how you 202 | want to deploy manifests and modules to your master. (tip: use r10k) 203 | 204 | You should definitely not use this module on an existing production 205 | puppetmaster unless you've tested it extensively. This is a tool developed by 206 | sysadmins, not developers, and testing is very incomplete. 207 | 208 | ## Development 209 | 210 | Read [CONTRIBUTING.md](CONTRIBUTING.md) to see instructions on running beaker 211 | and rspec tests. 212 | 213 | [puppetlabs-puppetdb]: https://github.com/puppetlabs/puppet-puppetdb 214 | [puppetlabs-apache]: https://github.com/puppetlabs/puppetlabs-apache 215 | [jfryman-nginx]: https://github.com/jfryman/puppet-nginx 216 | [r10k]: https://github.com/adrienthebo/r10k 217 | [hiera-lookup]: https://docs.puppetlabs.com/hiera/1/puppet.html#automatic-parameter-lookup 218 | [hiera-docs]: https://docs.puppetlabs.com/hiera/1/ 219 | [zack-r10k]: https://forge.puppetlabs.com/zack/r10k 220 | [sharpie-r10k]: https://github.com/Sharpie/puppet-r10k 221 | [sgs-1]: http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-1/ 222 | [hunner-hiera]: https://github.com/hunner/puppet-hiera 223 | --------------------------------------------------------------------------------