Showing with 185 additions and 17 deletions.
  1. +4 −0 CHANGELOG
  2. +1 −1 Modulefile
  3. +24 −3 README.md
  4. +30 −6 files/agent/r10k.ddl
  5. +9 −1 files/agent/r10k.rb
  6. +5 −2 files/application/r10k.rb
  7. +41 −3 files/webhook
  8. +1 −1 manifests/params.pp
  9. +22 −0 manifests/webhook.pp
  10. +48 −0 spec/classes/webhook.rb
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
2.5.0 - Zack Smith & Sean Keery
* Puppet Enterprise 3.7 compatibility
* Fixed bugs #108,#110,#110,#111
* Support for /module end point in webhook to run r10k module deploy foo
2.4.4 - Eli Young & Igor Galić
* prevent potential leakage Resource defaults for ini
* Handle branch names containing slashes
Expand Down
2 changes: 1 addition & 1 deletion Modulefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name 'zack-r10k'
version '2.4.4'
version '2.5.0'
source 'https://github.com/acidprime/r10k'
author 'zack'
license 'Apache License, Version 2.0'
Expand Down
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,17 @@ mco r10k deploy <environment>
```
Note: This implies `-p`

An example post-recieve hook is included in the files directory.
You can sync an individual module using:

```shell
mco r10k deploy_module <module>
```

An example post-receive hook is included in the files directory.
This hook can automatically cause code to synchronize on your
servers at time of push in git. More modern git systems use webhooks, for those see below.

###Install mcollective support for post recieve hooks
###Install mcollective support for post receive hooks
Install the `mco` command from the puppet enterprise installation directory i.e.
```shell
cd ~/puppet-enterprise-3.0.1-el-6-x86_64/packages/el-6-x86_64
Expand Down Expand Up @@ -323,11 +329,26 @@ class {'r10k::webhook':
}
# https://github.com/abrader/abrader-gms
# Add webhook to control repository ( the repo where the Puppetfile lives )
git_webhook { 'web_post_receive_webhook' :
ensure => present,
webhook_url => 'http://master.of.masters:8088/payload',
token => hiera('github_api_token'),
project_name => 'puppet/controle',
project_name => 'organization/control',
server_url => 'http://github.com',
provider => 'github',
}
# Add webhook to module repo if we are tacking branch in Puppetfile i.e.
# mod 'module_name',
# :git => 'http://github.com/organization/puppet-module_name',
# :branch => 'master'
git_webhook { 'web_post_receive_webhook_for_module' :
ensure => present,
webhook_url => 'http://master.of.masters:8088/module',
token => hiera('github_api_token'),
project_name => 'organization/puppet-module_name',
server_url => 'http://github.com',
provider => 'github',
}
Expand Down
36 changes: 30 additions & 6 deletions files/agent/r10k.ddl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
metadata :name => "r10k",
:description => "Syncs modules using git ",
:description => "Syncs modules using git",
:author => "Zack Smith",
:license => "MIT",
:version => "1.0",
Expand Down Expand Up @@ -54,20 +54,44 @@ action 'deploy', :description => "Deploy a specific environment, and its Puppetf
# Wanted to rubyize the following regex but didn't have time to test: ^(?!/|.*([/.]\.|//|@\{|\\\\))[^\040\177 ~^:?*\[]+(?<!\.lock|[/.])$
:validation => '.',
:optional => true,
:maxlength => 256
:maxlength => 256

output :environment,
:description => "Deploy a particular environment",
:display_as => "Specific environment"


output :output,
:description => "Output from r10k",
:display_as => "Output"

output :error,
:description => "Error from r10k",
:display_as => "Errors"

display :always
end

action 'deploy_module', :description => "Deploy a specific module" do
input :module_name,
:prompt => "Specific module",
:description => "Deploy a particular module",
:type => :string,
:validation => '.',
:optional => true,
:maxlength => 256

output :module_name,
:description => "Deploy a particular module",
:display_as => "Specific module"

output :output,
:description => "Output from r10k",
:display_as => "Output"

output :error,
:description => "Error from r10k",
:display_as => "Errors"

