puppetlabs-onceover 5.0.3 → 5.0.4

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.rubocop_todo.yml +35 -15
  4. data/CHANGELOG.md +387 -3
  5. data/Gemfile +1 -0
  6. data/lib/puppetlabs-onceover/cli/plugins.rb +3 -3
  7. data/lib/puppetlabs-onceover/controlrepo.rb +8 -7
  8. data/lib/puppetlabs-onceover/version.rb +1 -1
  9. data/spec/fixtures/controlrepos/deploy_test/Puppetfile +3 -0
  10. data/spec/fixtures/controlrepos/deploy_test/a_dir/nested_file.txt +1 -0
  11. data/spec/fixtures/controlrepos/deploy_test/a_symlink +1 -0
  12. data/spec/fixtures/controlrepos/deploy_test/environment.conf +1 -0
  13. data/spec/fixtures/controlrepos/deploy_test/regular_file.txt +1 -0
  14. data/spec/puppetlabs-onceover/beaker_spec.rb +293 -0
  15. data/spec/puppetlabs-onceover/class_spec.rb +89 -0
  16. data/spec/puppetlabs-onceover/cli/run_spec.rb +80 -0
  17. data/spec/puppetlabs-onceover/cli/show_spec.rb +67 -0
  18. data/spec/puppetlabs-onceover/cli_spec.rb +79 -0
  19. data/spec/puppetlabs-onceover/controlrepo_spec.rb +787 -5
  20. data/spec/puppetlabs-onceover/deploy_spec.rb +246 -0
  21. data/spec/puppetlabs-onceover/group_spec.rb +121 -0
  22. data/spec/puppetlabs-onceover/logger_spec.rb +30 -0
  23. data/spec/puppetlabs-onceover/node_spec.rb +124 -0
  24. data/spec/puppetlabs-onceover/rake_tasks_spec.rb +173 -0
  25. data/spec/puppetlabs-onceover/rspec/formatters_spec.rb +361 -0
  26. data/spec/puppetlabs-onceover/runner_spec.rb +307 -0
  27. data/spec/puppetlabs-onceover/test_spec.rb +177 -0
  28. data/spec/puppetlabs-onceover/testconfig_spec.rb +451 -0
  29. data/spec/puppetlabs-onceover/vendored_modules_spec.rb +302 -0
  30. data/spec/puppetlabs-onceover/version_spec.rb +10 -0
  31. data/spec/spec_helper.rb +8 -0
  32. metadata +22 -1
