comrad 0.0.3 → 0.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 +4 -4
- data/.travis.yml +6 -2
- data/Gemfile +1 -8
- data/README.md +5 -1
- data/bin/comrad +2 -2
- data/comrad.gemspec +4 -4
- data/comrad.yml.EXAMPLE +1 -0
- data/lib/comrad.rb +54 -0
- data/lib/{change.rb → comrad/changeset.rb} +35 -45
- data/lib/comrad/chef.rb +64 -0
- data/lib/{config.rb → comrad/config.rb} +30 -36
- data/lib/comrad/notifier.rb +62 -0
- data/lib/{string.rb → core_ext/string.rb} +0 -0
- metadata +12 -12
- data/lib/application.rb +0 -66
- data/lib/chef.rb +0 -94
- data/lib/slack.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8a7c8c56e6699348b75573ab291f298b1b40267
|
4
|
+
data.tar.gz: 4f27ebddb71d6bad85cc9ea6ec947bc1b1f524b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7807998016b768f385925a846ad053eb41be367dadb1400936ae5a7eb1746d79f1ecbc129ddf98696071f5a7fc63ccd18b05c6510065a4b25558a240ac576daf
|
7
|
+
data.tar.gz: a90d91f1dd5542dbd01c964a87305b87949ae2d277dd60500ce778df73a33a2b315d7d7a0518ecb58e5f04799d859ce449308d89e9bb3af1bbca37e390005f0e
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
data/bin/comrad
CHANGED
data/comrad.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'comrad'
|
3
|
-
s.version = '0.0
|
3
|
+
s.version = '0.1.0'
|
4
4
|
s.date = Date.today.to_s
|
5
5
|
s.platform = Gem::Platform::RUBY
|
6
6
|
s.extra_rdoc_files = ['README.md', 'LICENSE']
|
@@ -14,13 +14,13 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.required_ruby_version = '>= 1.9.3'
|
15
15
|
s.add_dependency 'rest-client', '~> 1.7.0'
|
16
16
|
s.add_dependency 'chef', '>= 11.0'
|
17
|
-
s.add_dependency '
|
17
|
+
s.add_dependency 'slack-post', '~> 0.3'
|
18
18
|
s.add_development_dependency 'rake', '~> 10.0'
|
19
19
|
s.add_development_dependency 'rubocop', '~> 0.30.0'
|
20
20
|
|
21
21
|
s.files = `git ls-files -z`.split("\x0")
|
22
|
-
s.executables = s.
|
22
|
+
s.executables = s.name
|
23
23
|
s.require_paths = ['lib']
|
24
24
|
s.extra_rdoc_files = ['README.md']
|
25
|
-
s.rdoc_options = ['--line-numbers', '--inline-source', '--title', '
|
25
|
+
s.rdoc_options = ['--line-numbers', '--inline-source', '--title', 'comrad', '--main', 'README.md']
|
26
26
|
end
|
data/comrad.yml.EXAMPLE
CHANGED
data/lib/comrad.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
+
# Copyright:: Copyright (c) 2014-2015 Tim Smith
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
require 'pp'
|
19
|
+
|
20
|
+
require 'core_ext/string'
|
21
|
+
|
22
|
+
require 'comrad/config'
|
23
|
+
require 'comrad/changeset'
|
24
|
+
require 'comrad/chef'
|
25
|
+
require 'comrad/notifier'
|
26
|
+
|
27
|
+
# Your rad comrade - Sync changes from git to Chef Server via Jenkins
|
28
|
+
module Comrad
|
29
|
+
module_function
|
30
|
+
|
31
|
+
# Evaluate the current build for chef object changes,
|
32
|
+
# use knife to upload changed objects to the Chef server,
|
33
|
+
# send a notification to slack.
|
34
|
+
def self::run
|
35
|
+
if Config.config['flags']['print_config']
|
36
|
+
'Current config file / CLI flag values'.marquee
|
37
|
+
Config.print
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
|
41
|
+
if Changeset.empty?
|
42
|
+
'No objects to test. Exiting'.to_green
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
'The following chef objects will be changed'.marquee
|
47
|
+
pp Changeset.changes
|
48
|
+
|
49
|
+
'Making Chef Changes'.marquee
|
50
|
+
Chef.process_changes
|
51
|
+
|
52
|
+
Notifier.notify_changes
|
53
|
+
end
|
54
|
+
end
|
@@ -16,82 +16,72 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
require 'rest_client'
|
22
|
-
require 'uri'
|
23
|
-
rescue LoadError => e
|
24
|
-
raise "Missing gem or lib #{e}"
|
25
|
-
end
|
19
|
+
require 'json'
|
20
|
+
require 'rest_client'
|
26
21
|
|
27
22
|
module Comrad
|
28
23
|
# interacts with jenkins API to determine changes files
|
29
24
|
# parses changed files to determine relevant files that will
|
30
25
|
# need to be uploaded to chef server
|
31
|
-
class
|
32
|
-
|
33
|
-
def
|
34
|
-
@
|
35
|
-
|
36
|
-
@jenkins_host = URI(@config['jenkins']['url']).host
|
37
|
-
@username = @config['jenkins']['username']
|
38
|
-
@password = @config['jenkins']['password']
|
39
|
-
@job_name = @config['jenkins']['job_name']
|
40
|
-
@jenkins_workspace_path = @config['jenkins']['workspace_dir']
|
41
|
-
@build_num = ENV['BUILD_NUMBER']
|
26
|
+
class Changeset
|
27
|
+
# Return a hash of all changed objects
|
28
|
+
def self::changes
|
29
|
+
@changes ||= create_change_hash(changed_files_array)
|
30
|
+
end
|
42
31
|
|
43
|
-
|
44
|
-
|
32
|
+
# True if there are no changed objects
|
33
|
+
def self::empty?
|
34
|
+
changes == empty_chef_object_hash
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return a hash of all Jenkins details for this build
|
38
|
+
def self::build_data
|
39
|
+
@build_data ||= fetch_build_data
|
45
40
|
end
|
46
41
|
|
47
42
|
# fetch build data using Jenkins API
|
48
|
-
def
|
49
|
-
|
43
|
+
def self::fetch_build_data
|
44
|
+
j = Config.config['jenkins']
|
45
|
+
url = j['url'] + '/job/' + j['job_name'] + '/' + ENV['BUILD_NUMBER'] + '/api/json?'
|
46
|
+
conn = RestClient::Resource.new(url, j['username'], j['password'])
|
50
47
|
JSON.parse(conn.get)
|
51
48
|
end
|
52
49
|
|
53
50
|
# parse jenkins build data to determine unique list of all files that have changed accross multiple commits
|
54
|
-
def changed_files_array
|
51
|
+
def self::changed_files_array
|
55
52
|
changed_files = []
|
56
|
-
|
57
|
-
@build_data['changeSet']['items'].each do |change|
|
53
|
+
build_data['changeSet']['items'].each do |change|
|
58
54
|
changed_files.concat(change['affectedPaths'])
|
59
55
|
end
|
60
|
-
|
61
56
|
changed_files.uniq
|
62
57
|
end
|
63
58
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
objects
|
59
|
+
def self::empty_chef_object_hash
|
60
|
+
{
|
61
|
+
'environments' => {},
|
62
|
+
'roles' => {},
|
63
|
+
'data_bags' => {},
|
64
|
+
'cookbooks' => {}
|
65
|
+
}
|
73
66
|
end
|
74
67
|
|
75
68
|
# return an action based on the status of the object in the workspace
|
76
|
-
def action(file)
|
77
|
-
::File.exist?(::File.join(
|
78
|
-
end
|
79
|
-
|
80
|
-
def print
|
69
|
+
def self::action(file)
|
70
|
+
::File.exist?(::File.join(Config.config['jenkins']['workspace_dir'], file)) ? 'update' : 'delete'
|
81
71
|
end
|
82
72
|
|
83
73
|
# takes an array of files that have changes and returns hash of chef objects and an action to take on the object
|
84
|
-
def create_change_hash(files_array)
|
74
|
+
def self::create_change_hash(files_array)
|
85
75
|
objects = empty_chef_object_hash
|
86
76
|
files_array.each do |file|
|
77
|
+
split_file = file.split('/')
|
87
78
|
case
|
88
79
|
when file.match(/^[cookbook|roles|environments]/)
|
89
|
-
|
80
|
+
# "cookbooks"=>{"some-cookbook"=>"update"}
|
90
81
|
objects[split_file[0]][split_file[1]] = action(split_file[0..1].join('/'))
|
91
82
|
when file.match(/^data_bags/)
|
92
|
-
|
93
|
-
objects[split_file[0]][split_file[1]] =
|
94
|
-
objects[split_file[0]][split_file[1]][split_file[2]] = action(split_file[0..2].join('/'))
|
83
|
+
# "data_bags"=>{"some_dbag/some_item.json"=>"update"}
|
84
|
+
objects[split_file[0]][split_file[1..2].join('/')] = action(split_file[0..2].join('/'))
|
95
85
|
end
|
96
86
|
end
|
97
87
|
objects
|
data/lib/comrad/chef.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
+
# Copyright:: Copyright (c) 2014-2015 Tim Smith
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
module Comrad
|
20
|
+
# uploads / removes objects that changed from the chef server
|
21
|
+
class Chef
|
22
|
+
# Given an +item+ of +item_class+, return a knife command that performs +action+
|
23
|
+
def self::knife_command(item_class, item, action)
|
24
|
+
item_path = {
|
25
|
+
cookbooks: item,
|
26
|
+
data_bags: "data_bags/#{item}",
|
27
|
+
environments: "environments/#{item}",
|
28
|
+
roles: "roles/#{item}"
|
29
|
+
}
|
30
|
+
# Special case: if we're deleting a data_bag, we want
|
31
|
+
# "bag_name item_name" without the '.json' rather than the path to the item.
|
32
|
+
item_path[:data_bags] = item.split('/').join(' ').chomp('.json') if action == 'delete'
|
33
|
+
|
34
|
+
knife_action = { delete: 'delete', update: 'from file' }
|
35
|
+
# Special case: we call 'upload' instead of 'from file' for updated cookbooks
|
36
|
+
knife_action[:update] = 'upload' if item_class == 'cookbooks'
|
37
|
+
|
38
|
+
'knife %s %s %s' % [ # rubocop:disable FormatString
|
39
|
+
item_class.chomp('s'), # cookbook, data_bag, environment, role
|
40
|
+
knife_action[action.to_sym], # 'upload', 'delete' or 'from file'
|
41
|
+
item_path[item_class.to_sym] # cookbookname, environments/foo.json, 'dbag bagitem', etc.
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
# run the provided knife command
|
46
|
+
def self::excute_knife_cmd(cmd)
|
47
|
+
if Config.config['flags']['dryrun']
|
48
|
+
puts "I would be running '#{cmd}'"
|
49
|
+
else
|
50
|
+
puts "Live mode is not implemented. Not performing '#{cmd}'"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# perform the appropriate knife action for each item in the +changeset+
|
55
|
+
def self::process_changes
|
56
|
+
Changeset.changes.each_pair do |item_class, action_pairs|
|
57
|
+
next if action_pairs.empty?
|
58
|
+
action_pairs.each_pair do |item, action|
|
59
|
+
excute_knife_cmd(knife_command(item_class, item, action))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end # Chef class
|
64
|
+
end # Comrad Module
|
@@ -16,25 +16,34 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
require 'optparse'
|
22
|
-
rescue LoadError => e
|
23
|
-
raise "Missing gem or lib #{e}"
|
24
|
-
end
|
19
|
+
require 'yaml'
|
20
|
+
require 'optparse'
|
25
21
|
|
26
22
|
module Comrad
|
27
23
|
# builds a single config from passed flags, yaml config, and knife.rb
|
28
24
|
class Config
|
29
|
-
|
30
|
-
def
|
31
|
-
@flags
|
32
|
-
@
|
33
|
-
|
25
|
+
# return the fully-evaluated configuration hash
|
26
|
+
def self::config
|
27
|
+
@flags ||= parse_flags
|
28
|
+
@config ||= merge_configs(load_file(@flags[:config]), @flags)
|
29
|
+
end
|
30
|
+
|
31
|
+
# pretty print the config hash
|
32
|
+
def self::print(hash = config, spaces = 0)
|
33
|
+
hash.each do |k, v|
|
34
|
+
spaces.times { print ' ' }
|
35
|
+
print k.to_s + ': '
|
36
|
+
if v.class == Hash
|
37
|
+
print "\n"
|
38
|
+
print_config(v, spaces + 2)
|
39
|
+
else
|
40
|
+
puts v
|
41
|
+
end
|
42
|
+
end
|
34
43
|
end
|
35
44
|
|
36
45
|
# make sure the BUILD_NUMBER Jenkins variable exists. If not force help
|
37
|
-
def check_jenkins_env_variable
|
46
|
+
def self::check_jenkins_env_variable
|
38
47
|
if ENV['BUILD_NUMBER'].nil?
|
39
48
|
puts "Jenkins set BUILD_NUMBER environmental variable not set. Cannot continue.\n\n"
|
40
49
|
ARGV[0] = '-h'
|
@@ -42,14 +51,14 @@ module Comrad
|
|
42
51
|
end
|
43
52
|
|
44
53
|
# grabs the flags passed in via command line
|
45
|
-
def parse_flags
|
54
|
+
def self::parse_flags
|
46
55
|
check_jenkins_env_variable
|
47
56
|
|
48
57
|
flags = { config: '/etc/comrad.yml', print_config: false, quiet: false }
|
49
58
|
OptionParser.new do |opts|
|
50
59
|
opts.banner = 'Usage: comrad [options]'
|
51
60
|
|
52
|
-
opts.on('-p', '--print', 'Print the config options that
|
61
|
+
opts.on('-p', '--print', 'Print the config options that would be used, and then exit') do |config|
|
53
62
|
flags[:print_config] = config
|
54
63
|
end
|
55
64
|
|
@@ -79,42 +88,27 @@ module Comrad
|
|
79
88
|
end
|
80
89
|
|
81
90
|
# loads the comrad.yml config file from /etc/comrad.yml or the passed location
|
82
|
-
def load_file
|
83
|
-
config = YAML.load_file(
|
91
|
+
def self::load_file(file_path)
|
92
|
+
config = YAML.load_file(file_path)
|
84
93
|
if config == false
|
85
94
|
puts "ERROR: Comrad config at #{@flags[:config]} does not contain any configuration data"
|
86
95
|
exit 1
|
87
96
|
end
|
88
97
|
config
|
89
98
|
rescue Errno::ENOENT
|
90
|
-
puts "ERROR: Cannot load Comrad config file at #{
|
99
|
+
puts "ERROR: Cannot load Comrad config file at #{file_path}"
|
91
100
|
exit 1
|
92
101
|
rescue Psych::SyntaxError
|
93
|
-
puts "ERROR: Syntax error in Comrad config file at #{
|
102
|
+
puts "ERROR: Syntax error in Comrad config file at #{file_path}"
|
94
103
|
exit 1
|
95
104
|
end
|
96
105
|
|
97
106
|
# join the config file with the passed flags into a single object
|
98
|
-
def merge_configs
|
99
|
-
config =
|
107
|
+
def self::merge_configs(file_config, flags_config)
|
108
|
+
config = file_config.dup
|
100
109
|
config['flags'] = {}
|
101
|
-
|
110
|
+
flags_config.each { |k, v| config['flags'][k.to_s] = v }
|
102
111
|
config
|
103
112
|
end
|
104
|
-
|
105
|
-
# pretty print the config hash
|
106
|
-
def print(hash = nil, spaces = 0)
|
107
|
-
hash = @settings if hash.nil?
|
108
|
-
hash.each do |k, v|
|
109
|
-
spaces.times { print ' ' }
|
110
|
-
print k.to_s + ': '
|
111
|
-
if v.class == Hash
|
112
|
-
print "\n"
|
113
|
-
print_config(v, spaces + 2)
|
114
|
-
else
|
115
|
-
puts v
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
113
|
end
|
120
114
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
+
# Copyright:: Copyright (c) 2014-2015 Tim Smith
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'slack/post'
|
20
|
+
|
21
|
+
module Comrad
|
22
|
+
# send notifications to slack
|
23
|
+
class Notifier
|
24
|
+
def self::configure
|
25
|
+
Slack::Post.configure(
|
26
|
+
webhook_url: Config.config['slack']['webhook_url'],
|
27
|
+
username: 'comrad',
|
28
|
+
channel: Config.config['slack']['channel']
|
29
|
+
)
|
30
|
+
@configured = true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Post a Slack notification containing details of the changes
|
34
|
+
def self::notify_changes
|
35
|
+
unless Config.config['flags']['quiet']
|
36
|
+
configure unless @configured
|
37
|
+
Slack::Post.post_with_attachments(
|
38
|
+
'Comrad action for chef repo <%s|Build #%s>' % [ # rubocop:disable FormatString
|
39
|
+
Changeset.build_data['url'],
|
40
|
+
Changeset.build_data['number']],
|
41
|
+
changeset_attachment)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Generate a Slack message attachment that contains changeset details
|
46
|
+
def self::changeset_attachment
|
47
|
+
attach = { fallback: '', color: '#36a64f', fields: [] }
|
48
|
+
|
49
|
+
Changeset.changes.each_pair do |item_class, action_pairs|
|
50
|
+
next if action_pairs.empty?
|
51
|
+
text = ''
|
52
|
+
action_pairs.each_pair do |item, action|
|
53
|
+
text << "#{action} #{item}\n"
|
54
|
+
end
|
55
|
+
attach[:fallback] << "#{item_class}:\n#{text}"
|
56
|
+
attach[:fields] << { title: item_class, value: text, short: false }
|
57
|
+
end
|
58
|
+
|
59
|
+
[attach]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: comrad
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -39,19 +39,19 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '11.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: slack-post
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '0.3'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '0.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,12 +98,12 @@ files:
|
|
98
98
|
- bin/comrad
|
99
99
|
- comrad.gemspec
|
100
100
|
- comrad.yml.EXAMPLE
|
101
|
-
- lib/
|
102
|
-
- lib/
|
103
|
-
- lib/chef.rb
|
104
|
-
- lib/config.rb
|
105
|
-
- lib/
|
106
|
-
- lib/string.rb
|
101
|
+
- lib/comrad.rb
|
102
|
+
- lib/comrad/changeset.rb
|
103
|
+
- lib/comrad/chef.rb
|
104
|
+
- lib/comrad/config.rb
|
105
|
+
- lib/comrad/notifier.rb
|
106
|
+
- lib/core_ext/string.rb
|
107
107
|
homepage: http://www.github.com/tas50/Comrad
|
108
108
|
licenses:
|
109
109
|
- Apache-2.0
|
@@ -113,7 +113,7 @@ rdoc_options:
|
|
113
113
|
- --line-numbers
|
114
114
|
- --inline-source
|
115
115
|
- --title
|
116
|
-
-
|
116
|
+
- comrad
|
117
117
|
- --main
|
118
118
|
- README.md
|
119
119
|
require_paths:
|
data/lib/application.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
#
|
3
|
-
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
-
# Copyright:: Copyright (c) 2014-2015 Tim Smith
|
5
|
-
# License:: Apache License, Version 2.0
|
6
|
-
#
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
-
# you may not use this file except in compliance with the License.
|
9
|
-
# You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
-
# See the License for the specific language governing permissions and
|
17
|
-
# limitations under the License.
|
18
|
-
|
19
|
-
module Comrad
|
20
|
-
# the main class for the comrad app. gets called by the comrad bin
|
21
|
-
class Application
|
22
|
-
require 'config'
|
23
|
-
require 'change'
|
24
|
-
require 'chef'
|
25
|
-
require 'string'
|
26
|
-
require 'slack'
|
27
|
-
|
28
|
-
def initialize
|
29
|
-
@config_obj = Comrad::Config.new
|
30
|
-
@config = @config_obj.settings
|
31
|
-
@changes = Comrad::Change.new(@config).changes
|
32
|
-
end
|
33
|
-
|
34
|
-
# exit with a friendly message if nothing we test has been changed
|
35
|
-
def check_empty_update
|
36
|
-
objects_updated = false
|
37
|
-
%w(cookbooks roles environments data_bags).each do |object|
|
38
|
-
objects_updated = true unless @changes[object].empty?
|
39
|
-
end
|
40
|
-
|
41
|
-
('No objects to test. Exiting'.to_green && exit) unless objects_updated
|
42
|
-
end
|
43
|
-
|
44
|
-
# check and see if the -p flag was passed and if so print the config hash
|
45
|
-
def check_print_config
|
46
|
-
if @config['flags']['print_config']
|
47
|
-
'Current config file / CLI flag values'.marquee
|
48
|
-
@config_obj.print
|
49
|
-
exit
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# run tests on each changed cookbook
|
54
|
-
def run
|
55
|
-
check_print_config
|
56
|
-
('No objects updated by this commit. Exiting'.to_green && exit) if check_empty_update
|
57
|
-
|
58
|
-
# print objects that will be uploaded
|
59
|
-
'The following chef objects will be changed'.marquee
|
60
|
-
puts @changes
|
61
|
-
|
62
|
-
'Making Chef Changes'.marquee
|
63
|
-
Comrad::Chef.new(@config, @changes).run
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
data/lib/chef.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
#
|
3
|
-
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
-
# Copyright:: Copyright (c) 2014-2015 Tim Smith
|
5
|
-
# License:: Apache License, Version 2.0
|
6
|
-
#
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
-
# you may not use this file except in compliance with the License.
|
9
|
-
# You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
-
# See the License for the specific language governing permissions and
|
17
|
-
# limitations under the License.
|
18
|
-
|
19
|
-
begin
|
20
|
-
require 'ridley'
|
21
|
-
rescue LoadError => e
|
22
|
-
raise "Missing gem or lib #{e}"
|
23
|
-
end
|
24
|
-
|
25
|
-
module Comrad
|
26
|
-
# uploads / removes objects that changed from the chef server
|
27
|
-
class Chef < Application
|
28
|
-
def initialize(config, changes)
|
29
|
-
@config = config
|
30
|
-
@changes = changes
|
31
|
-
@slack = Comrad::Slack.new(config)
|
32
|
-
end
|
33
|
-
|
34
|
-
# builds a string of what the action is / would be depending on dry run or not
|
35
|
-
def action_string(action, trailing_text)
|
36
|
-
string = @config['flags']['dryrun'] ? " - I would #{action} " : " - #{action.capitalize.chomp('e')}ing "
|
37
|
-
string + trailing_text
|
38
|
-
end
|
39
|
-
|
40
|
-
# a really horrible method to build knife commands based on item / action / item type (cookbook/role/environment/data_bag)
|
41
|
-
def build_knife_cmd(type, action, item1, item2 = nil)
|
42
|
-
if type == 'data_bags'
|
43
|
-
action == 'delete' ? "knife data bag delete #{item1} #{item2}" : "knife data bag from file #{item1} data_bags/#{item1}/#{item2}"
|
44
|
-
elsif action == 'delete'
|
45
|
-
"knife #{type.chomp('s')} delete #{item1} #{item2}"
|
46
|
-
elsif type == 'cookbooks'
|
47
|
-
"knife cookbook #{action == 'update' ? 'upload' : 'delete'} #{item1}"
|
48
|
-
else
|
49
|
-
"knife #{type.chomp('s')} from file #{type}/#{item1}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# run the provided knife command
|
54
|
-
def excute_knife_cmd(cmd)
|
55
|
-
if @config['flags']['dryrun']
|
56
|
-
@slack.slack_put(" - I would be running #{cmd}")
|
57
|
-
else
|
58
|
-
@slack.slack_put(' - Non-dry mode is not implemented. Doing nothing')
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# main method of the class. Iterates over the changes passed in and kicks off actions / slack messaging
|
63
|
-
def take_actions
|
64
|
-
@changes.each_pair do |type, name|
|
65
|
-
next if name.empty?
|
66
|
-
case
|
67
|
-
when type.match(/^['environments|roles']/)
|
68
|
-
name.each_pair do |item, action|
|
69
|
-
@slack.slack_put(action_string(action, "#{item}"))
|
70
|
-
excute_knife_cmd(build_knife_cmd(type, action, item))
|
71
|
-
end
|
72
|
-
when type == 'cookbooks'
|
73
|
-
name.each_pair do |item, action|
|
74
|
-
@slack.slack_put(action_string(action, "#{item}"))
|
75
|
-
excute_knife_cmd(build_knife_cmd(type, action, item))
|
76
|
-
end
|
77
|
-
when type == 'data_bags'
|
78
|
-
name.each_pair do |bag, item|
|
79
|
-
item.each_pair do |bag_item_name, action|
|
80
|
-
@slack.slack_put(action_string(action, "#{bag}::#{bag_item_name} data bag item"))
|
81
|
-
excute_knife_cmd(build_knife_cmd(type, action, bag, bag_item_name))
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# called by application to perform actions
|
89
|
-
def run
|
90
|
-
@slack.slack_put("Comrad action for chef repo build # #{ENV['BUILD_NUMBER']}:")
|
91
|
-
take_actions
|
92
|
-
end
|
93
|
-
end # Chef class
|
94
|
-
end # Comrad Module
|
data/lib/slack.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
#
|
3
|
-
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
-
# Copyright:: Copyright (c) 2014-2015 Tim Smith
|
5
|
-
# License:: Apache License, Version 2.0
|
6
|
-
#
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
-
# you may not use this file except in compliance with the License.
|
9
|
-
# You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
-
# See the License for the specific language governing permissions and
|
17
|
-
# limitations under the License.
|
18
|
-
|
19
|
-
require 'rest-client'
|
20
|
-
require 'json'
|
21
|
-
|
22
|
-
# base module
|
23
|
-
module Comrad
|
24
|
-
# send notifications to slack
|
25
|
-
class Slack
|
26
|
-
def initialize(config)
|
27
|
-
@config = config
|
28
|
-
@url = config['slack']['webhook_url']
|
29
|
-
end
|
30
|
-
|
31
|
-
def slack_put(text)
|
32
|
-
puts text
|
33
|
-
post(text) unless @config['flags']['quiet']
|
34
|
-
end
|
35
|
-
|
36
|
-
def post(text)
|
37
|
-
RestClient.post @url, create_message(text).to_json
|
38
|
-
end
|
39
|
-
|
40
|
-
def create_message(text)
|
41
|
-
message = {}
|
42
|
-
message['text'] = text
|
43
|
-
message
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|