reagan 0.8.2 → 1.0.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/.rubocop.yml +11 -17
- data/.travis.yml +0 -1
- data/Gemfile +1 -8
- data/README.md +3 -4
- data/bin/reagan +3 -2
- data/lib/core/ext/string.rb +35 -0
- data/lib/reagan.rb +82 -0
- data/lib/{change.rb → reagan/changeset.rb} +30 -33
- data/lib/reagan/config.rb +139 -0
- data/lib/{test_json.rb → reagan/test_json.rb} +4 -9
- data/lib/{test_knife.rb → reagan/test_knife.rb} +3 -4
- data/lib/{test_reagan.rb → reagan/test_reagan.rb} +6 -7
- data/lib/{test_version.rb → reagan/test_version.rb} +11 -13
- data/reagan.gemspec +2 -2
- metadata +12 -11
- data/lib/application.rb +0 -105
- data/lib/config.rb +0 -118
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1cd896de4c2788114697c7ff8df89e864324d2b0
|
4
|
+
data.tar.gz: 59f8878ea5836ef1f887553a2c8bedd2e9abfd06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13f87c4a768d237e634f8b1b556709b3d0c412d3e6f6d787870c37926d4cff43f175362de90428d1f4717357303545f6873a1f2d2f1f4e98d108943c4377170a
|
7
|
+
data.tar.gz: b6d4738eb02e1f04eea2a34b3fc95e5a8e9e70af97e1db37c36a07a1386013a80f5b8cacd2ec925bd5b3b3dd8650547635f181c5c49098d5d333f7cd8117e53c
|
data/.rubocop.yml
CHANGED
@@ -1,24 +1,18 @@
|
|
1
|
-
|
2
|
-
Exclude:
|
3
|
-
- vendor/**
|
4
|
-
|
5
|
-
AlignParameters:
|
6
|
-
Enabled: false
|
7
|
-
CyclomaticComplexity:
|
8
|
-
Enabled: false
|
9
|
-
Encoding:
|
10
|
-
Enabled: false
|
11
|
-
HashSyntax:
|
1
|
+
AbcSize:
|
12
2
|
Enabled: false
|
13
|
-
|
3
|
+
|
4
|
+
Documentation:
|
14
5
|
Enabled: false
|
6
|
+
|
15
7
|
MethodLength:
|
16
8
|
Enabled: false
|
17
|
-
|
18
|
-
|
19
|
-
NumericLiterals:
|
20
|
-
Enabled: false
|
21
|
-
SingleSpaceBeforeFirstArg:
|
9
|
+
|
10
|
+
LineLength:
|
22
11
|
Enabled: false
|
12
|
+
|
23
13
|
PerceivedComplexity:
|
24
14
|
Enabled: false
|
15
|
+
|
16
|
+
CyclomaticComplexity:
|
17
|
+
Enabled: false
|
18
|
+
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -30,12 +30,11 @@ While this app is written to be run as a Jenkins job it can also be run locally.
|
|
30
30
|
```ruby
|
31
31
|
source 'https://rubygems.org'
|
32
32
|
|
33
|
-
gem 'chef', '~>
|
34
|
-
gem 'berkshelf', '~> 3.2'
|
33
|
+
gem 'chef', '~> 12.3'
|
35
34
|
|
36
35
|
group :test do
|
37
|
-
gem 'reagan', '~> 0.
|
38
|
-
gem 'rubocop', '~> 0.
|
36
|
+
gem 'reagan', '~> 0.8'
|
37
|
+
gem 'rubocop', '~> 0.30'
|
39
38
|
gem 'foodcritic', '~> 4.0'
|
40
39
|
end
|
41
40
|
```
|
data/bin/reagan
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
2
3
|
#
|
3
4
|
# Author:: Tim Smith (<tim@cozy.co>)
|
4
5
|
# Copyright:: Copyright (c) 2015 Tim Smith
|
@@ -19,6 +20,6 @@
|
|
19
20
|
# load the libs
|
20
21
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
21
22
|
|
22
|
-
require '
|
23
|
+
require 'reagan'
|
23
24
|
|
24
|
-
Reagan
|
25
|
+
Reagan.run
|
@@ -0,0 +1,35 @@
|
|
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
|
+
# add a simple method for making marquees
|
20
|
+
class String
|
21
|
+
def marquee
|
22
|
+
puts "\n#{self}"
|
23
|
+
length.times { printf '-' }
|
24
|
+
puts "\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_red
|
28
|
+
"\033[31m#{self}\033[0m"
|
29
|
+
end
|
30
|
+
|
31
|
+
def indent(double_space_count = 1)
|
32
|
+
double_space_count.times { insert(0, ' ') }
|
33
|
+
self
|
34
|
+
end
|
35
|
+
end
|
data/lib/reagan.rb
ADDED
@@ -0,0 +1,82 @@
|
|
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
|
+
# main Reagan application to which gathers configs, determines changes, and runs tests
|
20
|
+
module Reagan
|
21
|
+
require 'core/ext/string'
|
22
|
+
require 'reagan/config'
|
23
|
+
require 'reagan/changeset'
|
24
|
+
require 'reagan/test_json'
|
25
|
+
require 'reagan/test_knife'
|
26
|
+
require 'reagan/test_reagan'
|
27
|
+
require 'reagan/test_version'
|
28
|
+
|
29
|
+
# exit with a friendly message if nothing we test has been changed
|
30
|
+
def self::check_empty_update
|
31
|
+
if ChangeSet.empty?
|
32
|
+
'No objects to test. Exiting'.marquee
|
33
|
+
exit 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# check and see if the -p flag was passed and if so print the config hash
|
38
|
+
def self::check_print_config
|
39
|
+
return unless Config.settings['flags']['print_config']
|
40
|
+
'Current config file / CLI flag values'.marquee
|
41
|
+
Config.pretty_print
|
42
|
+
exit 0
|
43
|
+
end
|
44
|
+
|
45
|
+
# run tests on each changed cookbook
|
46
|
+
def self::run
|
47
|
+
check_print_config
|
48
|
+
check_empty_update
|
49
|
+
|
50
|
+
# print objects that will be tested
|
51
|
+
'The following chef objects will be tested'.marquee
|
52
|
+
%w(cookbooks roles environments data_bags).each do |type|
|
53
|
+
unless ChangeSet.files[type].empty?
|
54
|
+
puts "#{type}:"
|
55
|
+
ChangeSet.files[type].each { |obj| puts ' ' + obj }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
results = []
|
60
|
+
ChangeSet.files['cookbooks'].each do |cookbook|
|
61
|
+
"Testing cookbook #{cookbook}".marquee
|
62
|
+
results << Reagan::TestKnife.new(cookbook).test
|
63
|
+
results << Reagan::TestVersion.new(cookbook).test
|
64
|
+
results << Reagan::TestReagan.new(cookbook).test
|
65
|
+
end
|
66
|
+
|
67
|
+
%w(data_bags roles environments).each do |type|
|
68
|
+
ChangeSet.files[type].each do |file|
|
69
|
+
"Testing #{type} file #{file}".marquee
|
70
|
+
results << TestJSON.new(file).test
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# print success or failure
|
75
|
+
failure = results.include?(false)
|
76
|
+
text = failure ? 'Reagan testing has failed' : 'All Reagan tests have suceeded'
|
77
|
+
text.to_red.marquee
|
78
|
+
|
79
|
+
# if any test failed then exit 1 so jenkins can pick up the failure
|
80
|
+
exit 1 if failure
|
81
|
+
end
|
82
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
#
|
3
3
|
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
-
# Copyright:: Copyright (c) 2014 Tim Smith
|
4
|
+
# Copyright:: Copyright (c) 2014-2015 Tim Smith
|
5
5
|
# License:: Apache License, Version 2.0
|
6
6
|
#
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -17,7 +17,6 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
19
|
begin
|
20
|
-
require 'rubygems'
|
21
20
|
require 'octokit'
|
22
21
|
rescue LoadError => e
|
23
22
|
raise "Missing gem or lib #{e}"
|
@@ -25,32 +24,40 @@ end
|
|
25
24
|
|
26
25
|
module Reagan
|
27
26
|
# determines changed files in the commit
|
28
|
-
class
|
29
|
-
|
30
|
-
|
31
|
-
@config = Reagan::Change.config
|
32
|
-
@files = changed_files
|
27
|
+
class ChangeSet
|
28
|
+
def self::files
|
29
|
+
@files ||= changed_files
|
33
30
|
end
|
34
31
|
|
35
32
|
# return hash of chef objects that have been changed
|
36
|
-
def changed_files
|
37
|
-
if
|
33
|
+
def self::changed_files
|
34
|
+
if Config.settings['flags']['override_cookbooks']
|
38
35
|
files_from_override
|
39
36
|
else
|
40
|
-
pull =
|
37
|
+
pull = Config.settings['flags']['pull']
|
41
38
|
puts "Grabbing contents of pull request #{pull}\n"
|
42
39
|
hash_builder(query_gh(pull))
|
43
40
|
end
|
44
41
|
end
|
45
42
|
|
43
|
+
# check if the changeset is empty
|
44
|
+
def self::empty?
|
45
|
+
objects_updated = false
|
46
|
+
%w(cookbooks roles environments data_bags).each do |object|
|
47
|
+
objects_updated = true unless files[object].empty?
|
48
|
+
puts files
|
49
|
+
end
|
50
|
+
!objects_updated
|
51
|
+
end
|
52
|
+
|
46
53
|
# build a files hash based on the override cookbooks passed by the user
|
47
|
-
def files_from_override
|
54
|
+
def self::files_from_override
|
48
55
|
files = {}
|
49
56
|
%w(environments roles data_bags).each { |object| files[object] = {} }
|
50
57
|
|
51
58
|
# ensure that the passed cookbooks exist in the workspace first
|
52
59
|
cookbooks = []
|
53
|
-
|
60
|
+
Config.settings['flags']['override_cookbooks'].each do |cb|
|
54
61
|
if object_still_exists(::File.join('cookbooks/', cb))
|
55
62
|
cookbooks << cb
|
56
63
|
else
|
@@ -62,29 +69,19 @@ module Reagan
|
|
62
69
|
files
|
63
70
|
end
|
64
71
|
|
65
|
-
# fetch pull num from either ENV or CLI param
|
66
|
-
def pull_num
|
67
|
-
if @config['flags']['pull']
|
68
|
-
pull = @config['flags']['pull']
|
69
|
-
elsif ENV['ghprbPullId']
|
70
|
-
pull = ENV['ghprbPullId']
|
71
|
-
else
|
72
|
-
puts 'Jenkins ghprbPullId environmental variable not set or --pull option not used. Cannot continue'
|
73
|
-
exit 1
|
74
|
-
end
|
75
|
-
pull
|
76
|
-
end
|
77
|
-
|
78
72
|
# queries github for the files that have changed
|
79
|
-
def query_gh(pull_num)
|
73
|
+
def self::query_gh(pull_num)
|
80
74
|
Octokit.auto_paginate = true # avoids issues with large commits
|
81
|
-
Octokit.api_endpoint =
|
82
|
-
gh = Octokit::Client.new(:
|
83
|
-
files_from_pull(gh.pull_request_files(
|
75
|
+
Octokit.api_endpoint = Config.settings['github']['api_endpoint'] || 'https://api.github.com'
|
76
|
+
gh = Octokit::Client.new(access_token: Config.settings['github']['auth_token'])
|
77
|
+
files_from_pull(gh.pull_request_files(Config.settings['github']['repo'], pull_num))
|
78
|
+
rescue
|
79
|
+
puts 'Failed to query pull request contents from Github. Check the api_endpoint, repo, and token.'
|
80
|
+
exit 1
|
84
81
|
end
|
85
82
|
|
86
83
|
# convert pull request response to array of changed files
|
87
|
-
def files_from_pull(pull_changes)
|
84
|
+
def self::files_from_pull(pull_changes)
|
88
85
|
files = []
|
89
86
|
pull_changes.each do |file|
|
90
87
|
files << file[:filename]
|
@@ -93,12 +90,12 @@ module Reagan
|
|
93
90
|
end
|
94
91
|
|
95
92
|
# check to see if the file exists in the workspace so we don't test deleted objects
|
96
|
-
def object_still_exists(file)
|
97
|
-
::File.exist?(::File.join(
|
93
|
+
def self::object_still_exists(file)
|
94
|
+
::File.exist?(::File.join(Config.settings['jenkins']['workspace_dir'], file))
|
98
95
|
end
|
99
96
|
|
100
97
|
# builds a hash of files / cookbooks that changed based on the pull data from GH
|
101
|
-
def hash_builder(pull_files)
|
98
|
+
def self::hash_builder(pull_files)
|
102
99
|
files = {}
|
103
100
|
%w(environments roles data_bags cookbooks).each { |object| files[object] = [] }
|
104
101
|
cookbooks = []
|
@@ -0,0 +1,139 @@
|
|
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 'yaml'
|
21
|
+
require 'optparse'
|
22
|
+
rescue LoadError => e
|
23
|
+
raise "Missing gem or lib #{e}"
|
24
|
+
end
|
25
|
+
|
26
|
+
module Reagan
|
27
|
+
# builds a single config from passed flags, yaml config, and knife.rb
|
28
|
+
class Config
|
29
|
+
# lazy load config settings
|
30
|
+
def self::settings
|
31
|
+
@settings ||= merge_config
|
32
|
+
end
|
33
|
+
|
34
|
+
# pretty print the config hash
|
35
|
+
def self::pretty_print(hash = nil, spaces = 0)
|
36
|
+
hash = @settings if hash.nil?
|
37
|
+
hash.each do |k, v|
|
38
|
+
spaces.times { print ' ' }
|
39
|
+
print k.to_s + ': '
|
40
|
+
if v.class == Hash
|
41
|
+
print "\n"
|
42
|
+
pretty_print(v, spaces + 2)
|
43
|
+
else
|
44
|
+
puts v
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# grabs the flags passed in via command line
|
50
|
+
def self::cli_flags
|
51
|
+
if @cli_flags
|
52
|
+
@cli_flags
|
53
|
+
else
|
54
|
+
flags = { 'config' => '/etc/reagan.yml' }
|
55
|
+
OptionParser.new do |opts|
|
56
|
+
opts.banner = 'Usage: reagan [options]'
|
57
|
+
opts.on('-o', '--override cb1,cb2', Array, 'Comma separated list of cookbooks to test') do |cookbooks|
|
58
|
+
flags[:override_cookbooks] = cookbooks
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on('-p', '--print', 'Print the config options that will be used') do |config|
|
62
|
+
flags['print_config'] = config
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on('-p', '--pull_num 123', Integer, 'Github pull number to test') do |pull|
|
66
|
+
flags['pull'] = pull
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on('-c', '--config reagan.yml', 'Path to config file (defaults to /etc/reagan.yml)') do |config|
|
70
|
+
flags['config'] = config
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on('-h', '--help', 'Displays Help') do
|
74
|
+
puts opts
|
75
|
+
exit
|
76
|
+
end
|
77
|
+
end.parse!
|
78
|
+
|
79
|
+
@cli_flags = flags
|
80
|
+
flags
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# loads the reagan.yml config file from /etc/reagan.yml or the passed location
|
85
|
+
def self::config_file
|
86
|
+
config = YAML.load_file(cli_flags['config'])
|
87
|
+
|
88
|
+
config
|
89
|
+
rescue Errno::ENOENT
|
90
|
+
puts "ERROR: Cannot load Reagan config file at #{cli_flags['config']}".to_red
|
91
|
+
exit 1
|
92
|
+
rescue Psych::SyntaxError
|
93
|
+
puts "ERROR: Syntax error in Reagan config file at #{cli_flags['config']}".to_red
|
94
|
+
exit 1
|
95
|
+
end
|
96
|
+
|
97
|
+
# make sure the config was properly loaded and contains the various keys we need
|
98
|
+
def self::validate
|
99
|
+
settings = Config.Settings
|
100
|
+
|
101
|
+
if settings == false
|
102
|
+
puts "ERROR: Reagan config at #{cli_flags['config']} does not contain any configuration data".to_red
|
103
|
+
exit 1
|
104
|
+
end
|
105
|
+
|
106
|
+
# make sure a pull request is specified
|
107
|
+
unless settings['flags']['pull']
|
108
|
+
puts 'Jenkins ghprbPullId environmental variable not set or --pull option not used. Cannot continue'
|
109
|
+
exit 1
|
110
|
+
end
|
111
|
+
|
112
|
+
# if workstation not defined in the config file try to use the Jenkins workspace variable
|
113
|
+
unless settings['jenkins'] && loaded_file['jenkins']['workspace_dir']
|
114
|
+
workspace = ENV['WORKSPACE']
|
115
|
+
if workspace
|
116
|
+
settings['jenkins'] = {}
|
117
|
+
settings['jenkins']['workspace_dir'] = workspace
|
118
|
+
else
|
119
|
+
puts 'Jenkins workspace_dir not defined in the config file and $WORKSPACE env variable empty. Exiting'.to_red
|
120
|
+
exit
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# join the config file with the passed flags into a single object
|
126
|
+
def self::merge_config
|
127
|
+
config = config_file
|
128
|
+
config['flags'] = {}
|
129
|
+
config['flags'].merge!(cli_flags)
|
130
|
+
|
131
|
+
# if no pull request provided at the CLI use the Jenkins environmental variable
|
132
|
+
unless config['flags']['pull']
|
133
|
+
config['flags']['pull'] = ENV['ghprbPullId']
|
134
|
+
end
|
135
|
+
|
136
|
+
config
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -16,17 +16,12 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
|
20
|
-
require 'json'
|
21
|
-
rescue LoadError => e
|
22
|
-
raise "Missing gem or lib #{e}"
|
23
|
-
end
|
19
|
+
require 'json'
|
24
20
|
|
25
21
|
module Reagan
|
26
22
|
# tests to make sure the version has been updated on the cookbook
|
27
|
-
class
|
23
|
+
class TestJSON
|
28
24
|
def initialize(file)
|
29
|
-
@config = Reagan::TestJSON.config
|
30
25
|
@file = file
|
31
26
|
end
|
32
27
|
|
@@ -35,13 +30,13 @@ module Reagan
|
|
35
30
|
def test
|
36
31
|
puts 'Running JSON parsing test:'
|
37
32
|
begin
|
38
|
-
json_file = File.read(File.join(
|
33
|
+
json_file = File.read(File.join(Config.settings['jenkins']['workspace_dir'], @file))
|
39
34
|
JSON.parse(json_file)
|
40
35
|
success = true
|
41
36
|
rescue JSON::JSONError
|
42
37
|
success = false
|
43
38
|
end
|
44
|
-
puts success ? '
|
39
|
+
puts success ? 'PASS: JSON parses'.indent : 'FAIL: JSON does NOT parse'.indent
|
45
40
|
success
|
46
41
|
end
|
47
42
|
end
|
@@ -18,9 +18,8 @@
|
|
18
18
|
|
19
19
|
module Reagan
|
20
20
|
# tests cookbooks using knife cookbook test functionality
|
21
|
-
class TestKnife
|
21
|
+
class TestKnife
|
22
22
|
def initialize(cookbook)
|
23
|
-
@config = Reagan::TestKnife.config
|
24
23
|
@cookbook = cookbook
|
25
24
|
end
|
26
25
|
|
@@ -28,10 +27,10 @@ module Reagan
|
|
28
27
|
# returns true if cookbook passed or false if it failed
|
29
28
|
def test
|
30
29
|
# grab the version of the cookbook in the local metadata
|
31
|
-
result = system "knife cookbook test -o #{File.join(
|
30
|
+
result = system "knife cookbook test -o #{File.join(Config.settings['jenkins']['workspace_dir'], 'cookbooks')} #{@cookbook} > /dev/null 2>&1"
|
32
31
|
|
33
32
|
puts 'Running knife cookbook test:'
|
34
|
-
puts result ? '
|
33
|
+
puts result ? 'PASS: Knife cookbook test was successful'.indent : 'FAIL: Knife cookbookk test was NOT successful'.indent
|
35
34
|
result
|
36
35
|
end
|
37
36
|
end
|
@@ -18,9 +18,8 @@
|
|
18
18
|
|
19
19
|
module Reagan
|
20
20
|
# tests cookbooks using tests defined in reagan_test.yml files
|
21
|
-
class TestReagan
|
21
|
+
class TestReagan
|
22
22
|
def initialize(cookbook)
|
23
|
-
@config = Reagan::TestReagan.config
|
24
23
|
@cookbook = cookbook
|
25
24
|
end
|
26
25
|
|
@@ -28,13 +27,13 @@ module Reagan
|
|
28
27
|
def test
|
29
28
|
puts 'Running reagan_test.yml defined tests:'
|
30
29
|
# check to see if a reagan_test.yml file exists
|
31
|
-
if File.exist?(File.join(
|
30
|
+
if File.exist?(File.join(Config.settings['jenkins']['workspace_dir'], 'cookbooks', @cookbook, 'reagan_test.yml'))
|
32
31
|
|
33
32
|
# load the reagan config file
|
34
|
-
reagan_def = YAML.load_file(File.join(
|
33
|
+
reagan_def = YAML.load_file(File.join(Config.settings['jenkins']['workspace_dir'], 'cookbooks', @cookbook, 'reagan_test.yml'))
|
35
34
|
|
36
35
|
# change into the cookbook dir so rake tests run locally
|
37
|
-
Dir.chdir(File.join(
|
36
|
+
Dir.chdir(File.join(Config.settings['jenkins']['workspace_dir'], 'cookbooks', @cookbook))
|
38
37
|
|
39
38
|
status = true
|
40
39
|
reagan_def['tests'].each do |test|
|
@@ -42,10 +41,10 @@ module Reagan
|
|
42
41
|
result = system test
|
43
42
|
status = false if result == false
|
44
43
|
end
|
45
|
-
puts status ? '
|
44
|
+
puts status ? 'PASS: reagan_test.yml test was successful' : ' FAIL: reagan_test.yml test was NOT successful'.indent
|
46
45
|
status
|
47
46
|
else
|
48
|
-
puts '
|
47
|
+
puts 'SKIP: No reagan_test.yml file found in the cookbook path. Skipping test'.indent
|
49
48
|
status
|
50
49
|
end
|
51
50
|
end
|
@@ -17,7 +17,6 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
19
|
begin
|
20
|
-
require 'rubygems'
|
21
20
|
require 'ridley'
|
22
21
|
require 'chef/cookbook/metadata'
|
23
22
|
rescue LoadError => e
|
@@ -26,16 +25,15 @@ end
|
|
26
25
|
|
27
26
|
module Reagan
|
28
27
|
# tests to make sure the version has been updated on the cookbook
|
29
|
-
class
|
28
|
+
class TestVersion
|
30
29
|
def initialize(cookbook)
|
31
|
-
@config = Reagan::TestVersion.config
|
32
30
|
@cookbook = cookbook
|
33
31
|
end
|
34
32
|
|
35
33
|
# grab the version of the cookbook in the local metadata
|
36
34
|
def check_commit_version
|
37
35
|
metadata = Chef::Cookbook::Metadata.new
|
38
|
-
metadata.from_file(File.join(
|
36
|
+
metadata.from_file(File.join(Config.settings['jenkins']['workspace_dir'], 'cookbooks', @cookbook, 'metadata.rb'))
|
39
37
|
metadata.version
|
40
38
|
end
|
41
39
|
|
@@ -44,11 +42,11 @@ module Reagan
|
|
44
42
|
Ridley::Logging.logger.level = Logger.const_get 'ERROR'
|
45
43
|
begin
|
46
44
|
server_con = Ridley.new(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
server_url: Config.settings['chef']['server_url'],
|
46
|
+
client_name: Config.settings['chef']['client_name'],
|
47
|
+
client_key: Config.settings['chef']['pem_path'],
|
48
|
+
ssl: { verify: false }
|
49
|
+
)
|
52
50
|
# return the version if the cookbook exists or return 0.0.0 if it's a new coobook not yet on the server
|
53
51
|
if server_con.cookbook.all[@cookbook]
|
54
52
|
server_con.cookbook.all[@cookbook][0]
|
@@ -56,7 +54,7 @@ module Reagan
|
|
56
54
|
'0.0.0'
|
57
55
|
end
|
58
56
|
rescue Ridley::Errors::ConnectionFailed
|
59
|
-
puts ' ERROR: Failed to connect to the Chef server. Cannot continue'
|
57
|
+
puts ' ERROR: Failed to connect to the Chef server. Cannot continue'.to_red
|
60
58
|
exit 1
|
61
59
|
end
|
62
60
|
end
|
@@ -69,9 +67,9 @@ module Reagan
|
|
69
67
|
updated = Gem::Version.new(serv_v) < Gem::Version.new(commit_v) ? true : false
|
70
68
|
|
71
69
|
puts "Running cookbook version rev'd test:"
|
72
|
-
puts "
|
73
|
-
puts "
|
74
|
-
puts updated ? "
|
70
|
+
puts "Server version: #{serv_v}".indent
|
71
|
+
puts "Commit version: #{commit_v}".indent
|
72
|
+
puts updated ? "PASS: Metadata version has been rev'd".indent : "FAIL: Metadata version has NOT been rev'd".indent
|
75
73
|
updated
|
76
74
|
end
|
77
75
|
end
|
data/reagan.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'reagan'
|
3
|
-
s.version = '0.
|
3
|
+
s.version = '1.0.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']
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.add_dependency 'chef', '>= 11.0'
|
17
17
|
s.add_dependency 'ridley', '~> 4.0'
|
18
18
|
s.add_development_dependency 'rake', '~> 10.0'
|
19
|
-
s.add_development_dependency 'rubocop', '~> 0.
|
19
|
+
s.add_development_dependency 'rubocop', '~> 0.30.1'
|
20
20
|
|
21
21
|
s.files = `git ls-files -z`.split("\x0")
|
22
22
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reagan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.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-
|
11
|
+
date: 2015-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: octokit
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - ~>
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
75
|
+
version: 0.30.1
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - ~>
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
82
|
+
version: 0.30.1
|
83
83
|
description: Trust But Verify - Ruby build script for Jenkins that automates the testing
|
84
84
|
of Chef cookbooks
|
85
85
|
email: tim@cozy.co
|
@@ -97,13 +97,14 @@ files:
|
|
97
97
|
- README.md
|
98
98
|
- Rakefile
|
99
99
|
- bin/reagan
|
100
|
-
- lib/
|
101
|
-
- lib/
|
102
|
-
- lib/
|
103
|
-
- lib/
|
104
|
-
- lib/
|
105
|
-
- lib/
|
106
|
-
- lib/
|
100
|
+
- lib/core/ext/string.rb
|
101
|
+
- lib/reagan.rb
|
102
|
+
- lib/reagan/changeset.rb
|
103
|
+
- lib/reagan/config.rb
|
104
|
+
- lib/reagan/test_json.rb
|
105
|
+
- lib/reagan/test_knife.rb
|
106
|
+
- lib/reagan/test_reagan.rb
|
107
|
+
- lib/reagan/test_version.rb
|
107
108
|
- reagan.gemspec
|
108
109
|
- reagan.yml.EXAMPLE
|
109
110
|
- reagan_test.yml.EXAMPLE
|
data/lib/application.rb
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
#
|
3
|
-
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
-
# Copyright:: Copyright (c) 2014 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 Reagan
|
20
|
-
# the main class for the reagan app. gets called by the reagan bin
|
21
|
-
class Application
|
22
|
-
require 'config'
|
23
|
-
require 'change'
|
24
|
-
require 'test_json'
|
25
|
-
require 'test_knife'
|
26
|
-
require 'test_reagan'
|
27
|
-
require 'test_version'
|
28
|
-
|
29
|
-
def initialize
|
30
|
-
@config_obj = Reagan::Config.new
|
31
|
-
@@config = @config_obj.settings
|
32
|
-
@changes = Reagan::Change.new.files
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.config
|
36
|
-
@@config
|
37
|
-
end
|
38
|
-
|
39
|
-
# nicely prints marques
|
40
|
-
def pretty_print(string)
|
41
|
-
puts "\n#{string}"
|
42
|
-
string.length.times { printf '-' }
|
43
|
-
puts "\n"
|
44
|
-
end
|
45
|
-
|
46
|
-
# exit with a friendly message if nothing we test has been changed
|
47
|
-
def check_empty_update
|
48
|
-
objects_updated = false
|
49
|
-
%w(cookbooks roles environments data_bags).each do |object|
|
50
|
-
objects_updated = true unless @changes[object].empty?
|
51
|
-
end
|
52
|
-
|
53
|
-
unless objects_updated
|
54
|
-
pretty_print('No objects to test. Exiting')
|
55
|
-
exit 0
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# check and see if the -p flag was passed and if so print the config hash
|
60
|
-
def check_print_config
|
61
|
-
return unless @@config['flags']['print_config']
|
62
|
-
pretty_print('Current config file / CLI flag values')
|
63
|
-
@config_obj.print_config
|
64
|
-
exit 0
|
65
|
-
end
|
66
|
-
|
67
|
-
# run tests on each changed cookbook
|
68
|
-
def run
|
69
|
-
check_print_config
|
70
|
-
check_empty_update
|
71
|
-
|
72
|
-
# print objects that will be tested
|
73
|
-
pretty_print('The following chef objects will be tested')
|
74
|
-
%w(cookbooks roles environments data_bags).each do |type|
|
75
|
-
unless @changes[type].empty?
|
76
|
-
puts "#{type}:"
|
77
|
-
@changes[type].each { |obj| puts ' ' + obj }
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
results = []
|
82
|
-
@changes['cookbooks'].each do |cookbook|
|
83
|
-
pretty_print("Testing cookbook #{cookbook}")
|
84
|
-
results << Reagan::TestKnife.new(cookbook).test
|
85
|
-
results << Reagan::TestVersion.new(cookbook).test
|
86
|
-
results << Reagan::TestReagan.new(cookbook).test
|
87
|
-
end
|
88
|
-
|
89
|
-
%w(data_bags roles environments).each do |type|
|
90
|
-
@changes[type].each do |file|
|
91
|
-
pretty_print("Testing #{type} file #{file}")
|
92
|
-
results << TestJSON.new(file).test
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# print success or failure
|
97
|
-
failure = results.include?(false)
|
98
|
-
text = failure ? 'Reagan testing has failed' : 'All Reagan tests have suceeded'
|
99
|
-
pretty_print(text)
|
100
|
-
|
101
|
-
# if any test failed then exit 1 so jenkins can pick up the failure
|
102
|
-
exit 1 if failure
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
data/lib/config.rb
DELETED
@@ -1,118 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
#
|
3
|
-
# Author:: Tim Smith (<tim@cozy.co>)
|
4
|
-
# Copyright:: Copyright (c) 2014 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 'yaml'
|
21
|
-
require 'optparse'
|
22
|
-
rescue LoadError => e
|
23
|
-
raise "Missing gem or lib #{e}"
|
24
|
-
end
|
25
|
-
|
26
|
-
module Reagan
|
27
|
-
# builds a single config from passed flags, yaml config, and knife.rb
|
28
|
-
class Config
|
29
|
-
attr_accessor :settings
|
30
|
-
def initialize
|
31
|
-
@flags = flag_parser
|
32
|
-
@config_file = load_config_file
|
33
|
-
@settings = build_config
|
34
|
-
end
|
35
|
-
|
36
|
-
# grabs the flags passed in via command line
|
37
|
-
def flag_parser
|
38
|
-
flags = { :pull => nil, :override_cookbooks => nil, :config => '/etc/reagan.yml', :print_config => false }
|
39
|
-
OptionParser.new do |opts|
|
40
|
-
opts.banner = 'Usage: reagan [options]'
|
41
|
-
opts.on('-o', '--override cb1,cb2', Array, 'Comma separated list of cookbooks to test') do |cookbooks|
|
42
|
-
flags[:override_cookbooks] = cookbooks
|
43
|
-
end
|
44
|
-
|
45
|
-
opts.on('-p', '--print', 'Print the config options that will be used') do |config|
|
46
|
-
flags[:print_config] = config
|
47
|
-
end
|
48
|
-
|
49
|
-
opts.on('-p', '--pull_num 123', Integer, 'Github pull number to test') do |pull|
|
50
|
-
flags[:pull] = pull
|
51
|
-
end
|
52
|
-
|
53
|
-
opts.on('-c', '--config reagan.yml', 'Path to config file (defaults to /etc/reagan.yml)') do |config|
|
54
|
-
flags[:config] = config
|
55
|
-
end
|
56
|
-
|
57
|
-
opts.on('-h', '--help', 'Displays Help') do
|
58
|
-
puts opts
|
59
|
-
exit
|
60
|
-
end
|
61
|
-
end.parse!
|
62
|
-
|
63
|
-
flags
|
64
|
-
end
|
65
|
-
|
66
|
-
# loads the reagan.yml config file from /etc/reagan.yml or the passed location
|
67
|
-
def load_config_file
|
68
|
-
config = YAML.load_file(@flags[:config])
|
69
|
-
if config == false
|
70
|
-
puts "ERROR: Reagan config at #{@flags[:config]} does not contain any configuration data"
|
71
|
-
exit 1
|
72
|
-
end
|
73
|
-
|
74
|
-
# if not workstation was set try to use the Jenkins workspace variable
|
75
|
-
unless config['jenkins'] && config['jenkins']['workspace_dir']
|
76
|
-
workspace = ENV['WORKSPACE']
|
77
|
-
if workspace
|
78
|
-
config['jenkins'] = {}
|
79
|
-
config['jenkins']['workspace_dir'] = workspace
|
80
|
-
else
|
81
|
-
puts 'Jenkins workspace not defined and $WORKSPACE empty. Exiting'
|
82
|
-
exit
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
config
|
87
|
-
rescue Errno::ENOENT
|
88
|
-
puts "ERROR: Cannot load Reagan config file at #{@flags[:config]}"
|
89
|
-
exit 1
|
90
|
-
rescue Psych::SyntaxError
|
91
|
-
puts "ERROR: Syntax error in Reagan config file at #{@flags[:config]}"
|
92
|
-
exit 1
|
93
|
-
end
|
94
|
-
|
95
|
-
# join the config file with the passed flags into a single object
|
96
|
-
def build_config
|
97
|
-
config = @config_file
|
98
|
-
config['flags'] = {}
|
99
|
-
@flags.each { |k, v| config['flags'][k.to_s] = v }
|
100
|
-
config
|
101
|
-
end
|
102
|
-
|
103
|
-
# pretty print the config hash
|
104
|
-
def print_config(hash = nil, spaces = 0)
|
105
|
-
hash = @settings if hash.nil?
|
106
|
-
hash.each do |k, v|
|
107
|
-
spaces.times { print ' ' }
|
108
|
-
print k.to_s + ': '
|
109
|
-
if v.class == Hash
|
110
|
-
print "\n"
|
111
|
-
print_config(v, spaces + 2)
|
112
|
-
else
|
113
|
-
puts v
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|