2 changes: 2 additions & 0 deletions .fixtures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ fixtures:
vcsrepo: "git://github.com/puppetlabs/puppetlabs-vcsrepo.git"
git: "git://github.com/puppetlabs/puppetlabs-git.git"
portage: "git://github.com/gentoo/puppet-portage.git"
symlinks:
r10k: "#{source_dir}"
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.swp
pkg
spec/fixtures/
spec/fixtures/manifests/site.pp
.DS_Store
*.swp
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 '1.0.2'
version '2.2.2'
source 'https://github.com/acidprime/r10k'
author 'zack'
license 'Apache License, Version 2.0'
Expand Down
58 changes: 33 additions & 25 deletions templates/webhook.erb → files/webhook
Original file line number Diff line number Diff line change
Expand Up @@ -16,82 +16,89 @@ require 'openssl'
require 'mcollective'
require 'resolv'
require 'json'

require 'yaml'
include MCollective::RPC


LOGFILE = '/var/log/webhook/access.log'
USER = 'puppet'
PASS = 'puppet'
PORT = '8088'
CERT_PATH = '/var/lib/peadmin/.mcollective.d'
CLIENT_CFG = '/var/lib/peadmin/.mcollective'
CERT_NAME = 'peadmin'
DISCOVER_TIMEOUT = 10
CLIENT_TIMEOUT = 120
USE_RUBY = false
PROTECTED = true
WEBHOOK_CONFIG = '/etc/webhook.yaml'
PIDFILE = '/var/run/webhook/webhook.pid'
APP_ROOT = '/var/run/webhook'

if (File.exists?(WEBHOOK_CONFIG))
$config = YAML.load_file(WEBHOOK_CONFIG)
else
raise "Configuration file: #{WEBHOOK_CONFIG} does not exist"
end

ENV['HOME'] = '/var/lib/peadmin'
ENV['PATH'] = '/sbin:/usr/sbin:/bin:/usr/bin:/opt/puppet/bin'

$logger = WEBrick::Log::new($config['logfile'], WEBrick::Log::INFO)


opts = {
:Port => PORT,
:Logger => WEBrick::Log::new(LOGFILE, WEBrick::Log::DEBUG),
:Port => $config['port'],
:Logger => $logger,
:ServerType => WEBrick::Daemon,
:SSLEnable => true,
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
:SSLCertificate => OpenSSL::X509::Certificate.new( File.open(File.join(CERT_PATH, "#{CERT_NAME}-cert.pem")).read),
:SSLPrivateKey => OpenSSL::PKey::RSA.new( File.open(File.join(CERT_PATH, "#{CERT_NAME}-private.pem")).read),
: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),
:SSLCertName => [ [ "CN",WEBrick::Utils::getservername ] ]
}


class Server < Sinatra::Base

set :static, false

#File.open('/var/run/webhook/webhook.pid', 'w') {|f| f.write $$ }

get '/' do
raise Sinatra::NotFound
end

# curl -d '{ "ref": "refs/heads/production" }' -H "Accept: application/json" 'https://puppet:puppet@localhost:8088/payload' -k -q
post '/payload' do
protected! if PROTECTED
protected! if $config['protected']
request.body.rewind # in case someone already read it
data = JSON.parse(request.body.read, :quirks_mode => true)
branch = data['ref'].split("/").last
deploy(branch)
end

not_found do
halt 404, 'You shall not pass! (page not found)'
halt 404, "You shall not pass! (page not found)\n"
end