display :always
end
# vim: set syntax=ruby:
10 changes: 9 additions & 1 deletion files/agent/r10k.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ class R10k<RPC::Agent
['cache',
'synchronize',
'deploy',
'deploy_module',
'sync'].each do |act|
action act do
if act == 'deploy'
validate :environment, :shellsafe
environment = request[:environment]
run_cmd act, environment
reply[:environment] = environment
elsif act == 'deploy_module'
validate :module_name, :shellsafe
module_name = request[:module_name]
run_cmd act, module_name
reply[:module_name] = module_name
else
run_cmd act
end
Expand All @@ -41,12 +47,14 @@ def run_cmd(action,arg=nil)
cmd << 'pull' if action == 'pull'
cmd << 'status' if action == 'status'
reply[:status] = run(cmd, :stderr => :error, :stdout => :output, :chomp => true, :cwd => arg )
when 'cache','synchronize','sync', 'deploy'
when 'cache','synchronize','sync', 'deploy', 'deploy_module'
cmd = r10k
cmd << 'cache' if action == 'cache'
cmd << 'deploy' << 'environment' << '-p' if action == 'synchronize' or action == 'sync'
if action == 'deploy'
cmd << 'deploy' << 'environment' << arg << '-p'
elsif action == 'deploy_module'
cmd << 'deploy' << 'module' << arg
end
reply[:status] = run(cmd, :stderr => :error, :stdout => :output, :chomp => true)
end
Expand Down
7 changes: 5 additions & 2 deletions files/application/r10k.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,28 @@ def post_option_parser(configuration)
configuration[:path] = ARGV.shift || docs
when 'deploy'
configuration[:environment] = ARGV.shift || docs
when 'deploy_module'
configuration[:module_name] = ARGV.shift || docs
end
else
docs
end
end

def docs
puts "Usage: #{$0} push | pull | status | deploy"
puts "Usage: #{$0} push | pull | status | deploy | deploy_module"
end

def main
mc = rpcclient("r10k", :chomp => true)
options = {:path => configuration[:path]} if ['push','pull','status'].include? configuration[:command]
options = {:environment => configuration[:environment]} if ['deploy'].include? configuration[:command]
options = {:module_name => configuration[:module_name]} if ['deploy_module'].include? configuration[:command]
mc.send(configuration[:command], options).each do |resp|
puts "#{resp[:sender]}:"
if resp[:statuscode] == 0
responses = resp[:data][:output]
puts responses if responses and ['push','pull','status','deploy'].include? configuration[:command]
puts responses if responses and ['push','pull','status','deploy','deploy_module'].include? configuration[:command]
else
puts resp[:statusmsg]
end
Expand Down
44 changes: 41 additions & 3 deletions files/webhook
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,22 @@ ENV['PATH'] = '/sbin:/usr/sbin:/bin:/usr/bin:/opt/puppet/bin'

$logger = WEBrick::Log::new($config['access_logfile'], WEBrick::Log::DEBUG)

# PE 3.7 check for renamed certificate path
public_key_path = File.join("#{$config['certpath']}", "#{$config['certname']}-cert.pem")

private_key_path = File.join("#{$config['certpath']}", "#{$config['certname']}-private.pem")

opts = {
:Port => $config['port'],
:Logger => $logger,
:ServerType => WEBrick::Daemon,
:SSLEnable => $config['enable_ssl'],
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
:SSLCertificate => OpenSSL::X509::Certificate.new( File.open(File.join("#{$config['certpath']}", "#{$config['certname']}-cert.pem")).read),
:SSLPrivateKey => OpenSSL::PKey::RSA.new( File.open(File.join("#{$config['certpath']}", "#{$config['certname']}-private.pem")).read),
:SSLCertificate => OpenSSL::X509::Certificate.new( File.open(File.join("#{public_key_path}")).read),
:SSLPrivateKey => OpenSSL::PKey::RSA.new( File.open(File.join("#{private_key_path}")).read),
:SSLCertName => [ [ "CN",WEBrick::Utils::getservername ] ]
}


class Server < Sinatra::Base

set :static, false
Expand All @@ -57,6 +61,21 @@ class Server < Sinatra::Base
raise Sinatra::NotFound
end