@@ -0,0 +1,302 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+ require 'fileutils'
4
+ require 'multi_json'
5
+ require 'net/http'
6
+ require 'puppetlabs-onceover/controlrepo' # pulls in `include PuppetlabsOnceover::Logger` at top-level
7
+ require 'puppetlabs-onceover/vendored_modules'
8
+
9
+ describe PuppetlabsOnceover::VendoredModules do
10
+ let(:tempdir) { Dir.mktmpdir }
11
+ let(:spec_dir) { Dir.mktmpdir }
12
+ let(:repo_double) { double('repo', tempdir: tempdir, spec_dir: spec_dir) }
13
+ let(:puppet_version) { Puppet.version }
14
+ let(:core_modules) { described_class::CORE_MODULES }
15
+
16
+ def populate_cache(cachedir, mods: core_modules, version: puppet_version, tag: 'v1.2.3')
17
+ FileUtils.mkdir_p(cachedir)
18
+ mods.each do |mod|
19
+ File.write(File.join(cachedir, "#{mod}-puppet_agent-#{version}.json"), MultiJson.dump([{ 'name' => tag }]))
20
+ end
21
+ end
22
+
23
+ after do
24
+ FileUtils.rm_rf(tempdir)
25
+ FileUtils.rm_rf(spec_dir)
26
+ end
27
+
28
+ describe '#initialize' do
29
+ context 'when the cache is already populated (cache-hit branch, no network access)' do
30
+ before { populate_cache(File.join(tempdir, 'vendored_modules')) }
31
+
32
+ subject(:vm) { described_class.new(repo: repo_double) }
33
+
34
+ it 'resolves a vendored reference for every core module' do
35
+ expect(vm.vendored_references.size).to eq(core_modules.size)
36
+ end
37
+
38
+ it 'builds the url and ref from the cached tag' do
39
+ ref = vm.vendored_references.first
40
+ expect(ref['url']).to eq("https://github.com/puppetlabs/puppetlabs-#{core_modules.first}.git")
41
+ expect(ref['ref']).to eq('refs/tags/v1.2.3')
42
+ end
43
+
44
+ it 'starts with an empty missing_vendored list' do
45
+ expect(vm.missing_vendored).to eq([])
46
+ end
47
+ end
48
+
49
+ context 'when the cachedir does not exist yet' do
50
+ it 'creates it via FileUtils.mkdir_p' do
51
+ cachedir = File.join(tempdir, 'vendored_modules')
52
+ expect(File.directory?(cachedir)).to be false
53
+ allow_any_instance_of(described_class).to receive(:github_get).and_return([{ 'name' => 'v9.9.9' }])
54
+ described_class.new(repo: repo_double)
55
+ expect(File.directory?(cachedir)).to be true
56
+ end
57
+ end
58
+
59
+ context 'when opts[:cachedir] is explicitly provided' do
60
+ it 'uses it instead of the repo tempdir default' do
61
+ custom_cachedir = Dir.mktmpdir
62
+ populate_cache(custom_cachedir)
63
+ vm = described_class.new(repo: repo_double, cachedir: custom_cachedir)
64
+ expect(vm.vendored_references.size).to eq(core_modules.size)
65
+ FileUtils.remove_entry(custom_cachedir)
66
+ end
67
+ end
68
+
69
+ context 'when opts[:force_update] is true' do
70
+ it 'always calls github_get, ignoring any existing cache' do
71
+ populate_cache(File.join(tempdir, 'vendored_modules'))
72
+ expect_any_instance_of(described_class).to receive(:github_get).at_least(:once).and_return([{ 'name' => 'v5.0.0' }])
73
+ described_class.new(repo: repo_double, force_update: true)
74
+ end
75
+ end
76
+
77
+ context 'when running against Puppet < 6' do
78
+ it 'raises' do
79
+ allow(Puppet).to receive(:version).and_return('5.5.10')
80
+ populate_cache(File.join(tempdir, 'vendored_modules'), version: '5.5.10')
81
+ expect { described_class.new(repo: repo_double) }.to raise_error(/only applies to puppet versions >= 6/)
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#component_cache' do
87
+ subject(:vm) do
88
+ populate_cache(File.join(tempdir, 'vendored_modules'))
89
+ described_class.new(repo: repo_double)
90
+ end
91
+
92
+ let(:component) { core_modules.first } # augeas_core
93
+ let(:desired_name) { "#{component}-puppet_agent-#{puppet_version}.json" }
94
+
95
+ def touch_files(dir, names)
96
+ FileUtils.mkdir_p(dir)
97
+ names.each { |n| FileUtils.touch(File.join(dir, n)) }
98
+ end
99
+
100
+ it 'falls back to the default cachedir file when there is no manual_vendored_dir' do
101
+ vm.instance_variable_set(:@manual_vendored_dir, File.join(tempdir, 'does-not-exist'))
102
+ result = vm.component_cache(component)
103
+ expect(result).to eq(File.join(vm.instance_variable_get(:@cachedir), desired_name))
104
+ end
105
+
106
+ it 'falls back to the default cachedir file when manual dir has zero matches' do
107
+ manual_dir = Dir.mktmpdir
108
+ touch_files(manual_dir, ['some_other_component-puppet_agent-1.0.0.json'])
109
+ vm.instance_variable_set(:@manual_vendored_dir, manual_dir)
110
+ result = vm.component_cache(component)
111
+ expect(result).to eq(File.join(vm.instance_variable_get(:@cachedir), desired_name))
112
+ FileUtils.remove_entry(manual_dir)
113
+ end
114
+
115
+ it 'uses the single manual cache file when exactly one match is found' do
116
+ manual_dir = Dir.mktmpdir
117
+ touch_files(manual_dir, ["#{component}-puppet_agent-9.0.0.json"])
118
+ vm.instance_variable_set(:@manual_vendored_dir, manual_dir)
119
+ result = vm.component_cache(component)
120
+ expect(result).to eq(File.join(manual_dir, "#{component}-puppet_agent-9.0.0.json"))
121
+ FileUtils.remove_entry(manual_dir)
122
+ end
123
+
124
+ it 'uses the exact version match when multiple manual caches exist and one matches exactly' do
125
+ manual_dir = Dir.mktmpdir
126
+ touch_files(manual_dir, [desired_name, "#{component}-puppet_agent-1.0.0.json"])
127
+ vm.instance_variable_set(:@manual_vendored_dir, manual_dir)
128
+ result = vm.component_cache(component)
129
+ expect(result).to eq(File.join(manual_dir, desired_name))
130
+ FileUtils.remove_entry(manual_dir)
131
+ end
132
+
133
+ it 'uses the latest major-version match when no exact match exists' do
134
+ major = vm.instance_variable_get(:@puppet_major_version).to_s
135
+ manual_dir = Dir.mktmpdir
136
+ touch_files(manual_dir, ["#{component}-puppet_agent-#{major}.1.0.json", "#{component}-puppet_agent-1.0.0.json"])
137
+ vm.instance_variable_set(:@manual_vendored_dir, manual_dir)
138
+ result = vm.component_cache(component)
139
+ expect(result).to eq(File.join(manual_dir, "#{component}-puppet_agent-#{major}.1.0.json"))
140
+ FileUtils.remove_entry(manual_dir)
141
+ end
142
+
143
+ it 'falls back to the latest supplied version when neither exact nor major-version matches exist' do
144
+ # Fixed, deliberately-unrealistic majors (the CI matrix runs this same
145
+ # spec against both puppet 8.x and 9.x, so hardcoding "9.0.0" would
146
+ # accidentally match the major-version-match branch instead of this
147
+ # fallback branch when the suite runs under puppet 9). The production
148
+ # major-version check is a plain substring match
149
+ # (`s["...puppet_agent-#{major}"]`), so "9#{major}.0.0"-style values
150
+ # are NOT safe either -- "99.0.0" still contains "...puppet_agent-9".
151
+ # 500/501 can't collide with any realistic puppet major version.
152
+ lower = "500.0.0"
153
+ higher = "501.0.0"
154
+ manual_dir = Dir.mktmpdir
155
+ touch_files(manual_dir, ["#{component}-puppet_agent-#{lower}.json", "#{component}-puppet_agent-#{higher}.json"])
156
+ vm.instance_variable_set(:@manual_vendored_dir, manual_dir)
157
+ result = vm.component_cache(component)
158
+ expect(result).to eq(File.join(manual_dir, "#{component}-puppet_agent-#{higher}.json"))
159
+ FileUtils.remove_entry(manual_dir)
160
+ end
161
+
162
+ it 'skips the manual dir entirely when force_update is true' do
163
+ manual_dir = Dir.mktmpdir
164
+ touch_files(manual_dir, ["#{component}-puppet_agent-9.0.0.json"])
165
+ vm.instance_variable_set(:@manual_vendored_dir, manual_dir)
166
+ vm.instance_variable_set(:@force_update, true)
167
+ result = vm.component_cache(component)
168
+ expect(result).to eq(File.join(vm.instance_variable_get(:@cachedir), desired_name))
169
+ FileUtils.remove_entry(manual_dir)
170
+ end
171
+ end
172
+
173
+ describe '#version_from_file' do
174
+ subject(:vm) do
175
+ populate_cache(File.join(tempdir, 'vendored_modules'))
176
+ described_class.new(repo: repo_double)
177
+ end
178
+
179
+ it 'parses the embedded puppet_agent version out of the filename' do
180
+ expect(vm.version_from_file('/some/path/augeas_core-puppet_agent-8.13.0.json')).to eq(Gem::Version.new('8.13.0'))
181
+ end
182
+ end
183
+
184
+ describe '#puppetfile_missing_vendored' do
185
+ subject(:vm) do
186
+ populate_cache(File.join(tempdir, 'vendored_modules'))
187
+ described_class.new(repo: repo_double)
188
+ end
189
+
190
+ it 'records core modules that are absent from the Puppetfile' do
191
+ existing_module = double('r10k_module', name: core_modules.first)
192
+ puppetfile = double('puppetfile')
193
+ allow(puppetfile).to receive(:load)
194
+ allow(puppetfile).to receive(:modules).and_return([existing_module])
195
+
196
+ vm.puppetfile_missing_vendored(puppetfile)
197
+
198
+ expect(vm.missing_vendored.size).to eq(core_modules.size - 1)
199
+ missing_names = vm.missing_vendored.map { |h| h.keys.first }
200
+ expect(missing_names).not_to include("puppetlabs-#{core_modules.first}")
201
+ expect(missing_names).to include("puppetlabs-#{core_modules[1]}")
202
+ end
203
+
204
+ it 'rewrites ssh urls to https for the missing entries' do
205
+ puppetfile = double('puppetfile')
206
+ allow(puppetfile).to receive(:load)
207
+ allow(puppetfile).to receive(:modules).and_return([])
208
+
209
+ vm.puppetfile_missing_vendored(puppetfile)
210
+
211
+ entry = vm.missing_vendored.first.values.first
212
+ expect(entry[:git]).to start_with('https://github.com/')
213
+ expect(entry[:git]).not_to include('git@github.com:')
214
+ end
215
+ end
216
+
217
+ describe '#query_or_cache' do
218
+ subject(:vm) do
219
+ populate_cache(File.join(tempdir, 'vendored_modules'))
220
+ described_class.new(repo: repo_double)
221
+ end
222
+
223
+ it 'reads from the cache file when it already exists' do
224
+ cache_file = File.join(Dir.mktmpdir, 'cached.json')
225
+ File.write(cache_file, MultiJson.dump([{ 'name' => 'v1.0.0' }]))
226
+ expect(vm).not_to receive(:github_get)
227
+ result = vm.query_or_cache('https://api.github.com/repos/puppetlabs/x/tags', nil, cache_file)
228
+ expect(result).to eq([{ 'name' => 'v1.0.0' }])
229
+ end
230
+
231
+ it 'calls github_get and writes the cache when the file does not exist' do
232
+ cache_file = File.join(Dir.mktmpdir, 'missing.json')
233
+ allow(vm).to receive(:github_get).and_return([{ 'name' => 'v2.0.0' }])
234
+ result = vm.query_or_cache('https://api.github.com/repos/puppetlabs/x/tags', nil, cache_file)
235
+ expect(result).to eq([{ 'name' => 'v2.0.0' }])
236
+ expect(File.exist?(cache_file)).to be true
237
+ expect(MultiJson.load(File.read(cache_file))).to eq([{ 'name' => 'v2.0.0' }])
238
+ end
239
+ end
240
+
241
+ describe '#github_get' do
242
+ subject(:vm) do
243
+ populate_cache(File.join(tempdir, 'vendored_modules'))
244
+ described_class.new(repo: repo_double)
245
+ end
246
+
247
+ it 'returns the parsed json body on a 200 response' do
248
+ response = Net::HTTPOK.new('1.1', '200', 'OK')
249
+ def response.body
250
+ MultiJson.dump([{ 'name' => 'v3.0.0' }])
251
+ end
252
+ http = instance_double(Net::HTTP)
253
+ allow(Net::HTTP).to receive(:new).and_return(http)
254
+ allow(http).to receive(:use_ssl=)
255
+ allow(http).to receive(:request).and_return(response)
256
+
257
+ result = vm.github_get('https://api.github.com/repos/puppetlabs/puppetlabs-augeas_core/tags', nil)
258
+ expect(result).to eq([{ 'name' => 'v3.0.0' }])
259
+ end
260
+
261
+ it 'passes along query params when given' do
262
+ response = Net::HTTPOK.new('1.1', '200', 'OK')
263
+ def response.body
264
+ MultiJson.dump([{ 'name' => 'v4.0.0' }])
265
+ end
266
+ http = instance_double(Net::HTTP)
267
+ allow(Net::HTTP).to receive(:new).and_return(http)
268
+ allow(http).to receive(:use_ssl=)
269
+ allow(http).to receive(:request).and_return(response)
270
+
271
+ result = vm.github_get('https://api.github.com/repos/puppetlabs/puppetlabs-augeas_core/tags', { 'per_page' => '1' })
272
+ expect(result).to eq([{ 'name' => 'v4.0.0' }])
273
+ end
274
+
275
+ it 'raises with the ratelimit headers on a non-200 response' do
276
+ response = Net::HTTPForbidden.new('1.1', '403', 'Forbidden')
277
+ def response.to_hash
278
+ { 'x-ratelimit-remaining' => ['0'] }
279
+ end
280
+ http = instance_double(Net::HTTP)
281
+ allow(Net::HTTP).to receive(:new).and_return(http)
282
+ allow(http).to receive(:use_ssl=)
283
+ allow(http).to receive(:request).and_return(response)
284
+
285
+ expect { vm.github_get('https://api.github.com/repos/puppetlabs/puppetlabs-augeas_core/tags', nil) }
286
+ .to raise_error(/403 Forbidden/)
287
+ end
288
+ end
289
+
290
+ describe '#read_json_dump and #write_json_dump' do
291
+ subject(:vm) do
292
+ populate_cache(File.join(tempdir, 'vendored_modules'))
293
+ described_class.new(repo: repo_double)
294
+ end
295
+
296
+ it 'round-trips json data through a file' do
297
+ file = File.join(Dir.mktmpdir, 'roundtrip.json')
298
+ vm.write_json_dump(file, [{ 'name' => 'v1.0.0' }])
299
+ expect(vm.read_json_dump(file)).to eq([{ 'name' => 'v1.0.0' }])
300
+ end
301
+ end
302
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'puppetlabs-onceover/version'
3
+
4
+ describe PuppetlabsOnceover do
5
+ it 'defines a VERSION constant' do
6
+ # Not a literal version string -- release-prep bumps this file on every
7
+ # release, which would otherwise break this test every single time.
8
+ expect(PuppetlabsOnceover::VERSION).to match(/\A\d+\.\d+\.\d+\z/)
9
+ end
10
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,11 @@
1
+ if ENV['COVERAGE']
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ skip '/spec/'
5
+ skip '/vendor/'
6
+ end
7
+ end
8
+
1
9
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
2
10
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
11
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppetlabs-onceover
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.3
4
+ version: 5.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
@@ -381,6 +381,11 @@ files:
381
381
  - spec/fixtures/controlrepos/control_branch/environment.conf
