puppet_webhook 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a2bedbab06ccd3b043abda7e4e3c4711f90bae1fcc6cc102c283c63c517f783
4
- data.tar.gz: b609d99c486ef605836756939f1ff33ae40056b38641d13c2665b33ecbd56093
3
+ metadata.gz: d64ee6bf97a42a0899e97f9759b200ff433a9f7460255e3820d0acfd05431c4d
4
+ data.tar.gz: 0b7b8b681a2d5418258702c9b9eda6a147732b03290f8d1cf3c18954c41dcca6
5
5
  SHA512:
6
- metadata.gz: 4cd5c13d66c1fef5b8eda9c5768e5ecc216cb24617cdd04ec07e0655213b543561074aec522790b394879e3906cd8835896d8bdf7abaffb265ade893cb2496ff
7
- data.tar.gz: d3870a2daffca879005cac928fa0fbe46a4c65c7d3594fb12a8333d9394afc2fb5e181027e45a57dfea6505ceb92e1369587aefe4dc4abe49a3025a4fd9d988f
6
+ metadata.gz: 56449a908c94d905486f48b72da716461e690cce591f33d3945be234562141ca11c82ad61238f2644c6422804f32e9b18ade4908a0c294593a2a3c860f35f16b
7
+ data.tar.gz: df5e40c06d0b3bd199ab724907ce07e0897ffc0644b870a94577a979c1d857cbe4b687d2c71d99d9f8454d02374958a99a231523fd789dd6cb8146f6b191e1d0
data/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
4
4
  Each new release typically also includes the latest modulesync defaults.
5
5
  These should not affect the functionality of the module.
6
6
 
