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