382
382
  - spec/fixtures/controlrepos/custom_puppetfile/Puppetfile.custom
383
383
  - spec/fixtures/controlrepos/custom_puppetfile/environment.conf
384
+ - spec/fixtures/controlrepos/deploy_test/Puppetfile
385
+ - spec/fixtures/controlrepos/deploy_test/a_dir/nested_file.txt
386
+ - spec/fixtures/controlrepos/deploy_test/a_symlink
387
+ - spec/fixtures/controlrepos/deploy_test/environment.conf
388
+ - spec/fixtures/controlrepos/deploy_test/regular_file.txt
384
389
  - spec/fixtures/controlrepos/factsets/environment.conf
385
390
  - spec/fixtures/controlrepos/factsets/site/profile/manifests/base.pp
386
391
  - spec/fixtures/controlrepos/factsets/site/role/manifests/example.pp
@@ -561,7 +566,23 @@ files:
561
566
  - spec/fixtures/controlrepos/windows/site/role/manifests/groups.pp
562
567
  - spec/fixtures/controlrepos/windows/site/role/manifests/users.pp
563
568
  - spec/fixtures/controlrepos/windows/spec/onceover.yaml
569
+ - spec/puppetlabs-onceover/beaker_spec.rb
570
+ - spec/puppetlabs-onceover/class_spec.rb
571
+ - spec/puppetlabs-onceover/cli/run_spec.rb
572
+ - spec/puppetlabs-onceover/cli/show_spec.rb
573
+ - spec/puppetlabs-onceover/cli_spec.rb
564
574
  - spec/puppetlabs-onceover/controlrepo_spec.rb
575
+ - spec/puppetlabs-onceover/deploy_spec.rb
576
+ - spec/puppetlabs-onceover/group_spec.rb
577
+ - spec/puppetlabs-onceover/logger_spec.rb
578
+ - spec/puppetlabs-onceover/node_spec.rb
579
+ - spec/puppetlabs-onceover/rake_tasks_spec.rb
580
+ - spec/puppetlabs-onceover/rspec/formatters_spec.rb
581
+ - spec/puppetlabs-onceover/runner_spec.rb
582
+ - spec/puppetlabs-onceover/test_spec.rb
583
+ - spec/puppetlabs-onceover/testconfig_spec.rb
584
+ - spec/puppetlabs-onceover/vendored_modules_spec.rb
585
+ - spec/puppetlabs-onceover/version_spec.rb
565
586
  - spec/spec_helper.rb
566
587
  - templates/.fixtures.yml.erb
567
588
  - templates/Gemfile.erb