puppet_webhook 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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