puppetlabs-onceover 5.0.3 → 5.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/.gitignore +4 -0
- data/.rubocop_todo.yml +35 -15
- data/CHANGELOG.md +398 -3
- data/Gemfile +21 -1
- data/lib/puppetlabs-onceover/cli/plugins.rb +3 -3
- data/lib/puppetlabs-onceover/controlrepo.rb +8 -7
- data/lib/puppetlabs-onceover/rake_tasks.rb +2 -2
- data/lib/puppetlabs-onceover/vendored_modules.rb +29 -32
- data/lib/puppetlabs-onceover/version.rb +1 -1
- data/spec/fixtures/controlrepos/deploy_test/Puppetfile +3 -0
- data/spec/fixtures/controlrepos/deploy_test/a_dir/nested_file.txt +1 -0
- data/spec/fixtures/controlrepos/deploy_test/a_symlink +1 -0
- data/spec/fixtures/controlrepos/deploy_test/environment.conf +1 -0
- data/spec/fixtures/controlrepos/deploy_test/regular_file.txt +1 -0
- data/spec/puppetlabs-onceover/beaker_spec.rb +293 -0
- data/spec/puppetlabs-onceover/class_spec.rb +89 -0
- data/spec/puppetlabs-onceover/cli/run_spec.rb +80 -0
- data/spec/puppetlabs-onceover/cli/show_spec.rb +67 -0
- data/spec/puppetlabs-onceover/cli_spec.rb +79 -0
- data/spec/puppetlabs-onceover/controlrepo_spec.rb +787 -5
- data/spec/puppetlabs-onceover/deploy_spec.rb +246 -0
- data/spec/puppetlabs-onceover/group_spec.rb +121 -0
- data/spec/puppetlabs-onceover/logger_spec.rb +30 -0
- data/spec/puppetlabs-onceover/node_spec.rb +124 -0
- data/spec/puppetlabs-onceover/rake_tasks_spec.rb +179 -0
- data/spec/puppetlabs-onceover/rspec/formatters_spec.rb +361 -0
- data/spec/puppetlabs-onceover/runner_spec.rb +307 -0
- data/spec/puppetlabs-onceover/test_spec.rb +177 -0
- data/spec/puppetlabs-onceover/testconfig_spec.rb +451 -0
- data/spec/puppetlabs-onceover/vendored_modules_spec.rb +293 -0
- data/spec/puppetlabs-onceover/version_spec.rb +10 -0
- data/spec/spec_helper.rb +8 -0
- metadata +23 -2
|
@@ -672,14 +672,15 @@ class PuppetlabsOnceover
|
|
|
672
672
|
end
|
|
673
673
|
|
|
674
674
|
def find_classname(filename)
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
675
|
+
File.open(filename, "r") do |file|
|
|
676
|
+
while (line = file.gets)
|
|
677
|
+
begin
|
|
678
|
+
if line =~ /^class (\w+(?:::\w+)*)/
|
|
679
|
+
return $1
|
|
680
|
+
end
|
|
681
|
+
rescue ArgumentError => e
|
|
682
|
+
logger.error "ignoring invalid line in file: #{filename} (#{e.message}) - line: '#{line}'"
|
|
680
683
|
end
|
|
681
|
-
rescue ArgumentError => e
|
|
682
|
-
logger.error "ignoring invalid line in file: #{filename} (#{e.message}) - line: '#{line}'"
|
|
683
684
|
end
|
|
684
685
|
end
|
|
685
686
|
return nil
|
|
@@ -40,7 +40,7 @@ task :generate_onceover_yaml do
|
|
|
40
40
|
repo = PuppetlabsOnceover::Controlrepo.new
|
|
41
41
|
template_dir = File.expand_path('../../templates', File.dirname(__FILE__))
|
|
42
42
|
onceover_yaml_template = File.read(File.expand_path('./controlrepo.yaml.erb', template_dir))
|
|
43
|
-
puts ERB.new(onceover_yaml_template,
|
|
43
|
+
puts ERB.new(onceover_yaml_template, trim_mode: '-').result(binding)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
task :generate_nodesets do
|
|
@@ -81,7 +81,7 @@ task :generate_nodesets do
|
|
|
81
81
|
# Use an ERB template
|
|
82
82
|
template_dir = File.expand_path('../../templates', File.dirname(__FILE__))
|
|
83
83
|
fixtures_template = File.read(File.expand_path('./nodeset.yaml.erb', template_dir))
|
|
84
|
-
puts ERB.new(fixtures_template,
|
|
84
|
+
puts ERB.new(fixtures_template, trim_mode: '-').result(binding)
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
87
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'puppet/version'
|
|
2
|
-
require '
|
|
3
|
-
require 'uri'
|
|
2
|
+
require 'git'
|
|
4
3
|
require 'multi_json'
|
|
5
4
|
require 'r10k/module_loader/puppetfile'
|
|
6
5
|
require 'puppetlabs-onceover/logger'
|
|
@@ -69,16 +68,18 @@ class PuppetlabsOnceover
|
|
|
69
68
|
# any puppet version this gem tests against). Neither is needed: the
|
|
70
69
|
# module list is stable (see CORE_MODULES) and each module's own repo
|
|
71
70
|
# is the authoritative source for its own latest version.
|
|
72
|
-
#
|
|
71
|
+
#
|
|
72
|
+
# Tags are read via the git protocol (git ls-remote) rather than
|
|
73
|
+
# GitHub's REST API: the REST API's unauthenticated rate limit
|
|
74
|
+
# (60 req/hr) is shared across every GitHub-hosted Actions runner IP,
|
|
75
|
+
# so 10 sequential lookups per test run reliably exhausted it in CI.
|
|
76
|
+
# git's smart-HTTP protocol has no equivalent per-hour cap.
|
|
73
77
|
@vendored_references = CORE_MODULES.map do |mod_name|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
nil,
|
|
77
|
-
component_cache(mod_name),
|
|
78
|
-
)
|
|
78
|
+
repo_url = "https://github.com/puppetlabs/puppetlabs-#{mod_name}.git"
|
|
79
|
+
tags = query_or_cache(repo_url, component_cache(mod_name))
|
|
79
80
|
latest_tag = tags.first['name']
|
|
80
81
|
{
|
|
81
|
-
'url' =>
|
|
82
|
+
'url' => repo_url,
|
|
82
83
|
'ref' => "refs/tags/#{latest_tag}"
|
|
83
84
|
}
|
|
84
85
|
end
|
|
@@ -153,40 +154,36 @@ class PuppetlabsOnceover
|
|
|
153
154
|
end
|
|
154
155
|
end
|
|
155
156
|
|
|
156
|
-
# Return
|
|
157
|
-
|
|
157
|
+
# Return tag data from a query whom caches, or from the cache to avoid
|
|
158
|
+
# re-querying the remote on every run
|
|
159
|
+
def query_or_cache(repo_url, filepath)
|
|
158
160
|
if (File.exist? filepath) && (@force_update == false)
|
|
159
161
|
logger.debug "Using cache: #{filepath}"
|
|
160
162
|
json = read_json_dump(filepath)
|
|
161
163
|
else
|
|
162
|
-
logger.debug "
|
|
163
|
-
json =
|
|
164
|
+
logger.debug "Listing tags for: #{repo_url}"
|
|
165
|
+
json = remote_tags(repo_url)
|
|
164
166
|
logger.debug "Caching response to: #{filepath}"
|
|
165
167
|
write_json_dump(filepath, json)
|
|
166
168
|
end
|
|
167
169
|
json
|
|
168
170
|
end
|
|
169
171
|
|
|
170
|
-
# Given a
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
MultiJson.load(response.body)
|
|
184
|
-
else
|
|
185
|
-
# Expose the ratelimit response headers
|
|
186
|
-
# https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#checking-the-status-of-your-rate-limit
|
|
187
|
-
ratelimit_headers = response.to_hash.select { |k, _v| k =~ /x-ratelimit.*/ }
|
|
188
|
-
raise "#{response.code} #{response.message} #{ratelimit_headers}"
|
|
172
|
+
# Given a git remote url, return its tags as [{ 'name' => tag }, ...],
|
|
173
|
+
# sorted newest version first (matches the shape/ordering the old
|
|
174
|
+
# GitHub REST tags API returned, so callers can keep using .first).
|
|
175
|
+
def remote_tags(repo_url)
|
|
176
|
+
refs = Git.ls_remote(repo_url, refs: true)['tags'] || {}
|
|
177
|
+
versions = refs.keys.filter_map do |name|
|
|
178
|
+
next if name.end_with?('^{}') # annotated-tag dereference entries
|
|
179
|
+
|
|
180
|
+
begin
|
|
181
|
+
[Gem::Version.new(name.delete_prefix('v')), name]
|
|
182
|
+
rescue ArgumentError
|
|
183
|
+
nil # skip non-version-shaped tags
|
|
184
|
+
end
|
|
189
185
|
end
|
|
186
|
+
versions.sort_by(&:first).reverse.map { |_version, name| { 'name' => name } }
|
|
190
187
|
end
|
|
191
188
|
|
|
192
189
|
# Returns parsed json of file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nested
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
regular_file.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
modulepath = site:modules:$basemodulepath
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hello
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'puppetlabs-onceover/beaker'
|
|
3
|
+
|
|
4
|
+
# The `beaker`/`beaker-rspec` gems are intentionally NOT in this project's Gemfile
|
|
5
|
+
# (removing that dependency is the whole point of this fork -- see CAT-2770 et al).
|
|
6
|
+
# beaker.rb's deprecated, still-shippped `deploy_controlrepo_on`/`provision_and_test`/
|
|
7
|
+
# `host_create` methods `require 'beaker-rspec'` / `require 'beaker/network_manager'`
|
|
8
|
+
# and reference `::Beaker::NetworkManager` inline. To cover those code paths without
|
|
9
|
+
# pulling the real (large, unmaintained-for-this-repo's-purposes) beaker gems back in,
|
|
10
|
+
# stand in a minimal fake `::Beaker::NetworkManager` and make the specific `require`
|
|
11
|
+
# calls those methods make into no-ops.
|
|
12
|
+
module ::Beaker
|
|
13
|
+
unless defined?(::Beaker::NetworkManager)
|
|
14
|
+
class NetworkManager
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "PuppetlabsOnceover::Beaker" do
|
|
20
|
+
before(:each) do
|
|
21
|
+
allow(Kernel).to receive(:warn)
|
|
22
|
+
allow_any_instance_of(Object).to receive(:require).and_wrap_original do |original, name|
|
|
23
|
+
%w[beaker-rspec beaker/network_manager].include?(name) || original.call(name)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe ".facts_to_vagrant_box" do
|
|
28
|
+
it "maps Ubuntu facts to a puppetlabs vagrant box name" do
|
|
29
|
+
facts = { 'os' => { 'distro' => { 'id' => 'Ubuntu', 'release' => { 'major' => '20.04' } }, 'architecture' => 'x86_64' } }
|
|
30
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_vagrant_box(facts)).to eq('puppetlabs/ubuntu-20.04-64-puppet')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "maps Debian facts to a puppetlabs vagrant box name" do
|
|
34
|
+
facts = { 'os' => { 'distro' => { 'id' => 'Debian', 'release' => { 'full' => '10.9' } }, 'architecture' => 'x86_64' } }
|
|
35
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_vagrant_box(facts)).to eq('puppetlabs/Debian-10.9-64-puppet')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "maps RedHat family facts to a centos vagrant box name" do
|
|
39
|
+
facts = { 'os' => { 'family' => 'RedHat', 'release' => { 'major' => '7', 'minor' => '9' }, 'architecture' => 'x86_64' } }
|
|
40
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_vagrant_box(facts)).to eq('puppetlabs/centos-7.9-64-puppet')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "returns UNKNOWN when the OS cannot be determined" do
|
|
44
|
+
facts = { 'os' => { 'family' => 'SomeWeirdOS', 'architecture' => 'x86_64' } }
|
|
45
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_vagrant_box(facts)).to eq('UNKNOWN')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "detects 32-bit architectures" do
|
|
49
|
+
facts = { 'os' => { 'family' => 'RedHat', 'release' => { 'major' => '6', 'minor' => '0' } }, 'architecture' => 'i386' }
|
|
50
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_vagrant_box(facts)).to eq('puppetlabs/centos-6.0-32-puppet')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "recurses over an array of factsets" do
|
|
54
|
+
facts = [
|
|
55
|
+
{ 'os' => { 'family' => 'RedHat', 'release' => { 'major' => '7', 'minor' => '0' }, 'architecture' => 'x86_64' } },
|
|
56
|
+
{ 'os' => { 'family' => 'SomeWeirdOS', 'architecture' => 'x86_64' } }
|
|
57
|
+
]
|
|
58
|
+
result = PuppetlabsOnceover::Beaker.facts_to_vagrant_box(facts)
|
|
59
|
+
expect(result).to eq(['puppetlabs/centos-7.0-64-puppet', 'UNKNOWN'])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "does not blow up when the facts hash does not have the expected 'os' shape" do
|
|
63
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_vagrant_box({ 'architecture' => 'x86_64' })).to eq('UNKNOWN')
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe ".facts_to_platform" do
|
|
68
|
+
it "maps RedHat family facts to an 'el' platform string" do
|
|
69
|
+
facts = { 'os' => { 'family' => 'RedHat', 'release' => { 'major' => '7' }, 'architecture' => 'x86_64' } }
|
|
70
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_platform(facts)).to eq('el-7-64')
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "maps Ubuntu facts to an 'ubuntu' platform string" do
|
|
74
|
+
facts = { 'os' => { 'family' => 'Debian', 'distro' => { 'id' => 'Ubuntu', 'release' => { 'major' => '20.04' } }, 'architecture' => 'x86_64' } }
|
|
75
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_platform(facts)).to eq('ubuntu-20.04-64')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "maps Debian facts to a 'debian' platform string" do
|
|
79
|
+
facts = { 'os' => { 'family' => 'Debian', 'distro' => { 'id' => 'Debian', 'release' => { 'full' => '10.9' } }, 'architecture' => 'x86_64' } }
|
|
80
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_platform(facts)).to eq('debian-10.9-64')
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "detects 32-bit architectures" do
|
|
84
|
+
facts = { 'os' => { 'family' => 'RedHat', 'release' => { 'major' => '6' }, 'architecture' => 'i386' } }
|
|
85
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_platform(facts)).to eq('el-6-32')
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "produces a nil-nil platform string when nothing matches" do
|
|
89
|
+
facts = { 'os' => { 'family' => 'SomeWeirdOS', 'architecture' => 'x86_64' } }
|
|
90
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_platform(facts)).to eq('--64')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "recurses over an array of factsets" do
|
|
94
|
+
facts = [
|
|
95
|
+
{ 'os' => { 'family' => 'RedHat', 'release' => { 'major' => '7' }, 'architecture' => 'x86_64' } },
|
|
96
|
+
{ 'os' => { 'family' => 'RedHat', 'release' => { 'major' => '8' }, 'architecture' => 'x86_64' } }
|
|
97
|
+
]
|
|
98
|
+
expect(PuppetlabsOnceover::Beaker.facts_to_platform(facts)).to eq(['el-7-64', 'el-8-64'])
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe ".deploy_controlrepo_on" do
|
|
103
|
+
it "installs r10k, git, copies the r10k config, and deploys via r10k on a single host" do
|
|
104
|
+
host = double('host')
|
|
105
|
+
repo = double('repo', r10k_config_file: '/tmp/r10k.yaml')
|
|
106
|
+
|
|
107
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:require).and_return(true)
|
|
108
|
+
allow(host).to receive(:is_a?).with(Array).and_return(false)
|
|
109
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:install_r10k_on)
|
|
110
|
+
allow(host).to receive(:install_package).with('git')
|
|
111
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:scp_to)
|
|
112
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:r10k_deploy)
|
|
113
|
+
|
|
114
|
+
PuppetlabsOnceover::Beaker.deploy_controlrepo_on(host, repo)
|
|
115
|
+
|
|
116
|
+
expect(PuppetlabsOnceover::Beaker).to have_received(:install_r10k_on).with(host)
|
|
117
|
+
expect(host).to have_received(:install_package).with('git')
|
|
118
|
+
expect(PuppetlabsOnceover::Beaker).to have_received(:scp_to).with(host, '/tmp/r10k.yaml', '/tmp/r10k.yaml')
|
|
119
|
+
expect(PuppetlabsOnceover::Beaker).to have_received(:r10k_deploy).with(host, { puppetfile: true, configfile: '/tmp/r10k.yaml' })
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "recurses when given an array of hosts (accepted quirk: iterates `hosts`, an undefined local, not the `host` array param)" do
|
|
123
|
+
host_array = double('host_array')
|
|
124
|
+
allow(host_array).to receive(:is_a?).with(Array).and_return(true)
|
|
125
|
+
repo = double('repo', r10k_config_file: '/tmp/r10k.yaml')
|
|
126
|
+
|
|
127
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:install_r10k_on)
|
|
128
|
+
allow(host_array).to receive(:install_package)
|
|
129
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:scp_to)
|
|
130
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:r10k_deploy)
|
|
131
|
+
|
|
132
|
+
# `hosts.each { ... }` inside the `if host.is_a?(Array)` branch references an
|
|
133
|
+
# undefined local `hosts` (not the `host` param) -- a real bug in the source.
|
|
134
|
+
# It raises NameError immediately, so the array-recursion branch never actually
|
|
135
|
+
# recurses; we assert the crash rather than pretending it works.
|
|
136
|
+
expect { PuppetlabsOnceover::Beaker.deploy_controlrepo_on(host_array, repo) }.to raise_error(NameError, /hosts/)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe ".provision_and_test" do
|
|
141
|
+
# provision_and_test's 4th positional param defaults to `PuppetlabsOnceover::Controlrepo.new`
|
|
142
|
+
# -- Ruby evaluates that default for any omitted arg before the method body (and its own
|
|
143
|
+
# raise checks) ever runs, and a real `Controlrepo.new` throws outside an actual controlrepo.
|
|
144
|
+
# Always pass an explicit (unused) repo double as the 4th arg to avoid tripping that default.
|
|
145
|
+
let(:unused_repo) { double('repo') }
|
|
146
|
+
|
|
147
|
+
it "raises when given an array of hosts" do
|
|
148
|
+
expect do
|
|
149
|
+
PuppetlabsOnceover::Beaker.provision_and_test([double('host')], 'role::example', {}, unused_repo)
|
|
150
|
+
end.to raise_error(RuntimeError, /must be a single host object/)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "raises when puppet_class is not a String" do
|
|
154
|
+
expect do
|
|
155
|
+
PuppetlabsOnceover::Beaker.provision_and_test(double('host'), :not_a_string, {}, unused_repo)
|
|
156
|
+
end.to raise_error(RuntimeError, /must be a single Class/)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "provisions (when host is not up), applies the manifest, checks idempotency, and cleans up" do
|
|
160
|
+
host = double('host', up?: false)
|
|
161
|
+
network_manager = double('network_manager')
|
|
162
|
+
allow(network_manager).to receive(:instance_variable_set)
|
|
163
|
+
allow(network_manager).to receive(:provision)
|
|
164
|
+
allow(network_manager).to receive(:proxy_package_manager)
|
|
165
|
+
allow(network_manager).to receive(:validate)
|
|
166
|
+
allow(network_manager).to receive(:configure)
|
|
167
|
+
allow(network_manager).to receive(:cleanup)
|
|
168
|
+
allow(Beaker::NetworkManager).to receive(:new).and_return(network_manager)
|
|
169
|
+
|
|
170
|
+
apply_calls = []
|
|
171
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:apply_manifest_on) do |h, manifest, opts|
|
|
172
|
+
apply_calls << [h, manifest, opts]
|
|
173
|
+
end
|
|
174
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:options).and_return({})
|
|
175
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:logger).and_return(double('logger'))
|
|
176
|
+
|
|
177
|
+
PuppetlabsOnceover::Beaker.provision_and_test(host, 'role::example', {}, unused_repo)
|
|
178
|
+
|
|
179
|
+
expect(network_manager).to have_received(:provision)
|
|
180
|
+
expect(network_manager).to have_received(:cleanup)
|
|
181
|
+
expect(apply_calls.map { |c| c[2] }).to include({ catch_failures: true }, { catch_changes: true })
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "skips provisioning when the host is already up, and skips idempotency check when disabled" do
|
|
185
|
+
host = double('host', up?: true)
|
|
186
|
+
network_manager = double('network_manager')
|
|
187
|
+
allow(network_manager).to receive(:instance_variable_set)
|
|
188
|
+
allow(network_manager).to receive(:cleanup)
|
|
189
|
+
allow(Beaker::NetworkManager).to receive(:new).and_return(network_manager)
|
|
190
|
+
|
|
191
|
+
apply_calls = []
|
|
192
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:apply_manifest_on) do |h, manifest, opts|
|
|
193
|
+
apply_calls << [h, manifest, opts]
|
|
194
|
+
end
|
|
195
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:options).and_return({})
|
|
196
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:logger).and_return(double('logger'))
|
|
197
|
+
|
|
198
|
+
PuppetlabsOnceover::Beaker.provision_and_test(host, 'role::example', { check_idempotency: false, runs_before_idempotency: 2 }, unused_repo)
|
|
199
|
+
|
|
200
|
+
expect(apply_calls.size).to eq(2)
|
|
201
|
+
expect(apply_calls.map { |c| c[2] }).to eq([{ catch_failures: true }, { catch_failures: true }])
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
describe ".match_indentation" do
|
|
206
|
+
it "sets the logger's line_prefix based on the scoped_id depth" do
|
|
207
|
+
test = double('test', metadata: { scoped_id: '1:2:3' })
|
|
208
|
+
logger = double('logger')
|
|
209
|
+
expect(logger).to receive(:line_prefix=).with(' ')
|
|
210
|
+
PuppetlabsOnceover::Beaker.match_indentation(test, logger)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "uses no prefix for a top-level scoped_id" do
|
|
214
|
+
test = double('test', metadata: { scoped_id: '1' })
|
|
215
|
+
logger = double('logger')
|
|
216
|
+
expect(logger).to receive(:line_prefix=).with('')
|
|
217
|
+
PuppetlabsOnceover::Beaker.match_indentation(test, logger)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
describe ".host_create" do
|
|
222
|
+
it "builds a single host via a NetworkManager, wires up a down! method, and returns it" do
|
|
223
|
+
nodes = { HOSTS: { 'centos7' => { platform: 'el-7-x86_64' }, 'other' => { platform: 'ubuntu' } }, other_opt: 'value' }
|
|
224
|
+
|
|
225
|
+
host_double = double('host')
|
|
226
|
+
allow(host_double).to receive(:instance_variable_set)
|
|
227
|
+
allow(host_double).to receive(:define_singleton_method)
|
|
228
|
+
|
|
229
|
+
network_manager = double('network_manager')
|
|
230
|
+
allow(network_manager).to receive(:provision)
|
|
231
|
+
allow(network_manager).to receive(:proxy_package_manager)
|
|
232
|
+
allow(network_manager).to receive(:validate)
|
|
233
|
+
allow(network_manager).to receive(:configure)
|
|
234
|
+
allow(network_manager).to receive(:instance_variable_get).with(:@hosts).and_return([host_double])
|
|
235
|
+
allow(Beaker::NetworkManager).to receive(:new).and_return(network_manager)
|
|
236
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:logger).and_return(double('logger'))
|
|
237
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:hosts).and_return([host_double])
|
|
238
|
+
|
|
239
|
+
result = PuppetlabsOnceover::Beaker.host_create('centos7', nodes)
|
|
240
|
+
|
|
241
|
+
expect(result).to eq(host_double)
|
|
242
|
+
expect(Beaker::NetworkManager).to have_received(:new) do |opts, _logger|
|
|
243
|
+
expect(opts[:HOSTS]).to eq({ 'centos7' => { platform: 'el-7-x86_64' } })
|
|
244
|
+
expect(opts[:other_opt]).to eq('value')
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it "supports the default_proc string/symbol key fallback on current_opts" do
|
|
249
|
+
nodes = { HOSTS: { 'centos7' => {} } }
|
|
250
|
+
captured_opts = nil
|
|
251
|
+
|
|
252
|
+
host_double = double('host')
|
|
253
|
+
allow(host_double).to receive(:instance_variable_set)
|
|
254
|
+
allow(host_double).to receive(:define_singleton_method)
|
|
255
|
+
|
|
256
|
+
network_manager = double('network_manager')
|
|
257
|
+
allow(network_manager).to receive(:provision)
|
|
258
|
+
allow(network_manager).to receive(:proxy_package_manager)
|
|
259
|
+
allow(network_manager).to receive(:validate)
|
|
260
|
+
allow(network_manager).to receive(:configure)
|
|
261
|
+
allow(network_manager).to receive(:instance_variable_get).with(:@hosts).and_return([host_double])
|
|
262
|
+
allow(Beaker::NetworkManager).to receive(:new) do |opts, _logger|
|
|
263
|
+
captured_opts = opts
|
|
264
|
+
network_manager
|
|
265
|
+
end
|
|
266
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:logger).and_return(double('logger'))
|
|
267
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:hosts).and_return([host_double])
|
|
268
|
+
|
|
269
|
+
PuppetlabsOnceover::Beaker.host_create('centos7', nodes)
|
|
270
|
+
|
|
271
|
+
expect(captured_opts[:HOSTS]).to eq({ 'centos7' => {} })
|
|
272
|
+
expect(captured_opts['HOSTS']).to eq({ 'centos7' => {} })
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
it "raises if the network manager created more than one host" do
|
|
276
|
+
nodes = { HOSTS: { 'centos7' => {} } }
|
|
277
|
+
|
|
278
|
+
network_manager = double('network_manager')
|
|
279
|
+
allow(network_manager).to receive(:provision)
|
|
280
|
+
allow(network_manager).to receive(:proxy_package_manager)
|
|
281
|
+
allow(network_manager).to receive(:validate)
|
|
282
|
+
allow(network_manager).to receive(:configure)
|
|
283
|
+
allow(network_manager).to receive(:instance_variable_get).with(:@hosts).and_return([double('a'), double('b')])
|
|
284
|
+
allow(Beaker::NetworkManager).to receive(:new).and_return(network_manager)
|
|
285
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:logger).and_return(double('logger'))
|
|
286
|
+
allow(PuppetlabsOnceover::Beaker).to receive(:hosts).and_return([double('a'), double('b')])
|
|
287
|
+
|
|
288
|
+
expect do
|
|
289
|
+
PuppetlabsOnceover::Beaker.host_create('centos7', nodes)
|
|
290
|
+
end.to raise_error(RuntimeError, /too many machines/)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'puppetlabs-onceover/class'
|
|
3
|
+
require 'puppetlabs-onceover/controlrepo'
|
|
4
|
+
|
|
5
|
+
describe "PuppetlabsOnceover::Class" do
|
|
6
|
+
before(:each) do
|
|
7
|
+
PuppetlabsOnceover::Class.class_variable_set(:@@all, [])
|
|
8
|
+
# Silence the info/warn logging noise from the shared $logger global
|
|
9
|
+
logger.level = :fatal
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe ".name_is_regexp?" do
|
|
13
|
+
it "returns true for a /.../ delimited string" do
|
|
14
|
+
expect(PuppetlabsOnceover::Class.name_is_regexp?('/role::/')).to eq(true)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "returns false for a plain string" do
|
|
18
|
+
expect(PuppetlabsOnceover::Class.name_is_regexp?('role::webserver')).to eq(false)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "returns false for a string that only starts with /" do
|
|
22
|
+
expect(PuppetlabsOnceover::Class.name_is_regexp?('/role::webserver')).to eq(false)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe ".string_to_regexp" do
|
|
27
|
+
it "converts a /.../ delimited string into a Regexp" do
|
|
28
|
+
expect(PuppetlabsOnceover::Class.string_to_regexp('/role::/')).to eq(Regexp.new('role::'))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "raises when the string is not regexp-shaped" do
|
|
32
|
+
expect { PuppetlabsOnceover::Class.string_to_regexp('role::webserver') }.to raise_error(
|
|
33
|
+
RuntimeError, /does not start and end with/
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "#initialize" do
|
|
39
|
+
it "sets the name and adds itself to .all for a plain class name" do
|
|
40
|
+
cls = PuppetlabsOnceover::Class.new('role::webserver')
|
|
41
|
+
expect(cls.name).to eq('role::webserver')
|
|
42
|
+
expect(PuppetlabsOnceover::Class.all).to eq([cls])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "when given a regexp name" do
|
|
46
|
+
before(:each) do
|
|
47
|
+
PuppetlabsOnceover::Controlrepo.new(path: 'spec/fixtures/controlrepos/function_mocking')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "expands into one Class object per matching controlrepo class, and does not add the regexp itself" do
|
|
51
|
+
PuppetlabsOnceover::Class.new('/role::/')
|
|
52
|
+
names = PuppetlabsOnceover::Class.all.map(&:name)
|
|
53
|
+
expect(names).not_to be_empty
|
|
54
|
+
expect(names).to all(match(/role::/))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "creates no objects when nothing matches" do
|
|
58
|
+
PuppetlabsOnceover::Class.new('/nothing_matches_this_prefix_xyz::/')
|
|
59
|
+
expect(PuppetlabsOnceover::Class.all).to eq([])
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe ".find" do
|
|
65
|
+
it "finds an exact match by name" do
|
|
66
|
+
cls = PuppetlabsOnceover::Class.new('role::webserver')
|
|
67
|
+
expect(PuppetlabsOnceover::Class.find('role::webserver')).to eq(cls)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "returns nil (and warns) when nothing matches" do
|
|
71
|
+
expect(PuppetlabsOnceover::Class.find('role::does_not_exist')).to be_nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "finds all matches when given a regexp name" do
|
|
75
|
+
a = PuppetlabsOnceover::Class.new('role::a')
|
|
76
|
+
b = PuppetlabsOnceover::Class.new('profile::b')
|
|
77
|
+
expect(PuppetlabsOnceover::Class.find('/role::/')).to eq([a])
|
|
78
|
+
expect(PuppetlabsOnceover::Class.find('/role::/')).not_to include(b)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe ".all" do
|
|
83
|
+
it "returns every class created so far" do
|
|
84
|
+
a = PuppetlabsOnceover::Class.new('role::a')
|
|
85
|
+
b = PuppetlabsOnceover::Class.new('role::b')
|
|
86
|
+
expect(PuppetlabsOnceover::Class.all).to eq([a, b])
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'puppetlabs-onceover/cli/run'
|
|
3
|
+
|
|
4
|
+
describe PuppetlabsOnceover::CLI::Run do
|
|
5
|
+
describe '.command' do
|
|
6
|
+
it 'builds a memoized Cri::Command named run' do
|
|
7
|
+
cmd = described_class.command
|
|
8
|
+
expect(cmd).to be_a(Cri::Command)
|
|
9
|
+
expect(cmd.name).to eq('run')
|
|
10
|
+
expect(described_class.command).to equal(cmd)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'prints help and exits 0 when run directly' do
|
|
14
|
+
allow_any_instance_of(Object).to receive(:exit)
|
|
15
|
+
expect { described_class.command.run([]) }.to output(/Runs either the spec or acceptance tests/).to_stdout
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'registers the spec and acceptance subcommands' do
|
|
19
|
+
names = described_class.command.subcommands.map(&:name)
|
|
20
|
+
expect(names).to include('spec', 'acceptance')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe PuppetlabsOnceover::CLI::Run::Spec do
|
|
25
|
+
describe '.command' do
|
|
26
|
+
it 'builds a memoized Cri::Command named spec' do
|
|
27
|
+
cmd = described_class.command
|
|
28
|
+
expect(cmd).to be_a(Cri::Command)
|
|
29
|
+
expect(cmd.name).to eq('spec')
|
|
30
|
+
expect(described_class.command).to equal(cmd)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'deploys locally then prepares and runs the spec suite' do
|
|
34
|
+
repo = double('repo', onceover_yaml: '/some/repo/.onceover.yaml')
|
|
35
|
+
config = double('config')
|
|
36
|
+
deploy = double('deploy', deploy_local: nil)
|
|
37
|
+
runner = double('runner', prepare!: nil, run_spec!: nil)
|
|
38
|
+
|
|
39
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
40
|
+
allow(PuppetlabsOnceover::TestConfig).to receive(:new).and_return(config)
|
|
41
|
+
allow(PuppetlabsOnceover::Deploy).to receive(:new).and_return(deploy)
|
|
42
|
+
allow(PuppetlabsOnceover::Runner).to receive(:new).and_return(runner)
|
|
43
|
+
|
|
44
|
+
described_class.command.run([])
|
|
45
|
+
|
|
46
|
+
expect(deploy).to have_received(:deploy_local).with(repo, anything)
|
|
47
|
+
expect(PuppetlabsOnceover::Runner).to have_received(:new).with(repo, config, :spec)
|
|
48
|
+
expect(runner).to have_received(:prepare!)
|
|
49
|
+
expect(runner).to have_received(:run_spec!)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe PuppetlabsOnceover::CLI::Run::Acceptance do
|
|
55
|
+
describe '.command' do
|
|
56
|
+
it 'builds a memoized Cri::Command named acceptance' do
|
|
57
|
+
cmd = described_class.command
|
|
58
|
+
expect(cmd).to be_a(Cri::Command)
|
|
59
|
+
expect(cmd.name).to eq('acceptance')
|
|
60
|
+
expect(described_class.command).to equal(cmd)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'warns about deprecation then prepares and runs the acceptance suite' do
|
|
64
|
+
repo = double('repo', onceover_yaml: '/some/repo/.onceover.yaml')
|
|
65
|
+
config = double('config')
|
|
66
|
+
runner = double('runner', prepare!: nil, run_acceptance!: nil)
|
|
67
|
+
|
|
68
|
+
allow(PuppetlabsOnceover::Controlrepo).to receive(:new).and_return(repo)
|
|
69
|
+
allow(PuppetlabsOnceover::TestConfig).to receive(:new).and_return(config)
|
|
70
|
+
allow(PuppetlabsOnceover::Runner).to receive(:new).and_return(runner)
|
|
71
|
+
|
|
72
|
+
expect { described_class.command.run([]) }.to output(/DEPRECATION/).to_stderr
|
|
73
|
+
|
|
74
|
+
expect(PuppetlabsOnceover::Runner).to have_received(:new).with(repo, config, :acceptance)
|
|
75
|
+
expect(runner).to have_received(:prepare!)
|
|
76
|
+
expect(runner).to have_received(:run_acceptance!)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|