chef-umami 0.0.4 → 0.0.5
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/CHANGELOG.md +4 -0
- data/README.md +22 -2
- data/bin/umami +3 -3
- data/lib/chef-umami/client.rb +0 -3
- data/lib/chef-umami/exceptions.rb +0 -2
- data/lib/chef-umami/helpers/filetools.rb +22 -24
- data/lib/chef-umami/helpers/inspec.rb +23 -24
- data/lib/chef-umami/helpers/os.rb +9 -10
- data/lib/chef-umami/logger.rb +0 -1
- data/lib/chef-umami/options.rb +72 -0
- data/lib/chef-umami/policyfile/exporter.rb +2 -8
- data/lib/chef-umami/policyfile/uploader.rb +2 -9
- data/lib/chef-umami/runner.rb +37 -17
- data/lib/chef-umami/server.rb +0 -1
- data/lib/chef-umami/test.rb +2 -4
- data/lib/chef-umami/test/integration.rb +7 -10
- data/lib/chef-umami/test/unit.rb +5 -8
- data/lib/chef-umami/version.rb +1 -1
- data/spec/runner_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -103
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 782fcbb03ba84bce326bfc8d8c3e51b4a36f4c1e
|
4
|
+
data.tar.gz: c7fa44a081c7462509665637bcf1414249f35975
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6f76421bb97b53f93b3fedac539aa4d805d556cde66a36d78fb0acc3e55299df479d7285e696cdeca4597a0aabd7e95eac098d0c41539820cb78478a93d17ff
|
7
|
+
data.tar.gz: f17e6d3eaf84166fbb2a96578ec5274b97bfeedc815b2db512a6bce12b3e7edb17a9852319be574325d51c009eb53980f53f92dc64ce672cf372dd37567754d9
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -7,6 +7,26 @@ Let's see it in action!
|
|
7
7
|
|
8
8
|
[](https://asciinema.org/a/138816)
|
9
9
|
|
10
|
+
## Running `umami`
|
11
|
+
|
12
|
+
A number of options maybe specified on the command line:
|
13
|
+
|
14
|
+
```bash
|
15
|
+
Usage: umami [options]
|
16
|
+
|
17
|
+
A taste you won't forget!
|
18
|
+
|
19
|
+
-h, --help Prints this help message
|
20
|
+
-i, --[no-]integration-tests Write integration tests (DEFAULT: true)
|
21
|
+
-p, --policyfile POLICYFILE_PATH Specify the path to a policy (DEFAULT: Policyfile.rb)
|
22
|
+
-r, --recipes RECIPE1,RECIPE2 Specify one or more recipes for which we'll write tests (DEFAULT: All recipes)
|
23
|
+
-t, --test-root TEST_ROOT_PATH Specify the path into which we'll write tests (DEFAULT: spec)
|
24
|
+
-u, --[no-]unit-tests Write unit tests (DEFAULT: true)
|
25
|
+
-v, --version Show version and exit
|
26
|
+
```
|
27
|
+
|
28
|
+
If not options are specified, a reasonable set of defaults are defined.
|
29
|
+
|
10
30
|
## How does it Work?
|
11
31
|
|
12
32
|
`umami` loads up one or more cookbooks in a `chef-zero` instance, executes the
|
@@ -160,11 +180,11 @@ can build on. **Do NOT depend solely on `umami` to provide test coverage!**
|
|
160
180
|
|
161
181
|
This project came to be largely out of fear of having to write a lot of test
|
162
182
|
code from scratch where none had previously existed. The idea of starting from
|
163
|
-
nothing seemed so daunting that it's likely no
|
183
|
+
nothing seemed so daunting that it's likely no one would ever get started. I
|
164
184
|
wanted to give Chef developers a means to expedite writing tests. After all,
|
165
185
|
it's much easier to modify code than it is to write it in the first place.
|
166
186
|
|
167
|
-
`umami` is the product of
|
187
|
+
`umami` is the product of research into various projects' code, such as
|
168
188
|
Chef, ChefDK, and Test Kitchen. I am grateful to everyone that has contributed
|
169
189
|
to those projects. `umami` borrows some patterns from those projects and, in
|
170
190
|
some cases, bits of code.
|
data/bin/umami
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/opt/chefdk/embedded/bin/ruby
|
2
2
|
|
3
|
-
chef_umami_lib = File.expand_path(File.join(File.dirname(__FILE__),
|
4
|
-
|
5
|
-
require
|
3
|
+
chef_umami_lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
$LOAD_PATH.unshift(chef_umami_lib)
|
5
|
+
require 'chef-umami/runner'
|
6
6
|
|
7
7
|
Umami::Runner.new.run
|
data/lib/chef-umami/client.rb
CHANGED
@@ -16,8 +16,6 @@ require 'chef'
|
|
16
16
|
|
17
17
|
module Umami
|
18
18
|
class Client
|
19
|
-
|
20
|
-
attr_reader :client
|
21
19
|
def initialize
|
22
20
|
@client = client
|
23
21
|
end
|
@@ -45,6 +43,5 @@ module Umami
|
|
45
43
|
def resource_collection
|
46
44
|
client.run_status.run_context.resource_collection
|
47
45
|
end
|
48
|
-
|
49
46
|
end
|
50
47
|
end
|
@@ -15,32 +15,30 @@
|
|
15
15
|
module Umami
|
16
16
|
module Helper
|
17
17
|
module FileTools
|
18
|
+
require 'fileutils'
|
19
|
+
require 'rubocop'
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
f.write(content)
|
27
|
-
f.close
|
28
|
-
end
|
29
|
-
|
30
|
-
# Call Rubocop to ensure proper indentation and thus legibility.
|
31
|
-
def enforce_styling(path = 'spec/umami/')
|
32
|
-
puts "Running Rubocop over '#{path}' to enforce styling..."
|
33
|
-
r = RuboCop::CLI.new
|
34
|
-
# Don't output to STDOUT.
|
35
|
-
args = [
|
36
|
-
'--only', 'Style/IndentationWidth,Style/IndentationConsistency',
|
37
|
-
'--auto-correct',
|
38
|
-
'--out', '/dev/null',
|
39
|
-
path
|
40
|
-
]
|
41
|
-
r.run(args)
|
42
|
-
end
|
21
|
+
def write_file(path = nil, content = '')
|
22
|
+
parent_dir = File.dirname(path)
|
23
|
+
FileUtils.mkdir_p(parent_dir) unless ::File.exist?(parent_dir)
|
24
|
+
f = File.open(path, 'w') # Write with prejudice.
|
25
|
+
f.write(content)
|
26
|
+
f.close
|
27
|
+
end
|
43
28
|
|
29
|
+
# Call Rubocop to ensure proper indentation and thus legibility.
|
30
|
+
def enforce_styling(path = 'spec/umami/')
|
31
|
+
puts "Running Rubocop over '#{path}' to enforce styling..."
|
32
|
+
r = RuboCop::CLI.new
|
33
|
+
# Don't output to STDOUT.
|
34
|
+
args = [
|
35
|
+
'--only', 'Style/IndentationWidth,Style/IndentationConsistency',
|
36
|
+
'--auto-correct',
|
37
|
+
'--out', '/dev/null',
|
38
|
+
path
|
39
|
+
]
|
40
|
+
r.run(args)
|
41
|
+
end
|
44
42
|
end
|
45
43
|
end
|
46
44
|
end
|
@@ -15,14 +15,14 @@
|
|
15
15
|
module Umami
|
16
16
|
module Helper
|
17
17
|
module InSpec
|
18
|
-
|
19
18
|
# Call on a resource's #identity method to help describe the resource.
|
20
19
|
# This saves us from having to know/code the identity attribute for each
|
21
20
|
# resource (i.e. File is :path, User is :username, etc).
|
22
21
|
def desciption(resource)
|
23
|
-
identity = resource.identity
|
24
22
|
if identity.is_a? Hash # #identity could return a Hash. Take the first value.
|
25
23
|
identity = identity.values.first
|
24
|
+
else
|
25
|
+
identity = resource.identity
|
26
26
|
end
|
27
27
|
"describe #{resource.declared_type}('#{identity}') do"
|
28
28
|
end
|
@@ -34,17 +34,17 @@ module Umami
|
|
34
34
|
# (i.e. testing a directory resource requires defining a file test).
|
35
35
|
# 2. The method should should return a string joined by newlines.
|
36
36
|
#
|
37
|
-
#def test_wutang(resource)
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#end
|
37
|
+
# def test_wutang(resource)
|
38
|
+
# test = [desciption(resource)]
|
39
|
+
# test << "it { should be_financially_sound }"
|
40
|
+
# test << "it { should be_diverisified }"
|
41
|
+
# test.join("\n")
|
42
|
+
# end
|
43
43
|
|
44
44
|
# InSpec can evaluate if a gem is installed via the system `gem` (default)
|
45
45
|
# or via some other `gem` binary, defined by either the path to the gem
|
46
46
|
# binary of a symbol representing that context.
|
47
|
-
def test_gem_package(resource, gem_binary=nil)
|
47
|
+
def test_gem_package(resource, gem_binary = nil)
|
48
48
|
package_name = resource.package_name
|
49
49
|
if gem_binary
|
50
50
|
if gem_binary.is_a? Symbol
|
@@ -76,35 +76,35 @@ module Umami
|
|
76
76
|
"#{resource.weekday} " \
|
77
77
|
"#{resource.command}"
|
78
78
|
test << "it { should have_entry('#{cron_entry}').with_user('#{resource.user}') }"
|
79
|
-
test <<
|
79
|
+
test << 'end'
|
80
80
|
test.join("\n")
|
81
81
|
end
|
82
82
|
|
83
83
|
def test_file(resource)
|
84
84
|
test = ["describe file('#{resource.path}') do"]
|
85
85
|
if resource.declared_type =~ /directory/
|
86
|
-
test <<
|
86
|
+
test << 'it { should be_directory }'
|
87
87
|
else
|
88
|
-
test <<
|
88
|
+
test << 'it { should be_file }'
|
89
89
|
end
|
90
90
|
# Sometimes we see GIDs instead of group names.
|
91
|
-
|
91
|
+
unless resource.group.nil?
|
92
92
|
unless resource.group.is_a?(String) && resource.group.empty?
|
93
93
|
test << "it { should be_grouped_into '#{resource.group}' }"
|
94
94
|
end
|
95
95
|
end
|
96
96
|
# Guard for UIDs versus usernames as well.
|
97
|
-
|
97
|
+
unless resource.owner.nil?
|
98
98
|
unless resource.owner.is_a?(String) && resource.owner.empty?
|
99
99
|
test << "it { should be_owned_by '#{resource.owner}' }"
|
100
100
|
end
|
101
101
|
end
|
102
|
-
|
102
|
+
unless resource.mode.nil?
|
103
103
|
unless resource.mode.is_a?(String) && !resource.mode.empty?
|
104
104
|
test << "it { should be_mode '#{resource.mode}' }"
|
105
105
|
end
|
106
106
|
end
|
107
|
-
test <<
|
107
|
+
test << 'end'
|
108
108
|
test.join("\n")
|
109
109
|
end
|
110
110
|
alias_method :test_cookbook_file, :test_file
|
@@ -115,8 +115,8 @@ module Umami
|
|
115
115
|
|
116
116
|
def test_group(resource)
|
117
117
|
test = [desciption(resource)]
|
118
|
-
test <<
|
119
|
-
test <<
|
118
|
+
test << 'it { should exist }'
|
119
|
+
test << 'end'
|
120
120
|
test.join("\n")
|
121
121
|
end
|
122
122
|
|
@@ -125,18 +125,18 @@ module Umami
|
|
125
125
|
if !resource.version.nil? && !resource.version.empty?
|
126
126
|
test << "it { should be_installed.with_version('#{resource.version}') }"
|
127
127
|
else
|
128
|
-
test <<
|
128
|
+
test << 'it { should be_installed }'
|
129
129
|
end
|
130
|
-
test <<
|
130
|
+
test << 'end'
|
131
131
|
test.join("\n")
|
132
132
|
end
|
133
133
|
|
134
134
|
def test_user(resource)
|
135
135
|
test = [desciption(resource)]
|
136
|
-
test <<
|
136
|
+
test << 'it { should exist }'
|
137
137
|
# Guard for GIDs rather than strings. Chef aliases the #group method
|
138
138
|
# to the #gid method.
|
139
|
-
|
139
|
+
unless resource.gid.nil?
|
140
140
|
unless resource.gid.is_a?(String) && !resource.gid.empty?
|
141
141
|
test << "it { should belong_to_primary_group '#{resource.gid}' }"
|
142
142
|
end
|
@@ -144,10 +144,9 @@ module Umami
|
|
144
144
|
if !resource.home.nil? && !resource.home.empty?
|
145
145
|
test << "it { should have_home_directory '#{resource.home}' }"
|
146
146
|
end
|
147
|
-
test <<
|
147
|
+
test << 'end'
|
148
148
|
test.join("\n")
|
149
149
|
end
|
150
|
-
|
151
150
|
end
|
152
151
|
end
|
153
152
|
end
|
@@ -27,27 +27,27 @@ module Umami
|
|
27
27
|
when /aix/
|
28
28
|
# `oslevel` => '7.1.0.0'
|
29
29
|
version = `oslevel`[0..2]
|
30
|
-
{platform: 'aix', version: version}
|
30
|
+
{ platform: 'aix', version: version }
|
31
31
|
when /darwin|mac os/
|
32
32
|
version = `sw_vers -productVersion`.chomp
|
33
|
-
{platform: 'mac_os_x', version: version}
|
33
|
+
{ platform: 'mac_os_x', version: version }
|
34
34
|
when /linux/
|
35
35
|
# Perform very basic tests to determine distribution.
|
36
36
|
if File.exist?('/etc/centos-release')
|
37
37
|
version = File.read('/etc/redhat-release').split[2]
|
38
|
-
{platform: 'centos', version: version}
|
38
|
+
{ platform: 'centos', version: version }
|
39
39
|
elsif File.exist?('/etc/redhat-release') # True for CentOS too...
|
40
40
|
version = File.read('/etc/redhat-release').split[6]
|
41
|
-
{platform: 'redhat', version: version}
|
41
|
+
{ platform: 'redhat', version: version }
|
42
42
|
else
|
43
|
-
{platform: 'centos', version: '7.3.1611'} # Default to something reasonably sane.
|
43
|
+
{ platform: 'centos', version: '7.3.1611' } # Default to something reasonably sane.
|
44
44
|
end
|
45
45
|
when /solaris/
|
46
46
|
version = `uname -r`.chomp # Release level (i.e. 5.11).
|
47
|
-
{platform: 'solaris', version: version}
|
47
|
+
{ platform: 'solaris', version: version }
|
48
48
|
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
49
49
|
# Refer to https://en.wikipedia.org/wiki/Ver_(command)
|
50
|
-
win_version = `ver`.chomp.split(
|
50
|
+
win_version = `ver`.chomp.split('Version ')[1].gsub(/]/, '')
|
51
51
|
case win_version
|
52
52
|
when /^6.1.7/
|
53
53
|
version = '2008R2' # Also Win 7
|
@@ -58,14 +58,13 @@ module Umami
|
|
58
58
|
when /^(6.4|10)/
|
59
59
|
version = '10'
|
60
60
|
end
|
61
|
-
{platform: 'windows', version: version}
|
61
|
+
{ platform: 'windows', version: version }
|
62
62
|
else
|
63
63
|
# Default to something reasonably sane.
|
64
|
-
{platform: 'centos', version: '7.3.1611'}
|
64
|
+
{ platform: 'centos', version: '7.3.1611' }
|
65
65
|
end
|
66
66
|
)
|
67
67
|
end
|
68
|
-
|
69
68
|
end
|
70
69
|
end
|
71
70
|
end
|
data/lib/chef-umami/logger.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Copyright 2017 Bloomberg Finance, L.P.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'chef-umami/version'
|
16
|
+
|
17
|
+
module Umami
|
18
|
+
module Options
|
19
|
+
# Parse command line options. Returns hash of options.
|
20
|
+
def parse_options
|
21
|
+
options = {}
|
22
|
+
# Default options
|
23
|
+
options[:integration_tests] = true
|
24
|
+
options[:policyfile] = 'Policyfile.rb'
|
25
|
+
options[:test_root] = 'spec'
|
26
|
+
options[:unit_tests] = true
|
27
|
+
|
28
|
+
parser = OptionParser.new do |opts|
|
29
|
+
opts.banner = opts.banner + "\n\nA taste you won't forget!\n\n"
|
30
|
+
opts.on('-h', '--help', 'Prints this help message') {
|
31
|
+
puts opts
|
32
|
+
exit
|
33
|
+
}
|
34
|
+
opts.on('-i', '--[no-]integration-tests', 'Write integration tests' \
|
35
|
+
" (DEFAULT: #{options[:integration_tests]})") do |integration_tests|
|
36
|
+
options[:integration_tests] = integration_tests
|
37
|
+
end
|
38
|
+
opts.on('-p', '--policyfile POLICYFILE_PATH', 'Specify the path to a policy' \
|
39
|
+
" (DEFAULT: #{options[:policyfile]})") do |policyfile|
|
40
|
+
options[:policyfile] = policyfile
|
41
|
+
end
|
42
|
+
opts.on('-r', '--recipes RECIPE1,RECIPE2', Array,
|
43
|
+
"Specify one or more recipes for which we'll write tests" \
|
44
|
+
' (DEFAULT: All recipes)') do |recipes|
|
45
|
+
options[:recipes] = recipes
|
46
|
+
end
|
47
|
+
opts.on('-t', '--test-root TEST_ROOT_PATH', "Specify the path into which we'll write tests" \
|
48
|
+
" (DEFAULT: #{options[:test_root]})") do |test_root|
|
49
|
+
options[:test_root] = test_root
|
50
|
+
end
|
51
|
+
opts.on('-u', '--[no-]unit-tests', 'Write unit tests' \
|
52
|
+
" (DEFAULT: #{options[:unit_tests]})") do |unit_tests|
|
53
|
+
options[:unit_tests] = unit_tests
|
54
|
+
end
|
55
|
+
opts.on('-v', '--version', 'Show version and exit') {
|
56
|
+
puts "chef-umami v#{Umami::VERSION}"
|
57
|
+
exit
|
58
|
+
}
|
59
|
+
end
|
60
|
+
begin
|
61
|
+
parser.parse!
|
62
|
+
rescue OptionParser::InvalidOption => e
|
63
|
+
puts "Warning: #{e.message}"
|
64
|
+
if e.message =~ /--pattern/
|
65
|
+
puts 'Ah, this is likely parsed from `rspec` options. We can safely ignore this.'
|
66
|
+
end
|
67
|
+
puts 'Ignoring the option.'
|
68
|
+
end
|
69
|
+
options
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -20,13 +20,12 @@ require 'tmpdir' # Extends Dir
|
|
20
20
|
module Umami
|
21
21
|
class Policyfile
|
22
22
|
class Exporter
|
23
|
-
|
24
23
|
attr_reader :chef_config_file
|
25
24
|
attr_reader :cookbook_dir
|
26
25
|
attr_reader :export_root
|
27
26
|
attr_reader :export_path
|
28
|
-
attr_accessor :policyfile_lock_file
|
29
27
|
attr_reader :policyfile
|
28
|
+
attr_accessor :policyfile_lock_file
|
30
29
|
|
31
30
|
def initialize(policyfile_lock_file = nil, cookbook_dir = nil, policyfile = nil)
|
32
31
|
@policyfile = policyfile
|
@@ -39,10 +38,6 @@ module Umami
|
|
39
38
|
@chef_config_file = "#{export_path}/.chef/config.rb"
|
40
39
|
end
|
41
40
|
|
42
|
-
def policyfile
|
43
|
-
@policyfile
|
44
|
-
end
|
45
|
-
|
46
41
|
def ui
|
47
42
|
@ui ||= ChefDK::UI.new
|
48
43
|
end
|
@@ -92,7 +87,7 @@ module Umami
|
|
92
87
|
rescue ChefDK::PolicyfileExportRepoError => e
|
93
88
|
puts "\nFAILED TO EXPORT POLICYFILE: #{e.message} (#{e.class})"
|
94
89
|
puts "CAUSE: #{e.cause}"
|
95
|
-
puts
|
90
|
+
puts 'BACKTRACE:'
|
96
91
|
e.backtrace.each do |line|
|
97
92
|
puts "\t#{line}"
|
98
93
|
end
|
@@ -101,7 +96,6 @@ module Umami
|
|
101
96
|
cp_fake_client_key
|
102
97
|
update_chef_config
|
103
98
|
end
|
104
|
-
|
105
99
|
end
|
106
100
|
end
|
107
101
|
end
|
@@ -21,21 +21,15 @@ require 'chef-dk/ui'
|
|
21
21
|
module Umami
|
22
22
|
class Policyfile
|
23
23
|
class Uploader
|
24
|
-
|
25
|
-
attr_reader :http_client
|
26
|
-
attr_reader :policyfile_lock
|
27
24
|
attr_reader :policyfile_lock_file
|
28
|
-
|
29
|
-
attr_reader :storage_config
|
30
|
-
attr_reader :ui
|
31
|
-
def initialize(policyfile_lock_file = nil)
|
25
|
+
def initialize(policyfile_lock_file = nil)
|
32
26
|
@http_client = http_client
|
33
27
|
@policyfile_lock_file = policyfile_lock_file
|
34
28
|
@policyfile_lock = policyfile_lock
|
35
29
|
@policyfile_uploader = policyfile_uploader
|
36
30
|
@storage_config = storage_config
|
37
31
|
@ui = ui
|
38
|
-
|
32
|
+
end
|
39
33
|
|
40
34
|
def storage_config
|
41
35
|
@storage_config ||= ChefDK::Policyfile::StorageConfig.new.use_policyfile(policyfile_lock_file)
|
@@ -86,7 +80,6 @@ module Umami
|
|
86
80
|
def upload
|
87
81
|
policyfile_uploader.upload
|
88
82
|
end
|
89
|
-
|
90
83
|
end
|
91
84
|
end
|
92
85
|
end
|
data/lib/chef-umami/runner.rb
CHANGED
@@ -16,6 +16,7 @@ require 'chef'
|
|
16
16
|
require 'chef-umami/exceptions'
|
17
17
|
require 'chef-umami/client'
|
18
18
|
require 'chef-umami/logger'
|
19
|
+
require 'chef-umami/options'
|
19
20
|
require 'chef-umami/server'
|
20
21
|
require 'chef-umami/policyfile/exporter'
|
21
22
|
require 'chef-umami/policyfile/uploader'
|
@@ -24,17 +25,13 @@ require 'chef-umami/test/integration'
|
|
24
25
|
|
25
26
|
module Umami
|
26
27
|
class Runner
|
27
|
-
|
28
28
|
include Umami::Logger
|
29
|
+
include Umami::Options
|
29
30
|
|
30
31
|
attr_reader :cookbook_dir
|
31
|
-
|
32
|
-
|
33
|
-
# TODO: Build the ability to specify a custom policy lock file name.
|
34
|
-
def initialize(policyfile_lock_file = nil, policyfile = nil)
|
32
|
+
def initialize
|
33
|
+
@config = config
|
35
34
|
@cookbook_dir = Dir.pwd
|
36
|
-
@policyfile_lock_file = 'Policyfile.lock.json'
|
37
|
-
@policyfile = policyfile || 'Policyfile.rb'
|
38
35
|
@exporter = exporter
|
39
36
|
@chef_zero_server = chef_zero_server
|
40
37
|
# If we load the uploader or client now, they won't see the updated
|
@@ -43,8 +40,23 @@ module Umami
|
|
43
40
|
@chef_client = nil
|
44
41
|
end
|
45
42
|
|
43
|
+
# A hash of values describing the config. Comprised of command line
|
44
|
+
# options. May (in the future) contain options read from a config file.
|
45
|
+
def config
|
46
|
+
@config ||= parse_options
|
47
|
+
end
|
48
|
+
|
49
|
+
def policyfile
|
50
|
+
config[:policyfile]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return the computed policyfile lock name.
|
54
|
+
def policyfile_lock_file
|
55
|
+
policyfile.gsub(/\.rb$/, '.lock.json')
|
56
|
+
end
|
57
|
+
|
46
58
|
def validate_lock_file!
|
47
|
-
unless policyfile_lock_file.end_with?(
|
59
|
+
unless policyfile_lock_file.end_with?('lock.json')
|
48
60
|
raise InvalidPolicyfileLockFilename, "Policyfile lock files must end in '.lock.json'. I received '#{policyfile_lock_file}'."
|
49
61
|
end
|
50
62
|
|
@@ -73,7 +85,7 @@ module Umami
|
|
73
85
|
validate_lock_file!
|
74
86
|
puts "\nExporting the policy, related cookbooks, and a valid client configuration..."
|
75
87
|
exporter.export
|
76
|
-
Chef::Config.from_file(
|
88
|
+
Chef::Config.from_file(exporter.chef_config_file)
|
77
89
|
chef_zero_server.start
|
78
90
|
puts "\nUploading the policy and related cookbooks..."
|
79
91
|
uploader.upload
|
@@ -86,6 +98,12 @@ module Umami
|
|
86
98
|
recipe_resources = {}
|
87
99
|
chef_client.resource_collection.each do |resource|
|
88
100
|
canonical_recipe = "#{resource.cookbook_name}::#{resource.recipe_name}"
|
101
|
+
unless config[:recipes].empty?
|
102
|
+
# The user has explicitly requested that one or more recipes have
|
103
|
+
# tests written, to the exclusion of others.
|
104
|
+
# ONLY include the recipe if it matches the list.
|
105
|
+
next unless config[:recipes].include?(canonical_recipe)
|
106
|
+
end
|
89
107
|
if recipe_resources.key?(canonical_recipe)
|
90
108
|
recipe_resources[canonical_recipe] << resource
|
91
109
|
else
|
@@ -98,15 +116,17 @@ module Umami
|
|
98
116
|
re_export_path = Regexp.new('/tmp/umami')
|
99
117
|
FileUtils.rm_rf(exporter.export_root) if exporter.export_root.match(re_export_path)
|
100
118
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
integration_tester = Umami::Test::Integration.new
|
107
|
-
integration_tester.generate(recipe_resources)
|
119
|
+
if config[:unit_tests]
|
120
|
+
puts "\nGenerating a set of unit tests..."
|
121
|
+
unit_tester = Umami::Test::Unit.new(config[:test_root])
|
122
|
+
unit_tester.generate(recipe_resources)
|
123
|
+
end
|
108
124
|
|
125
|
+
if config[:integration_tests]
|
126
|
+
puts "\nGenerating a set of integration tests..."
|
127
|
+
integration_tester = Umami::Test::Integration.new(config[:test_root])
|
128
|
+
integration_tester.generate(recipe_resources)
|
129
|
+
end
|
109
130
|
end
|
110
|
-
|
111
131
|
end
|
112
132
|
end
|
data/lib/chef-umami/server.rb
CHANGED
data/lib/chef-umami/test.rb
CHANGED
@@ -14,10 +14,9 @@
|
|
14
14
|
|
15
15
|
module Umami
|
16
16
|
class Test
|
17
|
-
|
18
17
|
attr_reader :root_dir
|
19
|
-
def initialize
|
20
|
-
@root_dir =
|
18
|
+
def initialize(root_dir)
|
19
|
+
@root_dir = root_dir
|
21
20
|
end
|
22
21
|
|
23
22
|
# All subclasses should implement the following methods.
|
@@ -58,6 +57,5 @@ module Umami
|
|
58
57
|
def generate
|
59
58
|
raise NoMethodError, "#{self.class} needs to implement the ##{__method__} method! Refer to Umami::Test."
|
60
59
|
end
|
61
|
-
|
62
60
|
end
|
63
61
|
end
|
@@ -19,12 +19,11 @@ require 'chef-umami/helpers/filetools'
|
|
19
19
|
module Umami
|
20
20
|
class Test
|
21
21
|
class Integration < Umami::Test
|
22
|
-
|
23
22
|
include Umami::Helper::InSpec
|
24
23
|
include Umami::Helper::FileTools
|
25
24
|
|
26
25
|
attr_reader :test_root
|
27
|
-
def initialize
|
26
|
+
def initialize(root_dir)
|
28
27
|
super
|
29
28
|
@test_root = File.join(self.root_dir, 'umami', 'integration')
|
30
29
|
end
|
@@ -32,7 +31,7 @@ module Umami
|
|
32
31
|
# InSpec doesn't need a require statement to use its tests.
|
33
32
|
# We define #framework here for completeness.
|
34
33
|
def framework
|
35
|
-
|
34
|
+
'inspec'
|
36
35
|
end
|
37
36
|
|
38
37
|
def test_file_path(cookbook = '', recipe = '')
|
@@ -58,10 +57,10 @@ module Umami
|
|
58
57
|
# Raise NoMethodError for any other failed calls.
|
59
58
|
def method_missing(m, *args, &block)
|
60
59
|
case m
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
when /^test_/
|
61
|
+
"# #{m} is not currently defined. Stay tuned for updates."
|
62
|
+
else
|
63
|
+
raise NoMethodError
|
65
64
|
end
|
66
65
|
end
|
67
66
|
|
@@ -82,14 +81,12 @@ module Umami
|
|
82
81
|
enforce_styling(test_root)
|
83
82
|
|
84
83
|
unless test_files_written.empty?
|
85
|
-
puts
|
84
|
+
puts 'Wrote the following integration tests:'
|
86
85
|
test_files_written.each do |f|
|
87
86
|
puts "\t#{f}"
|
88
87
|
end
|
89
88
|
end
|
90
|
-
|
91
89
|
end
|
92
|
-
|
93
90
|
end
|
94
91
|
end
|
95
92
|
end
|
data/lib/chef-umami/test/unit.rb
CHANGED
@@ -19,20 +19,19 @@ require 'chef-umami/helpers/filetools'
|
|
19
19
|
module Umami
|
20
20
|
class Test
|
21
21
|
class Unit < Umami::Test
|
22
|
-
|
23
22
|
include Umami::Helper::OS
|
24
23
|
include Umami::Helper::FileTools
|
25
24
|
|
26
25
|
attr_reader :test_root
|
27
26
|
attr_reader :tested_cookbook # This cookbook.
|
28
|
-
def initialize
|
27
|
+
def initialize(root_dir)
|
29
28
|
super
|
30
29
|
@test_root = File.join(self.root_dir, 'umami', 'unit', 'recipes')
|
31
30
|
@tested_cookbook = File.basename(Dir.pwd)
|
32
31
|
end
|
33
32
|
|
34
33
|
def framework
|
35
|
-
|
34
|
+
'chefspec'
|
36
35
|
end
|
37
36
|
|
38
37
|
def test_file(recipe = '')
|
@@ -62,7 +61,7 @@ module Umami
|
|
62
61
|
def write_test(resource = nil)
|
63
62
|
state_attrs = [] # Attribute hash to be used with #with()
|
64
63
|
resource.state.each do |attr, value|
|
65
|
-
next if value.nil?
|
64
|
+
next if value.nil? || (value.respond_to?(:empty) && value.empty?)
|
66
65
|
if value.is_a? String
|
67
66
|
value = value.gsub("'", "\\\\'") # Escape any single quotes in the value.
|
68
67
|
end
|
@@ -95,7 +94,7 @@ module Umami
|
|
95
94
|
resources.each do |resource|
|
96
95
|
content << write_test(resource)
|
97
96
|
end
|
98
|
-
content <<
|
97
|
+
content << 'end'
|
99
98
|
test_file_name = test_file(recipe)
|
100
99
|
test_file_content = content.join("\n") + "\n"
|
101
100
|
write_file(test_file_name, test_file_content)
|
@@ -107,14 +106,12 @@ module Umami
|
|
107
106
|
test_files_written << spec_helper_path
|
108
107
|
|
109
108
|
unless test_files_written.empty?
|
110
|
-
puts
|
109
|
+
puts 'Wrote the following unit test files:'
|
111
110
|
test_files_written.each do |f|
|
112
111
|
puts "\t#{f}"
|
113
112
|
end
|
114
113
|
end
|
115
|
-
|
116
114
|
end
|
117
|
-
|
118
115
|
end
|
119
116
|
end
|
120
117
|
end
|
data/lib/chef-umami/version.rb
CHANGED
data/spec/runner_spec.rb
CHANGED
@@ -12,10 +12,10 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
require
|
15
|
+
require 'chef-umami/runner'
|
16
16
|
|
17
17
|
RSpec.describe Umami::Runner do
|
18
|
-
it
|
18
|
+
it 'initiates a Umami::Runner object' do
|
19
19
|
runner = Umami::Runner.new
|
20
20
|
expect(runner).to be_an_instance_of(Umami::Runner)
|
21
21
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,103 +0,0 @@
|
|
1
|
-
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
-
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
4
|
-
# this file to always be loaded, without a need to explicitly require it in any
|
5
|
-
# files.
|
6
|
-
#
|
7
|
-
# Given that it is always loaded, you are encouraged to keep this file as
|
8
|
-
# light-weight as possible. Requiring heavyweight dependencies from this file
|
9
|
-
# will add to the boot time of your test suite on EVERY test run, even for an
|
10
|
-
# individual file that may not need all of that loaded. Instead, consider making
|
11
|
-
# a separate helper file that requires the additional dependencies and performs
|
12
|
-
# the additional setup, and require it from the spec files that actually need
|
13
|
-
# it.
|
14
|
-
#
|
15
|
-
# The `.rspec` file also contains a few flags that are not defaults but that
|
16
|
-
# users commonly want.
|
17
|
-
#
|
18
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
19
|
-
RSpec.configure do |config|
|
20
|
-
# rspec-expectations config goes here. You can use an alternate
|
21
|
-
# assertion/expectation library such as wrong or the stdlib/minitest
|
22
|
-
# assertions if you prefer.
|
23
|
-
config.expect_with :rspec do |expectations|
|
24
|
-
# This option will default to `true` in RSpec 4. It makes the `description`
|
25
|
-
# and `failure_message` of custom matchers include text for helper methods
|
26
|
-
# defined using `chain`, e.g.:
|
27
|
-
# be_bigger_than(2).and_smaller_than(4).description
|
28
|
-
# # => "be bigger than 2 and smaller than 4"
|
29
|
-
# ...rather than:
|
30
|
-
# # => "be bigger than 2"
|
31
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
32
|
-
end
|
33
|
-
|
34
|
-
# rspec-mocks config goes here. You can use an alternate test double
|
35
|
-
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
36
|
-
config.mock_with :rspec do |mocks|
|
37
|
-
# Prevents you from mocking or stubbing a method that does not exist on
|
38
|
-
# a real object. This is generally recommended, and will default to
|
39
|
-
# `true` in RSpec 4.
|
40
|
-
mocks.verify_partial_doubles = true
|
41
|
-
end
|
42
|
-
|
43
|
-
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
44
|
-
# have no way to turn it off -- the option exists only for backwards
|
45
|
-
# compatibility in RSpec 3). It causes shared context metadata to be
|
46
|
-
# inherited by the metadata hash of host groups and examples, rather than
|
47
|
-
# triggering implicit auto-inclusion in groups with matching metadata.
|
48
|
-
config.shared_context_metadata_behavior = :apply_to_host_groups
|
49
|
-
|
50
|
-
# The settings below are suggested to provide a good initial experience
|
51
|
-
# with RSpec, but feel free to customize to your heart's content.
|
52
|
-
=begin
|
53
|
-
# This allows you to limit a spec run to individual examples or groups
|
54
|
-
# you care about by tagging them with `:focus` metadata. When nothing
|
55
|
-
# is tagged with `:focus`, all examples get run. RSpec also provides
|
56
|
-
# aliases for `it`, `describe`, and `context` that include `:focus`
|
57
|
-
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
58
|
-
config.filter_run_when_matching :focus
|
59
|
-
|
60
|
-
# Allows RSpec to persist some state between runs in order to support
|
61
|
-
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
62
|
-
# you configure your source control system to ignore this file.
|
63
|
-
config.example_status_persistence_file_path = "spec/examples.txt"
|
64
|
-
|
65
|
-
# Limits the available syntax to the non-monkey patched syntax that is
|
66
|
-
# recommended. For more details, see:
|
67
|
-
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
68
|
-
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
69
|
-
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
70
|
-
config.disable_monkey_patching!
|
71
|
-
|
72
|
-
# This setting enables warnings. It's recommended, but in some cases may
|
73
|
-
# be too noisy due to issues in dependencies.
|
74
|
-
config.warnings = true
|
75
|
-
|
76
|
-
# Many RSpec users commonly either run the entire suite or an individual
|
77
|
-
# file, and it's useful to allow more verbose output when running an
|
78
|
-
# individual spec file.
|
79
|
-
if config.files_to_run.one?
|
80
|
-
# Use the documentation formatter for detailed output,
|
81
|
-
# unless a formatter has already been configured
|
82
|
-
# (e.g. via a command-line flag).
|
83
|
-
config.default_formatter = 'doc'
|
84
|
-
end
|
85
|
-
|
86
|
-
# Print the 10 slowest examples and example groups at the
|
87
|
-
# end of the spec run, to help surface which specs are running
|
88
|
-
# particularly slow.
|
89
|
-
config.profile_examples = 10
|
90
|
-
|
91
|
-
# Run specs in random order to surface order dependencies. If you find an
|
92
|
-
# order dependency and want to debug it, you can fix the order by providing
|
93
|
-
# the seed, which is printed after each run.
|
94
|
-
# --seed 1234
|
95
|
-
config.order = :random
|
96
|
-
|
97
|
-
# Seed global randomization in this process using the `--seed` CLI option.
|
98
|
-
# Setting this allows you to use `--seed` to deterministically reproduce
|
99
|
-
# test failures related to randomization by passing the same `--seed` value
|
100
|
-
# as the one that triggered the failure.
|
101
|
-
Kernel.srand config.seed
|
102
|
-
=end
|
103
|
-
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chef-umami
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Frantz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- lib/chef-umami/helpers/inspec.rb
|
75
75
|
- lib/chef-umami/helpers/os.rb
|
76
76
|
- lib/chef-umami/logger.rb
|
77
|
+
- lib/chef-umami/options.rb
|
77
78
|
- lib/chef-umami/policyfile.rb
|
78
79
|
- lib/chef-umami/policyfile/exporter.rb
|
79
80
|
- lib/chef-umami/policyfile/uploader.rb
|