7
+ ## [v1.1.0](https://github.com/voxpupuli/puppet_webhook/tree/v1.1.0) (2018-03-05)
8
+ [Full Changelog](https://github.com/voxpupuli/puppet_webhook/compare/v1.0.0...v1.1.0)
9
+
10
+ **Implemented enhancements:**
11
+
12
+ - Allow github to send form-urlencoded payloads and bug fix in webrick\_opts [\#45](https://github.com/voxpupuli/puppet_webhook/pull/45) ([williamberman](https://github.com/williamberman))
13
+
14
+ **Closed issues:**
15
+
16
+ - Refactor binary to pass configuration values from file or CLI [\#27](https://github.com/voxpupuli/puppet_webhook/issues/27)
17
+
18
+ **Merged pull requests:**
19
+
20
+ - Replace all MCollective agent calls with the agent lib. [\#44](https://github.com/voxpupuli/puppet_webhook/pull/44) ([dhollinger](https://github.com/dhollinger))
21
+
7
22
  ## [v1.0.0](https://github.com/voxpupuli/puppet_webhook/tree/v1.0.0) (2017-12-21)
8
23
  [Full Changelog](https://github.com/voxpupuli/puppet_webhook/compare/v0.1.0...v1.0.0)
9
24
 
@@ -17,6 +32,7 @@ These should not affect the functionality of the module.
17
32
 
18
33
  **Merged pull requests:**
19
34
 
35
+ - Release 1.0.0 [\#42](https://github.com/voxpupuli/puppet_webhook/pull/42) ([dhollinger](https://github.com/dhollinger))
20
36
  - Update config options and docs [\#41](https://github.com/voxpupuli/puppet_webhook/pull/41) ([dhollinger](https://github.com/dhollinger))
21
37
  - Fix payload gentypes [\#39](https://github.com/voxpupuli/puppet_webhook/pull/39) ([dhollinger](https://github.com/dhollinger))
22
38
  - Add simple config.ru file [\#38](https://github.com/voxpupuli/puppet_webhook/pull/38) ([dhollinger](https://github.com/dhollinger))
@@ -60,4 +76,4 @@ These should not affect the functionality of the module.
60
76
 
61
77
 
62
78
 
63
- \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
79
+ \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
data/README.md CHANGED
@@ -90,7 +90,7 @@ When using the default SystemD unit file or SysVInit service file, the server co
90
90
 
91
91
  ##### Options
92
92
 
93
- #####`server_type`
93
+ ##### `server_type`
94
94
 
95
95
  Determines if the Webrick server should run in Simple or Daemon mode.
96
96
  * Valid options: [ `simple`, `daemon` ].
data/bin/puppet_webhook CHANGED
@@ -94,7 +94,6 @@ webrick_opts = {
94
94
  Port: options[:port],
95
95
  DocumentRoot: approot,
96
96
  ServerType: options[:server_type],
97
- ServerSoftware: PuppetWebhook,
98
97
  StartCallBack: proc { File.open(options[:pidfile], 'w') { |f| f.write Process.pid } }
99
98
  }
100
99
 
data/config/app.yml CHANGED
@@ -4,7 +4,7 @@ user: puppet
4
4
  pass: puppet
5
5
 
6
6
  # Mcollective
7
- client_cfg: "/var/lib/peadmin/.mcollective"
7
+ client_cfg: "/etc/puppetlabs/mcollective/server.cfg"
8
8
  client_timeout: "120"
9
9
  use_mco_ruby: false
10
10
  use_mcollective: false
@@ -1,17 +1,26 @@
1
+ require 'plugins/mcollective'
2
+
1
3
  module Deployments # rubocop:disable Style/Documentation
2
4
  def deploy(branch, deleted)
3
- if settings.use_mco_ruby
4
- result = mco(branch).first
5
- raise result.results[:statusmsg] unless result.results[:statuscode].zero?
5
+ # Currently requires puppet_webhook to be run as a non-root
6
+ # user with access to running the MCollective client.
7
+ if settings.use_mcollective
8
+ results = PuppetWebhook::Mcollective.new('r10k',
9
+ 'deploy',
10
+ {
11
+ dtimeout: settings.discovery_timeout,
12
+ timeout: settings.client_timeout
13
+ },
14
+ settings.client_cfg,
15
+ environment: branch).run
16
+ results.each do |result|
17
+ raise result.results[:statusmsg] unless result.results[:statuscode].zero?
18
+ end
19
+ raise results.stats[:noresponsefrom] unless result.stats[:noresponsefrom].length.zero?
6
20
 
7
21
  message = result.results[:statusmsg]
8
22
  else
9
- command = if settings.use_mcollective
10
- "#{settings.command_prefix} mco r10k deploy #{branch} #{settings.mco_arguments}"
11
- else
12
- # If you don't use mcollective then this hook needs to be running as r10k's user i.e. root
13
- "#{settings.command_prefix} r10k deploy environment #{branch} #{settings.r10k_deploy_arguments}"
14
- end
23
+ command = "#{settings.command_prefix} r10k deploy environment #{branch} #{settings.r10k_deploy_arguments}"
15
24
  message = run_command(command)
16
25
  end
17
26
  status_message = { status: :success, message: message.to_s, branch: branch, status_code: 200 }
@@ -20,7 +29,7 @@ module Deployments # rubocop:disable Style/Documentation
20
29
  generate_types(branch) if types?
21
30
  end
22
31
  notify_slack(status_message) if slack?
23
- status_message.to_json
32
+ [status_message[:status_code], status_message.to_json]
24
33
  rescue StandardError => e
25
34
  status_message = { status: :fail, message: e.message, trace: e.backtrace, branch: branch, status_code: 500 }
26
35
  LOGGER.error("message: #{e.message} trace: #{e.backtrace}")
@@ -30,12 +39,27 @@ module Deployments # rubocop:disable Style/Documentation
30
39
  end
31
40
 
32
41
  def deploy_module(module_name)
33
- command = if settings.use_mcollective
34
- "#{settings.command_prefix} mco r10k deploy_module #{module_name} #{settings.mco_arguments}"
35
- else
36
- "#{settings.command_prefix} r10k deploy module #{module_name}"
37
- end
38
- message = run_command(command)
42
+ # Currently requires puppet_webhook to be run as a non-root
43
+ # user with access to running the MCollective client.
44
+ if settings.use_mcollective
45
+ results = PuppetWebhook::Mcollective.new('r10k',
46
+ 'deploy',
47
+ {
48
+ dtimeout: settings.discovery_timeout,
49
+ timeout: settings.client_timeout
50
+ },
51
+ settings.client_cfg,
52
+ module: module_name).run
53
+ results.each do |result|
54
+ raise result.results[:statusmsg] unless result.results[:statuscode].zero?
55
+ end
56
+ raise results.stats[:noresponsefrom] unless results.stats[:noresponsefrom].length.zero?
57
+
58
+ message = result.results[:statusmsg]
59
+ else
60
+ command = "#{settings.command_prefix} r10k deploy module #{module_name}"
61
+ message = run_command(command)
62
+ end
39
63
  LOGGER.info("message: #{message} module_name: #{module_name}")
40
64
  status_message = { status: :success, message: message.to_s, module_name: module_name, status_code: 200 }
41
65
  notify_slack(status_message) if slack?
data/lib/helpers/tasks.rb CHANGED
@@ -153,13 +153,4 @@ module Tasks # rubocop:disable Style/Documentation
153
153
  throw(:halt, [401, "Not authorized\n"])
154
154
  end
155
155
  end
156
-
157
- def mco(branch)
158
- options = MCollective::Util.default_options
159
- options[:config] = settings.client_cfg
160
- client = rpcclient('r10k', exit_on_failure: false, options: options)
161
- client.discovery_timeout = settings.discovery_timeout
162
- client.timeout = settings.client_timeout
163
- client.send('deploy', environment: branch)
164
- end
165
156
  end
@@ -1,115 +1,15 @@
1
1
  require 'rack/bodyparser'
2
2
  require 'json'
3
3
 
4
+ require_relative 'webhook_parser'
5
+
4
6
  module Sinatra
5
7
  module Parsers
6
8
  class WebhookJsonParser # rubocop:disable Style/Documentation
7
- attr_accessor :env, :data
8
-
9
- def call(body)
10
- @data = JSON.parse(body, quirks_mode: true)
11
- @vcs = detect_vcs
12
- {
13
- branch: branch,
14
- deleted: deleted?,
15
- module_name: repo_name.sub(%r{^.*-}, ''),
16
- repo_name: repo_name,
17
- repo_user: repo_user
18
- }.delete_if { |_k, v| v.nil? }
19
- end
20
-
21
- def detect_vcs
22
- return 'github' if github_webhook?
23
- return 'gitlab' if gitlab_webhook?
24
- return 'stash' if stash_webhook?
25
- return 'bitbucket' if bitbucket_webhook?
26
- return 'tfs' if tfs_webhook?
27
- raise StandardError, 'payload not recognised'
28
- end
29
-
30
- def github_webhook?
31
- # https://developer.github.com/v3/activity/events/types/#pushevent
32
- env.key?('HTTP_X_GITHUB_EVENT')
33
- end
34
-
35
- def gitlab_webhook?
36
- # https://docs.gitlab.com/ce/user/project/integrations/webhooks.html
37
- env.key?('HTTP_X_GITLAB_EVENT')
38
- end
39
-
40
- # stash/bitbucket server
41
- def stash_webhook?
42
- # https://confluence.atlassian.com/bitbucketserver/post-service-webhook-for-bitbucket-server-776640367.html
43
- env.key?('HTTP_X_ATLASSIAN_TOKEN')
44
- end
45
-
46
- def bitbucket_webhook?
47
- # https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html
48
- env.key?('HTTP_X_EVENT_KEY')
49
- end
50
-
51
- def tfs_webhook?
52
- # https://docs.microsoft.com/en-us/vsts/service-hooks/services/webhooks
53
- return false unless @data.key? 'resource'
54
- return false unless @data.key? 'eventType'
55
- true
56
- end
57
-
58
- def branch
59
- case @vcs
60
- when 'github'
61
- if @data.key? 'ref'
62
- @data['ref'].sub('refs/heads/', '')
63
- else
64
- @data['repository']['default_branch']
65
- end
66
- when 'gitlab'
67
- @data['ref'].sub('refs/heads/', '')
68
- when 'stash'
69
- @data['refChanges'][0]['refId'].sub('refs/heads/', '')
70
- when 'bitbucket'
71
- return @data['push']['changes'][0]['new']['name'] unless deleted?
72
- @data['push']['changes'][0]['old']['name']
73
- when 'tfs'
74
- @data['resource']['refUpdates'][0]['name'].sub('refs/heads/', '')
75
- end
76
- end
77
-
78
- def deleted?
79
- case @vcs
80
- when 'github'
81
- @data['deleted']
82
- when 'gitlab'
83
- @data['after'] == '0000000000000000000000000000000000000000'
84
- when 'stash'
85
- @data['refChanges'][0]['type'] == 'DELETE'
86
- when 'bitbucket'
87
- @data['push']['changes'][0]['closed']
88
- when 'tfs'
89
- @data['resource']['refUpdates'][0]['newObjectId'] == '0000000000000000000000000000000000000000'
90
- end
91
- end
92
-
93
- def repo_name
94
- if @vcs == 'gitlab'
95
- @data['project']['name']
96
- elsif @vcs == 'tfs'
97
- @data['resource']['repository']['name']
98
- else
99
- @data['repository']['name']
100
- end
101
- end
9
+ include Sinatra::Parsers::WebhookParser
102
10
 
103
- def repo_user
104
- # TODO: Clarify what repo_user actually is.
105
- # github is currently using the repo's 'owner', gitlab is using the user who pushed.
106
- case @vcs
107
- when 'github'
108
- @data['repository']['owner']['login']
109
- when 'gitlab'
110
- @data['user_username']
111
- end
112
- # TODO: Bitbucket, Stash/Bitbucket Server, TFS
11
+ def parse_data(body)
12
+ JSON.parse(body, quirks_mode: true)
113
13
  end
114
14
  end
115
15
  end
@@ -0,0 +1,116 @@
1
+ require 'rack/bodyparser'
2
+ require 'json'
3
+
4
+ module Sinatra
5
+ module Parsers
6
+ module WebhookParser # rubocop:disable Style/Documentation
7
+ attr_accessor :env, :data
8
+
9
+ def call(body)
10
+ @data = parse_data(body)
11
+ @vcs = detect_vcs
12
+ {
13
+ branch: branch,
14
+ deleted: deleted?,
15
+ module_name: repo_name.sub(%r{^.*-}, ''),
16
+ repo_name: repo_name,
17
+ repo_user: repo_user
18
+ }.delete_if { |_k, v| v.nil? }
19
+ end
20
+
21
+ def detect_vcs
22
+ return 'github' if github_webhook?
23
+ return 'gitlab' if gitlab_webhook?
24
+ return 'stash' if stash_webhook?
25
+ return 'bitbucket' if bitbucket_webhook?
26
+ return 'tfs' if tfs_webhook?
27
+ raise StandardError, 'payload not recognised'
28
+ end
29
+
30
+ def github_webhook?
31
+ # https://developer.github.com/v3/activity/events/types/#pushevent
32
+ env.key?('HTTP_X_GITHUB_EVENT')
33
+ end
34
+
35
+ def gitlab_webhook?
36
+ # https://docs.gitlab.com/ce/user/project/integrations/webhooks.html
37
+ env.key?('HTTP_X_GITLAB_EVENT')
38
+ end
39
+
40
+ # stash/bitbucket server
41
+ def stash_webhook?
42
+ # https://confluence.atlassian.com/bitbucketserver/post-service-webhook-for-bitbucket-server-776640367.html
43
+ env.key?('HTTP_X_ATLASSIAN_TOKEN')
44
+ end
45
+
46
+ def bitbucket_webhook?
47
+ # https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html
48
+ env.key?('HTTP_X_EVENT_KEY')
49
+ end
50
+
51
+ def tfs_webhook?
52
+ # https://docs.microsoft.com/en-us/vsts/service-hooks/services/webhooks
53
+ return false unless @data.key? 'resource'
54
+ return false unless @data.key? 'eventType'
55
+ true
56
+ end
57
+
58
+ def branch
59
+ case @vcs
60
+ when 'github'
61
+ if @data.key? 'ref'
62
+ @data['ref'].sub('refs/heads/', '')
63
+ else
64
+ @data['repository']['default_branch']
65
+ end
66
+ when 'gitlab'
67
+ @data['ref'].sub('refs/heads/', '')
68
+ when 'stash'
69
+ @data['refChanges'][0]['refId'].sub('refs/heads/', '')
70
+ when 'bitbucket'
71
+ return @data['push']['changes'][0]['new']['name'] unless deleted?
72
+ @data['push']['changes'][0]['old']['name']
73
+ when 'tfs'
74
+ @data['resource']['refUpdates'][0]['name'].sub('refs/heads/', '')
75
+ end
76
+ end
77
+
78
+ def deleted?
79
+ case @vcs
80
+ when 'github'
81
+ @data['deleted']
82
+ when 'gitlab'
83
+ @data['after'] == '0000000000000000000000000000000000000000'
84
+ when 'stash'
85
+ @data['refChanges'][0]['type'] == 'DELETE'
86
+ when 'bitbucket'
87
+ @data['push']['changes'][0]['closed']
88
+ when 'tfs'
89
+ @data['resource']['refUpdates'][0]['newObjectId'] == '0000000000000000000000000000000000000000'
90
+ end
91
+ end
92
+
93
+ def repo_name
94
+ if @vcs == 'gitlab'
95
+ @data['project']['name']
96
+ elsif @vcs == 'tfs'
97
+ @data['resource']['repository']['name']
98
+ else
99
+ @data['repository']['name']
100
+ end
101
+ end
102
+
103
+ def repo_user
104
+ # TODO: Clarify what repo_user actually is.
105
+ # github is currently using the repo's 'owner', gitlab is using the user who pushed.
106
+ case @vcs
107
+ when 'github'
108
+ @data['repository']['owner']['login']
109
+ when 'gitlab'
110
+ @data['user_username']
111
+ end
112
+ # TODO: Bitbucket, Stash/Bitbucket Server, TFS
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,16 @@
1
+ require 'rack/bodyparser'
2
+ require 'json'
3
+
4
+ require_relative 'webhook_parser'
5
+
6
+ module Sinatra
7
+ module Parsers
8
+ class WebhookWWWFormURLEncodedParser # rubocop:disable Style/Documentation
9
+ include Sinatra::Parsers::WebhookParser
10
+
11
+ def parse_data(body)
12
+ JSON.parse(CGI.unescape(body).gsub(%r{^payload\=}, ''))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ require 'mcollective'
2
+
3
+ class PuppetWebhook
4
+ # Creates an Mcollective Agent object for running MCO agents
5
+ class Mcollective
6
+ attr_reader :results, :stats
7
+ include MCollective::RPC
8
+
9
+ def initialize(agent, command, timeouts = nil, options = nil, nodes = [], **args) # rubocop:disable Metrics/ParameterLists
10
+ @agent = agent
11
+ @command = command
12
+ @timeout = timeouts[:timeout] || '120'
13
+ @dtimeout = timeouts[:dtimeout] || '10'
14
+ @options = options
15
+ @nodes = nodes
16
+ @args = args
17
+ end
18
+
19
+ def run
20
+ LOGGER.info("Starting request for #{@agent}##{@command}")
21
+
22
+ begin
23
+ @results = client.send(@command, @args)
24
+ rescue StandardError => e
25
+ LOGGER.error("Error: #{e}")
26
+ end
27
+
28
+ @stats = client.stats
29
+ end
30
+
31
+ def client
32
+ client = MCollective::RPC::Client.new(@agent, config_file: MCollective::Util.config_file_for_user, options: MCollective::Util.default_options) # rubocop:disable Metrics/LineLength
33
+ client.config = @options if @options
34
+ client.timeout = @timeout if @timeout
35
+ client.discovery_timeout = @dtimeout if @dtimeout
36
+ client.progress = false
37
+ client
38
+ end
39
+ end
40
+ end
@@ -3,6 +3,7 @@ require 'sinatra/config_file'
3
3
  require 'json'
4
4
  require 'cgi'
5
5
  require 'parsers/webhook_json_parser'
6
+ require 'parsers/webhook_www_form_url_encoded_parser'
6
7
 
7
8
  # Routes
8
9
  require_relative 'routes/default'
@@ -12,8 +13,15 @@ require_relative 'routes/payload'
12
13
  class PuppetWebhook < Sinatra::Base # rubocop:disable Style/Documentation
13
14
  set :root, File.dirname(__FILE__)
14
15
  use Rack::BodyParser,
15
- parsers: { 'application/json' => Sinatra::Parsers::WebhookJsonParser.new },
16
- handlers: { 'application/json' => proc { |e, type| [400, { 'Content-Type' => type }, [{ error: e.to_s }.to_json]] } }
16
+ parsers: {
17
+ 'application/json' => Sinatra::Parsers::WebhookJsonParser.new,
18
+ 'application/x-www-form-urlencoded' => Sinatra::Parsers::WebhookWWWFormURLEncodedParser.new
19
+ },
20
+ handlers: {
21
+ 'application/json' => proc { |e, type|
22
+ [400, { 'Content-Type' => type }, [{ error: e.to_s }.to_json]]
23
+ }
24
+ }
17
25
  register Sinatra::ConfigFile
18
26
 
19
27
  config_file(File.join(__dir__, '..', 'config', 'app.yml'), '/etc/puppet_webhook/app.yml')
@@ -24,7 +32,7 @@ class PuppetWebhook < Sinatra::Base # rubocop:disable Style/Documentation
24
32
 
25
33
  # Custom Settings
26
34
  set :protected, false unless settings.respond_to? :protected=
27
- set :client_cfg, '/var/lib/peadmin/.mcollective' unless settings.respond_to? :client_cfg=
35
+ set :client_cfg, '/etc/puppetlabs/mcollective/client.cfg' unless settings.respond_to? :client_cfg=
28
36
  set :client_timeout, '120' unless settings.respond_to? :client_timeout=
29
37
  set :use_mco_ruby, false unless settings.respond_to? :use_mco_ruby=
30
38
  set :use_mcollective, false unless settings.respond_to? :use_mcollective=
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet_webhook
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vox Pupuli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-21 00:00:00.000000000 Z
11
+ date: 2018-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -237,6 +237,9 @@ files:
237
237
  - lib/helpers/init.rb
238
238
  - lib/helpers/tasks.rb
239
239
  - lib/parsers/webhook_json_parser.rb
240
+ - lib/parsers/webhook_parser.rb
241
+ - lib/parsers/webhook_www_form_url_encoded_parser.rb
242
+ - lib/plugins/mcollective.rb
240
243
  - lib/puppet_webhook.rb
241
244
  - lib/routes/default.rb
242
245
  - lib/routes/module.rb
@@ -262,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
262
265
  version: '0'
263
266
  requirements: []
264
267
  rubyforge_project:
265
- rubygems_version: 2.7.3
268
+ rubygems_version: 2.7.6
266
269
  signing_key:
267
270
  specification_version: 4
268
271
  summary: Sinatra Webhook Server for Puppet/R10K