helpers do
def deploy(branch)
begin
if USE_RUBY
if $config['use_mco_ruby']
result = mco(branch).first
if result.results[:statuscode] == 0
message = result.results[:statusmsg]
else
raise result.results[:statusmsg]
end
else
message = 'forked'
Process.detach(fork{ exec "/opt/puppet/bin/mco r10k deploy #{branch} >> #{LOGFILE} 2>&1 &"})
message = "triggered: mco r10k deploy #{branch} -p"
Process.detach(fork{ exec "/opt/puppet/bin/mco r10k deploy #{branch} -p >> #{LOGFILE} 2>&1 &"})
end
$logger.info("message: #{message} branch: #{branch}")
{: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 #end deploy()

def mco(branch)
options = MCollective::Util.default_options
options[:config] = CLIENT_CFG
options[:config] = $config['client_cfg']
client = rpcclient('r10k', :exit_on_failure => false,:options => options)
client.discovery_timeout = DISCOVER_TIMEOUT
client.timeout = CLIENT_TIMEOUT
client.discovery_timeout = $config['discovery_timeout']
client.timeout = $config['client_timeout']
result = client.send('deploy',{:environment => branch})
end # end deploy()

Expand All @@ -106,11 +113,12 @@ class Server < Sinatra::Base
def authorized?
@auth ||= Rack::Auth::Basic::Request.new(request.env)
@auth.provided? && @auth.basic? && @auth.credentials &&
@auth.credentials == [USER, PASS]
@auth.credentials == [$config['user'],$config['pass']]
end #end authorized?
end #end helpers
end

Rack::Handler::WEBrick.run(Server, opts) do |server|
server.start { File.open("#{PIDFILE}", 'w', 0644) { |f| f.write(Process.pid.to_s) } }
[:INT, :TERM].each { |sig| trap(sig) { server.stop } }
end
13 changes: 13 additions & 0 deletions manifests/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@
# Include the mcollective agent
$mcollective = false

# Webhook configuration information
$webhook_user = 'puppet'
$webhook_pass = 'puppet'
$webhook_port = '8088'
$webhook_logfile = '/var/log/webhook/access.log'
$webhook_certname = 'peadmin'
$webhook_certpath = '/var/lib/peadmin/.mcollective.d'
$webhook_client_cfg = '/var/lib/peadmin/.mcollective'
$webhook_use_mco_ruby = false
$webhook_protected = true
$webhook_discovery_timeout = 10
$webhook_client_timeout = 120

if $::is_pe =~ /true/ {
# Puppet Enterprise specific settings
$puppetconf_path = '/etc/puppetlabs/puppet'
Expand Down
25 changes: 17 additions & 8 deletions manifests/webhook.pp
Original file line number Diff line number Diff line change
@@ -1,38 +1,47 @@
# This class creates a github webhoook to allow curl style post-rec scripts
class r10k::webhook(
$owner = 'root',
$group = '0',
$user = 'peadmin',
$group = 'peadmin',
$git_server = 'localhost',
) {
require r10k::webhook::config

File {
ensure => file,
owner => $owner,
group => $group,
owner => 'root',
group => '0',
mode => '0755',
}

file { '/var/log/webhook':
ensure => 'directory',
owner => 'peadmin',
group => 'peadmin',
owner => $user,
group => $group,
before => File['webhook_bin'],
}

file { '/var/run/webhook':
ensure => 'directory',
owner => $user,
group => $group,
before => File['webhook_init_script'],
}

file { 'webhook_init_script':
content => template('r10k/webhook.init.erb'),
path => '/etc/init.d/webhook',
require => Package['sinatra'],
before => File['webhook_bin'],
}
file { 'webhook_bin':
content => template('r10k/webhook.erb'),
source => 'puppet:///modules/r10k/webhook',
path => '/usr/local/bin/webhook',
notify => Service['webhook'],
}

service { 'webhook':
ensure => 'running',
pattern => '.*ruby.*webhoo[k]',
pattern => '.*ruby.*webhoo[k]$',
hasstatus => false,
}

Expand Down
52 changes: 52 additions & 0 deletions manifests/webhook/config.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# == Class: r10k::webhook::config
#
# Set up the root r10k config file (/etc/webhook.yaml).
#
# === Authors
#
# Zack Smith <zack@puppetlabs.com>
class r10k::webhook::config (
$hash = 'UNSET',
$certname = $r10k::params::webhook_certname,
$certpath = $r10k::params::webhook_certpath,
$user = $r10k::params::webhook_user,
$pass = $r10k::params::webhook_pass,
$port = $r10k::params::webhook_port,
$logfile = $r10k::params::webhook_logfile,
$client_cfg = $r10k::params::webhook_client_cfg,
$use_mco_ruby = $r10k::params::webhook_use_mco_ruby,
$protected = $r10k::params::webhook_protected,
$discovery_timeout = $r10k::params::webhook_discovery_timeout,
$client_timeout = $r10k::params::webhook_client_timeout,
$configfile = '/etc/webhook.yaml',
) inherits r10k::params {

if $hash == 'UNSET' {
$webhook_hash = {
'user' => $user,
'pass' => $pass,
'port' => $port,
'certname' => $certname,
'client_timeout' => $client_timeout,
'discovery_timeout' => $discovery_timeout,
'certpath' => $certpath,
'client_cfg' => $client_cfg,
'certpath' => $certpath,
'use_mco_ruby' => $use_mco_ruby,
'logfile' => $logfile,
'protected' => $protected,
}
} else {
validate_hash($hash)
$webhook_hash = $hash
}

file { 'webhook.yaml':
ensure => file,
owner => 'root',
group => '0',
mode => '0644',
path => $configfile,
content => template('r10k/webhook.yaml.erb'),
}
}
5 changes: 5 additions & 0 deletions spec/fixtures/modules/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This keeps the modules directory available for fixtures
# Ignore everything in this directory
*
# Except this file
!.gitignore
1 change: 0 additions & 1 deletion spec/fixtures/modules/r10k/files

This file was deleted.

1 change: 0 additions & 1 deletion spec/fixtures/modules/r10k/manifests

This file was deleted.

1 change: 0 additions & 1 deletion spec/fixtures/modules/r10k/templates

This file was deleted.

51 changes: 9 additions & 42 deletions templates/webhook.init.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
# processname: webhook

[ -f /etc/sysconfig/webhook ] && . /etc/sysconfig/webhook
pidfile='/var/run/webhook.pid'
pidfile='/var/run/webhook/webhook.pid'
webhook='/usr/local/bin/webhook'
DAEMON_USER=peadmin
DAEMON_USER='<%= @user %>'
RETVAL=0

# Source function library.
Expand All @@ -20,51 +20,21 @@ RETVAL=0
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
export PATH

# Determine if we can use the -p option to daemon, killproc, and status.
# RHEL < 5 can't.
if status | grep -q -- '-p' 2>/dev/null; then
daemonopts="--pidfile $pidfile"
pidopts="-p $pidfile"
fi

start() {
echo
echo -n $"Starting webhook: "
daemon --user ${DAEMON_USER:?} $daemonopts $webhook
daemon --user ${DAEMON_USER:?} --pidfile $pidfile $webhook
RETVAL=$?
return $RETVAL
}

stop() {
echo -n $"Stopping webhook: "
pid=`ps -ef | awk '/.*ruby.*webhoo[k]/{print $2;exit}' 2> /dev/null`
kill $pid
pid=`netstat -lnp | awk '/.*:8088.*ruby/{sub("/.*","",$NF);print $NF}'`
#killproc -p $pidfile $webhook]
kill -9 $pid | failure 'webhook'
RETVAL=$?
echo
# wait until really stopped
i=0
if [ -n "${pid:-}" ]; then
while kill -0 "${pid:-}" 2> /dev/null; do
# If we get here it means the process has not yet died, but
# potentially will. In anticipation of it actually dying we
# set RETVAL to 0 here. If we eventually timeout, the real
# exit will be the return code of `kill -9` from below.
RETVAL=0
if [ $i = '60' ]; then
kill -9 $pid
RETVAL=$?
break;
else
if [ $i = '0' ]; then
echo -n " ... waiting "
else
echo -n "."
fi
i=$(($i+1))
sleep 1
fi
done
fi
[ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
success 'webhook'
return $RETVAL
}

Expand All @@ -75,17 +45,14 @@ restart() {

rh_status() {
if [ -f "${pidfile}" ]; then
status $pidopts $webhook
status -p $pidfile $webhook
RETVAL=$?
else
RETVAL=3
fi
return $RETVAL
}

rh_status_q() {
rh_status >/dev/null 2>&1
}

case "$1" in
start)
Expand Down
1 change: 1 addition & 0 deletions templates/webhook.yaml.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= @webhook_hash.to_yaml %>