# Simulate a github post:
# curl -d '{ "repository": { "name": "puppetlabs-stdlib" } }' -H "Accept: application/json" 'https://puppet:487156B2-7E67-4E1C-B447-001603C6B8B2@localhost:8088/module' -k -q
post '/module' do
protected! if $config['protected']
$logger.info("authenticated: #{$config['user']}")
request.body.rewind # in case someone already read it
decoded = request.body.read

data = JSON.parse(decoded, :quirks_mode => true)

module_name = ( data['repository']['name'] ).sub(/^.*-/, '')

deploy_module(module_name)
end

# Simulate a github post:
# curl -d '{ "ref": "refs/heads/production" }' -H "Accept: application/json" 'https://puppet:puppet@localhost:8088/payload' -k -q
#
Expand Down Expand Up @@ -101,6 +120,25 @@ class Server < Sinatra::Base
end

helpers do

def deploy_module(module_name)
begin
if $config['use_mcollective']
command = "/opt/puppet/bin/mco r10k deploy_module #{module_name} >> #{$config['mco_logfile']} 2>&1 &"
else
# If you don't use mcollective then this hook needs to be running as r10k's user i.e. root
command = "/opt/puppet/bin/r10k deploy module #{module_name} >> #{$config['mco_logfile']} 2>&1 &"
end
message = "triggered: #{command}"
Process.detach(fork{ exec "#{command}"})
$logger.info("message: #{message} module_name: #{module_name}")
{:status => :success, :message => message.to_s }.to_json
rescue => e
$logger.error("message: #{e.message} trace: #{e.backtrace}")
{:status => :fail, :message => e.message, :trace => e.backtrace}.to_json
end
end

def deploy(branch)
begin
if $config['use_mco_ruby']
Expand Down
2 changes: 1 addition & 1 deletion manifests/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
class r10k::params
{
$package_name = ''
$version = '1.3.5'
$version = '1.4.0'
$manage_modulepath = false
$manage_ruby_dependency = 'declare'
$install_options = undef
Expand Down
22 changes: 22 additions & 0 deletions manifests/webhook.pp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@
notify => Service['webhook'],
}

# 3.7 does not place the certificate in peadmin's ~
# This places it there as if it was an upgrade
file { '/var/lib/peadmin/.mcollective.d/peadmin-cert.pem':
ensure => 'file',
owner => 'peadmin',
group => 'peadmin',
content => file('/etc/puppetlabs/puppet/ssl/certs/pe-internal-peadmin-mcollective-client.pem'),
}


service { 'webhook':
ensure => 'running',
enable => true,
Expand All @@ -50,4 +60,16 @@
ensure => installed,
provider => 'pe_gem',
}

if versioncmp($::pe_version, '3.7.0') > 0 {
if !defined(Package['rack']) {
package { 'rack':
ensure => installed,
provider => 'pe_gem',
}
}
}



}
48 changes: 48 additions & 0 deletions spec/classes/webhook.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require 'spec_helper'
describe 'r10k::webhook' , :type => 'class' do
context "Puppet Enterprise 3.7.0 on a RedHat 5 installing webhook" do
let :facts do
{
:osfamily => 'RedHat',
:operatingsystemrelease => '5',
:operatingsystem => 'Centos',
:is_pe => 'true',
:pe_version => '3.7.0'
}
end
it { should contain_package("rack").with(
'ensure' => 'present',
)
}
end
context "Puppet Enterprise 3.7.x on a RedHat 5 installing webhook" do
let :facts do
{
:osfamily => 'RedHat',
:operatingsystemrelease => '5',
:operatingsystem => 'Centos',
:is_pe => '',
:pe_version => '3.7.1'
}
end
it { should contain_package("rack").with(
'ensure' => 'present',
)
}
end
context "Puppet Enterprise 3.3.x on a RedHat 5 installing webhook" do
let :facts do
{
:osfamily => 'RedHat',
:operatingsystemrelease => '5',
:operatingsystem => 'Centos',
:is_pe => '',
:pe_version => '3.3.1'
}
end
it { should_not contain_package("rack").with(
'ensure' => 'present',
)
}
end
end