r10k 3.11.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docker.yml +1 -1
  3. data/.github/workflows/rspec_tests.yml +2 -2
  4. data/.travis.yml +1 -1
  5. data/CHANGELOG.mkd +34 -0
  6. data/doc/dynamic-environments/configuration.mkd +35 -7
  7. data/doc/dynamic-environments/usage.mkd +26 -0
  8. data/doc/puppetfile.mkd +16 -4
  9. data/integration/tests/basic_functionality/basic_deployment.rb +176 -0
  10. data/lib/r10k/action/base.rb +1 -1
  11. data/lib/r10k/action/deploy/deploy_helpers.rb +4 -0
  12. data/lib/r10k/action/deploy/display.rb +1 -1
  13. data/lib/r10k/action/deploy/environment.rb +13 -8
  14. data/lib/r10k/action/deploy/module.rb +14 -10
  15. data/lib/r10k/action/puppetfile/check.rb +7 -5
  16. data/lib/r10k/action/puppetfile/install.rb +22 -16
  17. data/lib/r10k/action/puppetfile/purge.rb +12 -9
  18. data/lib/r10k/action/runner.rb +6 -0
  19. data/lib/r10k/action/visitor.rb +3 -0
  20. data/lib/r10k/cli/deploy.rb +1 -0
  21. data/lib/r10k/cli/puppetfile.rb +0 -1
  22. data/lib/r10k/content_synchronizer.rb +16 -4
  23. data/lib/r10k/environment/bare.rb +4 -7
  24. data/lib/r10k/environment/base.rb +64 -11
  25. data/lib/r10k/environment/name.rb +14 -9
  26. data/lib/r10k/environment/plain.rb +16 -0
  27. data/lib/r10k/environment/tarball.rb +78 -0
  28. data/lib/r10k/environment/with_modules.rb +6 -10
  29. data/lib/r10k/environment.rb +2 -0
  30. data/lib/r10k/errors.rb +5 -0
  31. data/lib/r10k/forge/module_release.rb +2 -1
  32. data/lib/r10k/git/cache.rb +4 -13
  33. data/lib/r10k/git/rugged/base_repository.rb +12 -1
  34. data/lib/r10k/git/rugged/cache.rb +8 -0
  35. data/lib/r10k/git/stateful_repository.rb +9 -0
  36. data/lib/r10k/initializers.rb +21 -7
  37. data/lib/r10k/logging.rb +78 -1
  38. data/lib/r10k/module/base.rb +5 -1
  39. data/lib/r10k/module/definition.rb +64 -0
  40. data/lib/r10k/module/forge.rb +10 -2
  41. data/lib/r10k/module/git.rb +22 -1
  42. data/lib/r10k/module/local.rb +6 -3
  43. data/lib/r10k/module/svn.rb +10 -0
  44. data/lib/r10k/module/tarball.rb +101 -0
  45. data/lib/r10k/module.rb +21 -2
  46. data/lib/r10k/module_loader/puppetfile/dsl.rb +8 -3
  47. data/lib/r10k/module_loader/puppetfile.rb +109 -30
  48. data/lib/r10k/puppetfile.rb +1 -2
  49. data/lib/r10k/settings.rb +46 -0
  50. data/lib/r10k/source/git.rb +18 -18
  51. data/lib/r10k/source/yaml.rb +1 -1
  52. data/lib/r10k/tarball.rb +183 -0
  53. data/lib/r10k/util/cacheable.rb +31 -0
  54. data/lib/r10k/util/downloader.rb +134 -0
  55. data/lib/r10k/util/purgeable.rb +6 -2
  56. data/lib/r10k/util/setopts.rb +2 -0
  57. data/lib/r10k/util/subprocess.rb +1 -0
  58. data/lib/r10k/version.rb +1 -1
  59. data/locales/r10k.pot +119 -71
  60. data/r10k.gemspec +2 -2
  61. data/r10k.yaml.example +28 -0
  62. data/spec/fixtures/tarball/tarball.tar.gz +0 -0
  63. data/spec/fixtures/unit/action/r10k_logging.yaml +12 -0
  64. data/spec/fixtures/unit/puppetfile/forge-override/Puppetfile +8 -0
  65. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile +10 -0
  66. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile.new +10 -0
  67. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_symlink_file +1 -0
  68. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/{managed_subdir_2 → subdir_allowlisted_2}/ignored_1 +0 -0
  69. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/unmanaged_symlink_dir +1 -0
  70. data/spec/fixtures/unit/util/purgeable/managed_one/managed_symlink_dir +1 -0
  71. data/spec/fixtures/unit/util/purgeable/managed_one/unmanaged_symlink_file +1 -0
  72. data/spec/integration/git/rugged/cache_spec.rb +33 -0
  73. data/spec/integration/util/purageable_spec.rb +41 -0
  74. data/spec/r10k-mocks/mock_env.rb +3 -0
  75. data/spec/r10k-mocks/mock_source.rb +7 -3
  76. data/spec/shared-contexts/tarball.rb +32 -0
  77. data/spec/spec_helper.rb +1 -0
  78. data/spec/unit/action/deploy/environment_spec.rb +80 -30
  79. data/spec/unit/action/deploy/module_spec.rb +52 -64
  80. data/spec/unit/action/puppetfile/check_spec.rb +17 -5
  81. data/spec/unit/action/puppetfile/install_spec.rb +42 -36
  82. data/spec/unit/action/puppetfile/purge_spec.rb +15 -17
  83. data/spec/unit/action/runner_spec.rb +52 -9
  84. data/spec/unit/environment/bare_spec.rb +13 -0
  85. data/spec/unit/environment/base_spec.rb +30 -17
  86. data/spec/unit/environment/git_spec.rb +2 -2
  87. data/spec/unit/environment/name_spec.rb +18 -0
  88. data/spec/unit/environment/plain_spec.rb +8 -0
  89. data/spec/unit/environment/svn_spec.rb +4 -3
  90. data/spec/unit/environment/tarball_spec.rb +45 -0
  91. data/spec/unit/environment/with_modules_spec.rb +3 -2
  92. data/spec/unit/git/cache_spec.rb +2 -15
  93. data/spec/unit/git/rugged/cache_spec.rb +19 -0
  94. data/spec/unit/module/base_spec.rb +8 -8
  95. data/spec/unit/module/forge_spec.rb +32 -4
  96. data/spec/unit/module/git_spec.rb +51 -10
  97. data/spec/unit/module/svn_spec.rb +18 -6
  98. data/spec/unit/module/tarball_spec.rb +70 -0
  99. data/spec/unit/module_loader/puppetfile_spec.rb +96 -31
  100. data/spec/unit/puppetfile_spec.rb +2 -2
  101. data/spec/unit/settings_spec.rb +25 -2
  102. data/spec/unit/tarball_spec.rb +57 -0
  103. data/spec/unit/util/cacheable_spec.rb +23 -0
  104. data/spec/unit/util/downloader_spec.rb +98 -0
  105. data/spec/unit/util/purgeable_spec.rb +22 -11
  106. metadata +44 -17
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'r10k/module_loader/puppetfile'
3
+ require 'tmpdir'
3
4
 
4
5
  describe R10K::ModuleLoader::Puppetfile do
5
6
  describe 'initial parameters' do
@@ -7,7 +8,6 @@ describe R10K::ModuleLoader::Puppetfile do
7
8
  let(:options) do
8
9
  {
9
10
  basedir: '/test/basedir/env',
10
- forge: 'localforge.internal.corp',
11
11
  overrides: { modules: { deploy_modules: true } },
12
12
  environment: R10K::Environment::Git.new('env',
13
13
  '/test/basedir/',
@@ -47,10 +47,6 @@ describe R10K::ModuleLoader::Puppetfile do
47
47
  end
48
48
  end
49
49
 
50
- it 'the forge' do
51
- expect(subject.instance_variable_get(:@forge)).to eq('localforge.internal.corp')
52
- end
53
-
54
50
  it 'the overrides' do
55
51
  expect(subject.instance_variable_get(:@overrides)).to eq({ modules: { deploy_modules: true }})
56
52
  end
@@ -71,10 +67,6 @@ describe R10K::ModuleLoader::Puppetfile do
71
67
  expect(subject.instance_variable_get(:@puppetfile_path)).to eq('/test/basedir/Puppetfile')
72
68
  end
73
69
 
74
- it 'uses the public forge' do
75
- expect(subject.instance_variable_get(:@forge)).to eq('forgeapi.puppetlabs.com')
76
- end
77
-
78
70
  it 'creates an empty overrides' do
79
71
  expect(subject.instance_variable_get(:@overrides)).to eq({})
80
72
  end
@@ -91,14 +83,14 @@ describe R10K::ModuleLoader::Puppetfile do
91
83
  subject { R10K::ModuleLoader::Puppetfile.new(basedir: basedir) }
92
84
 
93
85
  it 'should transform Forge modules with a string arg to have a version key' do
94
- expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
86
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
95
87
 
96
88
  expect { subject.add_module('puppet/test_module', '1.2.3') }.to change { subject.modules }
97
89
  expect(subject.modules.collect(&:name)).to include('test_module')
98
90
  end
99
91
 
100
92
  it 'should not accept Forge modules with a version comparison' do
101
- expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '< 1.2.0'), anything).and_call_original
93
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', subject.moduledir, hash_including(version: '< 1.2.0'), anything).and_call_original
102
94
 
103
95
  expect {
104
96
  subject.add_module('puppet/test_module', '< 1.2.0')
@@ -122,7 +114,7 @@ describe R10K::ModuleLoader::Puppetfile do
122
114
  it 'should accept non-Forge modules with a hash arg' do
123
115
  module_opts = { git: 'git@example.com:puppet/test_module.git' }
124
116
 
125
- expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, module_opts, anything).and_call_original
117
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', subject.moduledir, module_opts, anything).and_call_original
126
118
 
127
119
  expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
128
120
  expect(subject.modules.collect(&:name)).to include('test_module')
@@ -134,7 +126,7 @@ describe R10K::ModuleLoader::Puppetfile do
134
126
  git: 'git@example.com:puppet/test_module.git',
135
127
  }
136
128
 
137
- expect(R10K::Module).to receive(:new).with('puppet/test_module', File.join(basedir, 'vendor'), module_opts, anything).and_call_original
129
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', File.join(basedir, 'vendor'), module_opts, anything).and_call_original
138
130
 
139
131
  expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
140
132
  expect(subject.modules.collect(&:name)).to include('test_module')
@@ -148,7 +140,7 @@ describe R10K::ModuleLoader::Puppetfile do
148
140
  git: 'git@example.com:puppet/test_module.git',
149
141
  }
150
142
 
151
- expect(R10K::Module).to receive(:new).with('puppet/test_module', install_path, module_opts, anything).and_call_original
143
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', install_path, module_opts, anything).and_call_original
152
144
 
153
145
  expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
154
146
  expect(subject.modules.collect(&:name)).to include('test_module')
@@ -175,11 +167,12 @@ describe R10K::ModuleLoader::Puppetfile do
175
167
  it 'should disable and not add modules that conflict with the environment' do
176
168
  env = instance_double('R10K::Environment::Base')
177
169
  mod = instance_double('R10K::Module::Base', name: 'conflict', origin: :puppetfile, 'origin=': nil)
170
+ allow(env).to receive(:name).and_return('conflict')
178
171
  loader = R10K::ModuleLoader::Puppetfile.new(basedir: basedir, environment: env)
179
172
  allow(env).to receive(:'module_conflicts?').with(mod).and_return(true)
180
173
  allow(mod).to receive(:spec_deletable=)
181
174
 
182
- expect(R10K::Module).to receive(:new).with('conflict', anything, anything, anything).and_return(mod)
175
+ expect(R10K::Module).to receive(:from_metadata).with('conflict', anything, anything, anything).and_return(mod)
183
176
  expect { loader.add_module('conflict', {}) }.not_to change { loader.modules }
184
177
  end
185
178
  end
@@ -195,7 +188,9 @@ describe R10K::ModuleLoader::Puppetfile do
195
188
  context 'when belonging to an environment' do
196
189
  let(:env_contents) { ['env1', 'env2' ] }
197
190
  let(:env) { double(:environment, desired_contents: env_contents) }
198
-
191
+ before {
192
+ allow(env).to receive(:name).and_return('env1')
193
+ }
199
194
  subject { R10K::ModuleLoader::Puppetfile.new(basedir: '/test/basedir', environment: env) }
200
195
 
201
196
  it "includes environment's desired_contents" do
@@ -214,9 +209,9 @@ describe R10K::ModuleLoader::Puppetfile do
214
209
  end
215
210
 
216
211
  it 'returns an array of paths that #purge! will operate within' do
217
- expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
212
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
218
213
  subject.add_module('puppet/test_module', '1.2.3')
219
- subject.load
214
+ subject.load!
220
215
 
221
216
  expect(subject.modules.length).to be 1
222
217
  expect(subject.managed_directories).to match_array([subject.moduledir])
@@ -226,9 +221,9 @@ describe R10K::ModuleLoader::Puppetfile do
226
221
  it "basedir isn't in the list of paths to purge" do
227
222
  module_opts = { install_path: '', git: 'git@example.com:puppet/test_module.git' }
228
223
 
229
- expect(R10K::Module).to receive(:new).with('puppet/test_module', basedir, module_opts, anything).and_call_original
224
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', basedir, module_opts, anything).and_call_original
230
225
  subject.add_module('puppet/test_module', module_opts)
231
- subject.load
226
+ subject.load!
232
227
 
233
228
  expect(subject.modules.length).to be 1
234
229
  expect(subject.managed_directories).to be_empty
@@ -249,7 +244,7 @@ describe R10K::ModuleLoader::Puppetfile do
249
244
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'invalid-syntax')
250
245
  pf_path = File.join(@path, 'Puppetfile')
251
246
  expect {
252
- subject.load
247
+ subject.load!
253
248
  }.to raise_error do |e|
254
249
  expect_wrapped_error(e, pf_path, SyntaxError)
255
250
  end
@@ -259,7 +254,7 @@ describe R10K::ModuleLoader::Puppetfile do
259
254
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'load-error')
260
255
  pf_path = File.join(@path, 'Puppetfile')
261
256
  expect {
262
- subject.load
257
+ subject.load!
263
258
  }.to raise_error do |e|
264
259
  expect_wrapped_error(e, pf_path, LoadError)
265
260
  end
@@ -269,17 +264,38 @@ describe R10K::ModuleLoader::Puppetfile do
269
264
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'argument-error')
270
265
  pf_path = File.join(@path, 'Puppetfile')
271
266
  expect {
272
- subject.load
267
+ subject.load!
273
268
  }.to raise_error do |e|
274
269
  expect_wrapped_error(e, pf_path, ArgumentError)
275
270
  end
276
271
  end
277
272
 
273
+ describe 'forge declaration' do
274
+ before(:each) do
275
+ PuppetForge.host = ""
276
+ end
277
+
278
+ it 'is respected if `allow_puppetfile_override` is true' do
279
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'forge-override')
280
+ puppetfile = R10K::ModuleLoader::Puppetfile.new(basedir: @path, overrides: { forge: { allow_puppetfile_override: true } })
281
+ puppetfile.load!
282
+ expect(PuppetForge.host).to eq("my.custom.forge.com/")
283
+ end
284
+
285
+ it 'is ignored if `allow_puppetfile_override` is false' do
286
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'forge-override')
287
+ puppetfile = R10K::ModuleLoader::Puppetfile.new(basedir: @path, overrides: { forge: { allow_puppetfile_override: false } })
288
+ expect(PuppetForge).not_to receive(:host=).with("my.custom.forge.com")
289
+ puppetfile.load!
290
+ expect(PuppetForge.host).to eq("/")
291
+ end
292
+ end
293
+
278
294
  it 'rejects Puppetfiles with duplicate module names' do
279
295
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'duplicate-module-error')
280
296
  pf_path = File.join(@path, 'Puppetfile')
281
297
  expect {
282
- subject.load
298
+ subject.load!
283
299
  }.to raise_error(R10K::Error, /Puppetfiles cannot contain duplicate module names/i)
284
300
  end
285
301
 
@@ -287,7 +303,7 @@ describe R10K::ModuleLoader::Puppetfile do
287
303
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'name-error')
288
304
  pf_path = File.join(@path, 'Puppetfile')
289
305
  expect {
290
- subject.load
306
+ subject.load!
291
307
  }.to raise_error do |e|
292
308
  expect_wrapped_error(e, pf_path, NameError)
293
309
  end
@@ -296,21 +312,21 @@ describe R10K::ModuleLoader::Puppetfile do
296
312
  it 'accepts a forge module with a version' do
297
313
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
298
314
  pf_path = File.join(@path, 'Puppetfile')
299
- expect { subject.load }.not_to raise_error
315
+ expect { subject.load! }.not_to raise_error
300
316
  end
301
317
 
302
318
  describe 'setting a custom moduledir' do
303
319
  it 'allows setting an absolute moduledir' do
304
320
  @path = '/fake/basedir'
305
321
  allow(subject).to receive(:puppetfile_content).and_return('moduledir "/fake/moduledir"')
306
- subject.load
322
+ subject.load!
307
323
  expect(subject.instance_variable_get(:@moduledir)).to eq('/fake/moduledir')
308
324
  end
309
325
 
310
326
  it 'roots relative moduledirs in the basedir' do
311
327
  @path = '/fake/basedir'
312
328
  allow(subject).to receive(:puppetfile_content).and_return('moduledir "my/moduledir"')
313
- subject.load
329
+ subject.load!
314
330
  expect(subject.instance_variable_get(:@moduledir)).to eq(File.join(@path, 'my/moduledir'))
315
331
  end
316
332
  end
@@ -318,13 +334,13 @@ describe R10K::ModuleLoader::Puppetfile do
318
334
  it 'accepts a forge module without a version' do
319
335
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-without-version')
320
336
  pf_path = File.join(@path, 'Puppetfile')
321
- expect { subject.load }.not_to raise_error
337
+ expect { subject.load! }.not_to raise_error
322
338
  end
323
339
 
324
340
  it 'creates a git module and applies the default branch specified in the Puppetfile' do
325
341
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'default-branch-override')
326
342
  pf_path = File.join(@path, 'Puppetfile')
327
- expect { subject.load }.not_to raise_error
343
+ expect { subject.load! }.not_to raise_error
328
344
  git_module = subject.modules[0]
329
345
  expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
330
346
  end
@@ -334,10 +350,59 @@ describe R10K::ModuleLoader::Puppetfile do
334
350
  pf_path = File.join(@path, 'Puppetfile')
335
351
  default_branch_override = 'default_branch_override_name'
336
352
  subject.default_branch_override = default_branch_override
337
- expect { subject.load }.not_to raise_error
353
+ expect { subject.load! }.not_to raise_error
338
354
  git_module = subject.modules[0]
339
355
  expect(git_module.default_override_ref).to eq default_branch_override
340
356
  expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
341
357
  end
358
+
359
+ describe 'using module metadata' do
360
+ it 'properly loads module metadata' do
361
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'various-modules')
362
+ metadata = subject.load_metadata[:modules].map { |mod| [ mod.name, mod.version ] }.to_h
363
+ expect(metadata['apt']).to eq('2.1.1')
364
+ expect(metadata['stdlib']).to eq(nil)
365
+ expect(metadata['concat']).to eq(nil)
366
+ expect(metadata['rpm']).to eq('2.1.1-pre1')
367
+ expect(metadata['foo']).to eq(nil)
368
+ expect(metadata['bar']).to eq('v1.2.3')
369
+ expect(metadata['baz']).to eq('123abc456')
370
+ expect(metadata['fizz']).to eq('1234567890abcdef1234567890abcdef12345678')
371
+ expect(metadata['buzz']).to eq(nil)
372
+ expect(metadata['canary']).to eq('0.0.0')
373
+ end
374
+
375
+ it 'does not load module implementations for static versioned' do
376
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'various-modules')
377
+ subject.load_metadata
378
+ modules = subject.load[:modules].map { |mod| [ mod.name, mod ] }.to_h
379
+ expect(modules['apt']).to be_a_kind_of(R10K::Module::Definition)
380
+ expect(modules['stdlib']).to be_a_kind_of(R10K::Module::Forge)
381
+ expect(modules['concat']).to be_a_kind_of(R10K::Module::Forge)
382
+ expect(modules['rpm']).to be_a_kind_of(R10K::Module::Definition)
383
+ expect(modules['foo']).to be_a_kind_of(R10K::Module::Git)
384
+ expect(modules['bar']).to be_a_kind_of(R10K::Module::Definition)
385
+ expect(modules['baz']).to be_a_kind_of(R10K::Module::Definition)
386
+ expect(modules['fizz']).to be_a_kind_of(R10K::Module::Definition)
387
+ expect(modules['buzz']).to be_a_kind_of(R10K::Module::Git)
388
+ expect(modules['canary']).to be_a_kind_of(R10K::Module::Definition)
389
+ end
390
+
391
+ it 'loads module implementations whose static versions are different' do
392
+ fixture_path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'various-modules')
393
+ @path = Dir.mktmpdir
394
+ unsynced_pf_path = File.join(fixture_path, 'Puppetfile')
395
+ FileUtils.cp(unsynced_pf_path, @path)
396
+
397
+ subject.load_metadata
398
+
399
+ synced_pf_path = File.join(fixture_path, 'Puppetfile.new')
400
+ FileUtils.cp(synced_pf_path, File.join(@path, 'Puppetfile'))
401
+
402
+ modules = subject.load[:modules].map { |mod| [ mod.name, mod ] }.to_h
403
+
404
+ expect(modules['apt']).to be_a_kind_of(R10K::Module::Forge)
405
+ end
406
+ end
342
407
  end
343
408
  end
@@ -114,7 +114,7 @@ describe R10K::Puppetfile do
114
114
  path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
115
115
  subject = described_class.new(path, {})
116
116
 
117
- expect(subject.loader).to receive(:load).and_call_original.once
117
+ expect(subject.loader).to receive(:load!).and_call_original.once
118
118
 
119
119
  loaded_content1 = subject.load
120
120
  expect(subject.loaded?).to be true
@@ -273,7 +273,7 @@ describe R10K::Puppetfile do
273
273
  expect(subject).to receive(:modules).and_return([mod1, mod2])
274
274
 
275
275
  expect(Thread).to receive(:new).exactly(pool_size).and_call_original
276
- expect(Queue).to receive(:new).and_call_original
276
+ expect(Queue).to receive(:new).and_call_original.twice
277
277
 
278
278
  subject.accept(visitor)
279
279
  end
@@ -90,6 +90,27 @@ describe R10K::Settings do
90
90
  end
91
91
  end
92
92
  end
93
+
94
+ describe "allow_puppetfile_override" do
95
+ it 'is false by default' do
96
+ expect(subject.evaluate({})[:allow_puppetfile_override]).to eq(false)
97
+ end
98
+
99
+ it 'can be set to true' do
100
+ expect(subject.evaluate({"allow_puppetfile_override" => true})[:allow_puppetfile_override]).to eq(true)
101
+ end
102
+
103
+ it "raises an error for non-boolean values" do
104
+ expect {
105
+ subject.evaluate({"allow_puppetfile_override" => 'invalid_string'})
106
+ }.to raise_error do |err|
107
+ expect(err.message).to match(/Validation failed for 'forge' settings group/)
108
+ expect(err.errors.size).to eq 1
109
+ expect(err.errors[:allow_puppetfile_override]).to be_a_kind_of(ArgumentError)
110
+ expect(err.errors[:allow_puppetfile_override].message).to match(/`allow_puppetfile_override` can only be a boolean value, not 'invalid_string'/)
111
+ end
112
+ end
113
+ end
93
114
  end
94
115
 
95
116
  describe "deploy settings" do
@@ -270,10 +291,12 @@ describe R10K::Settings do
270
291
  it "passes settings through to the forge settings" do
271
292
  output = subject.evaluate("forge" => {"baseurl" => "https://forge.tessier-ashpool.freeside",
272
293
  "proxy" => "https://proxy.tessier-ashpool.freesize:3128",
273
- "authorization_token" => "faketoken"})
294
+ "authorization_token" => "faketoken",
295
+ "allow_puppetfile_override" => true})
274
296
  expect(output[:forge]).to eq(:baseurl => "https://forge.tessier-ashpool.freeside",
275
297
  :proxy => "https://proxy.tessier-ashpool.freesize:3128",
276
- :authorization_token => "faketoken")
298
+ :authorization_token => "faketoken",
299
+ :allow_puppetfile_override => true)
277
300
  end
278
301
  end
279
302
  end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'r10k/tarball'
3
+
4
+ describe R10K::Tarball do
5
+ include_context 'Tarball'
6
+
7
+ subject { described_class.new('fixture-tarball', fixture_tarball, checksum: fixture_checksum) }
8
+
9
+ describe 'initialization' do
10
+ it 'initializes' do
11
+ expect(subject).to be_kind_of(described_class)
12
+ end
13
+ end
14
+
15
+ describe 'downloading and caching' do
16
+ it 'downloads the source to the cache' do
17
+ # No cache present initially
18
+ expect(File.exist?(subject.cache_path)).to be(false)
19
+ expect(subject.cache_valid?).to be(false)
20
+
21
+ subject.get
22
+
23
+ expect(subject.cache_valid?).to be(true)
24
+ expect(File.exist?(subject.cache_path)).to be(true)
25
+ end
26
+
27
+ let(:raw_content) {[
28
+ './',
29
+ './Puppetfile',
30
+ './metadata.json',
31
+ './spec/',
32
+ './environment.conf',
33
+ './spec/1',
34
+ ]}
35
+
36
+ let(:clean_content) {[
37
+ 'Puppetfile',
38
+ 'metadata.json',
39
+ 'spec',
40
+ 'environment.conf',
41
+ 'spec/1',
42
+ ]}
43
+
44
+ it 'returns clean paths when listing cached tarball content' do
45
+ iterator = allow(subject).to receive(:each_tarball_entry)
46
+ raw_content.each { |entry| iterator.and_yield(entry) }
47
+
48
+ expect(subject.paths).to eq(clean_content)
49
+ end
50
+ end
51
+
52
+ describe 'http sources'
53
+
54
+ describe 'file sources'
55
+
56
+ describe 'syncing'
57
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'r10k/util/cacheable'
3
+
4
+ RSpec.describe R10K::Util::Cacheable do
5
+
6
+ subject { Object.new.extend(R10K::Util::Cacheable) }
7
+
8
+ describe "dirname sanitization" do
9
+ let(:input) { 'git://some/git/remote' }
10
+
11
+ it 'sanitizes URL to directory name' do
12
+ expect(subject.sanitized_dirname(input)).to eq('git---some-git-remote')
13
+ end
14
+
15
+ context 'with username and password' do
16
+ let(:input) { 'https://"user:pa$$w0rd:@authenticated/git/remote' }
17
+
18
+ it 'sanitizes authenticated URL to directory name' do
19
+ expect(subject.sanitized_dirname(input)).to eq('https---authenticated-git-remote')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+ require 'r10k/util/downloader'
3
+
4
+ describe R10K::Util::Downloader do
5
+
6
+ subject(:downloader) do
7
+ subj = Object.new
8
+ subj.extend(R10K::Util::Downloader)
9
+ subj.singleton_class.class_eval { public :download }
10
+ subj.singleton_class.class_eval { public :http_get }
11
+ subj.singleton_class.class_eval { public :file_digest }
12
+ subj
13
+ end
14
+
15
+ let(:tmpdir) { Dir.mktmpdir }
16
+ after(:each) { FileUtils.remove_entry_secure(tmpdir) }
17
+
18
+ describe 'http_get' do
19
+ let(:src_url) { 'https://example.com' }
20
+ let(:dst_file) { File.join(tmpdir, 'test.out') }
21
+ let(:tarball_uri) { URI('http://tarball.example.com/tarball.tar.gz') }
22
+ let(:redirect_uri) { URI('http://redirect.example.com/redirect') }
23
+ let(:proxy_uri) { URI('http://user:password@proxy.example.com') }
24
+
25
+ it 'downloads a simple file' do
26
+ mock_session = instance_double('Net::HTTP', active?: true)
27
+ tarball_response = instance_double('Net::HTTPSuccess')
28
+
29
+ expect(Net::HTTP).to receive(:new).with(tarball_uri.host, any_args).and_return(mock_session)
30
+ expect(Net::HTTPSuccess).to receive(:===).with(tarball_response).and_return(true)
31
+
32
+ expect(mock_session).to receive(:request_get).and_yield(tarball_response)
33
+ expect(mock_session).to receive(:start).once
34
+ expect(mock_session).to receive(:finish).once
35
+
36
+ expect { |b| downloader.http_get(tarball_uri, &b) }.to yield_with_args(tarball_response)
37
+ end
38
+
39
+ it 'follows redirects' do
40
+ mock_session_1 = instance_double('Net::HTTP', active?: false)
41
+ mock_session_2 = instance_double('Net::HTTP', active?: true)
42
+ redirect_response = instance_double('Net::HTTPRedirection')
43
+ tarball_response = instance_double('Net::HTTPSuccess')
44
+
45
+ expect(Net::HTTP).to receive(:new).with(redirect_uri.host, any_args).and_return(mock_session_1).once
46
+ expect(Net::HTTP).to receive(:new).with(tarball_uri.host, any_args).and_return(mock_session_2).once
47
+ expect(Net::HTTPRedirection).to receive(:===).with(redirect_response).and_return(true)
48
+ expect(Net::HTTPSuccess).to receive(:===).with(tarball_response).and_return(true)
49
+ allow(Net::HTTPRedirection).to receive(:===).and_call_original
50
+
51
+ expect(mock_session_1).to receive(:request_get).and_yield(redirect_response)
52
+ expect(mock_session_2).to receive(:request_get).and_yield(tarball_response)
53
+
54
+ # The redirect response should be queried for the redirect location
55
+ expect(redirect_response).to receive(:[]).with('location').and_return(tarball_uri.to_s)
56
+
57
+ # Both sessions should start and finish cleanly
58
+ expect(mock_session_1).to receive(:start).once
59
+ expect(mock_session_1).to receive(:finish).once
60
+ expect(mock_session_2).to receive(:start).once
61
+ expect(mock_session_2).to receive(:finish).once
62
+
63
+ expect { |b| downloader.http_get(redirect_uri, &b) }.to yield_with_args(tarball_response)
64
+ end
65
+
66
+ it 'can use a proxy' do
67
+ mock_session = instance_double('Net::HTTP', active?: true)
68
+
69
+ expect(Net::HTTP).to receive(:new)
70
+ .with(tarball_uri.host,
71
+ tarball_uri.port,
72
+ proxy_uri.host,
73
+ proxy_uri.port,
74
+ proxy_uri.user,
75
+ proxy_uri.password,
76
+ any_args)
77
+ .and_return(mock_session)
78
+
79
+ expect(mock_session).to receive(:request_get).and_return(:not_yielded)
80
+ expect(mock_session).to receive(:start).once
81
+ expect(mock_session).to receive(:finish).once
82
+
83
+ downloader.http_get(tarball_uri, proxy: proxy_uri)
84
+ end
85
+ end
86
+
87
+ describe 'checksums' do
88
+ let(:fixture_checksum) { '0bcea17aa0c5e868c18f0fa042feda770e47c1a4223229f82116ccb3ca33c6e3' }
89
+ let(:fixture_tarball) do
90
+ File.expand_path('spec/fixtures/integration/git/puppet-boolean-bare.tar', PROJECT_ROOT)
91
+ end
92
+
93
+ it 'checksums files' do
94
+ expect(downloader.file_digest(fixture_tarball)).to eql(fixture_checksum)
95
+ end
96
+ end
97
+ end
98
+
@@ -15,8 +15,10 @@ RSpec.describe R10K::Util::Purgeable do
15
15
  'spec/fixtures/unit/util/purgeable/managed_one/expected_1',
16
16
  'spec/fixtures/unit/util/purgeable/managed_one/new_1',
17
17
  'spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1',
18
+ 'spec/fixtures/unit/util/purgeable/managed_one/managed_symlink_dir',
18
19
  'spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/subdir_expected_1',
19
20
  'spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/subdir_new_1',
21
+ 'spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_symlink_file',
20
22
  'spec/fixtures/unit/util/purgeable/managed_two/expected_2',
21
23
  'spec/fixtures/unit/util/purgeable/managed_two/new_2',
22
24
  'spec/fixtures/unit/util/purgeable/managed_two/.hidden',
@@ -30,7 +32,8 @@ RSpec.describe R10K::Util::Purgeable do
30
32
 
31
33
  describe '#current_contents' do
32
34
  it 'collects direct contents of all managed directories' do
33
- expect(subject.current_contents(recurse)).to contain_exactly(/\/expected_1/, /\/expected_2/, /\/unmanaged_1/, /\/unmanaged_2/, /\/managed_subdir_1/)
35
+ expect(subject.current_contents(recurse)).to contain_exactly(/\/expected_1/, /\/expected_2/, /\/unmanaged_1/, /\/unmanaged_2/,
36
+ /\/managed_subdir_1/, /\/managed_symlink_dir/, /\/unmanaged_symlink_file/)
34
37
  end
35
38
  end
36
39
 
@@ -46,7 +49,7 @@ RSpec.describe R10K::Util::Purgeable do
46
49
  let(:whitelist) { [] }
47
50
 
48
51
  it 'collects current_contents that should not exist' do
49
- expect(subject.stale_contents(recurse, exclusions, whitelist)).to contain_exactly(/\/unmanaged_1/, /\/unmanaged_2/)
52
+ expect(subject.stale_contents(recurse, exclusions, whitelist)).to contain_exactly(/\/unmanaged_1/, /\/unmanaged_2/, /\/unmanaged_symlink_file/)
50
53
  end
51
54
  end
52
55
 
@@ -56,7 +59,7 @@ RSpec.describe R10K::Util::Purgeable do
56
59
 
57
60
  it 'collects current_contents that should not exist except whitelisted items' do
58
61
  expect(subject.logger).to receive(:debug).with(/unmanaged_1.*whitelist match/i)
59
- expect(subject.stale_contents(recurse, exclusions, whitelist)).to contain_exactly(/\/unmanaged_2/)
62
+ expect(subject.stale_contents(recurse, exclusions, whitelist)).to contain_exactly(/\/unmanaged_2/, /\/unmanaged_symlink_file/)
60
63
  end
61
64
  end
62
65
 
@@ -66,7 +69,7 @@ RSpec.describe R10K::Util::Purgeable do
66
69
 
67
70
  it 'collects current_contents that should not exist except excluded items' do
68
71
  expect(subject.logger).to receive(:debug2).with(/unmanaged_2.*internal exclusion match/i)
69
- expect(subject.stale_contents(recurse, exclusions, whitelist)).to contain_exactly(/\/unmanaged_1/)
72
+ expect(subject.stale_contents(recurse, exclusions, whitelist)).to contain_exactly(/\/unmanaged_1/, /\/unmanaged_symlink_file/)
70
73
  end
71
74
  end
72
75
  end
@@ -102,9 +105,13 @@ RSpec.describe R10K::Util::Purgeable do
102
105
  expect(subject.current_contents(recurse)).
103
106
  to contain_exactly(/\/expected_1/, /\/expected_2/,
104
107
  /\/unmanaged_1/, /\/unmanaged_2/,
108
+ /\/managed_symlink_dir/,
109
+ /\/unmanaged_symlink_file/,
105
110
  /\/managed_subdir_1/,
106
111
  /\/subdir_expected_1/, /\/subdir_unmanaged_1/,
107
- /\/managed_subdir_2/, /\/ignored_1/,
112
+ /\/managed_symlink_file/,
113
+ /\/unmanaged_symlink_dir/,
114
+ /\/subdir_allowlisted_2/, /\/ignored_1/,
108
115
  /\/\.hidden/)
109
116
  end
110
117
  end
@@ -122,7 +129,8 @@ RSpec.describe R10K::Util::Purgeable do
122
129
 
123
130
  it 'collects current_contents that should not exist recursively' do
124
131
  expect(subject.stale_contents(recurse, exclusions, whitelist)).
125
- to contain_exactly(/\/unmanaged_1/, /\/unmanaged_2/, /\/subdir_unmanaged_1/, /\/ignored_1/)
132
+ to contain_exactly(/\/unmanaged_1/, /\/unmanaged_2/, /\/unmanaged_symlink_file/, /\/subdir_unmanaged_1/,
133
+ /\/ignored_1/, /\/subdir_allowlisted_2/, /\/unmanaged_symlink_dir/)
126
134
  end
127
135
  end
128
136
 
@@ -134,7 +142,8 @@ RSpec.describe R10K::Util::Purgeable do
134
142
  expect(subject.logger).to receive(:debug).with(/unmanaged_1.*whitelist match/i)
135
143
 
136
144
  expect(subject.stale_contents(recurse, exclusions, whitelist)).
137
- to contain_exactly(/\/unmanaged_2/, /\/subdir_unmanaged_1/, /\/ignored_1/)
145
+ to contain_exactly(/\/unmanaged_2/, /\/subdir_unmanaged_1/, /\/unmanaged_symlink_file/, /\/ignored_1/,
146
+ /\/subdir_allowlisted_2/, /\/unmanaged_symlink_dir/)
138
147
  end
139
148
 
140
149
  it 'does not collect contents that match recursive globbed whitelist items as intermediate values' do
@@ -142,7 +151,7 @@ RSpec.describe R10K::Util::Purgeable do
142
151
  expect(subject.logger).not_to receive(:debug).with(/ignored_1/)
143
152
 
144
153
  expect(subject.stale_contents(recurse, exclusions, recursive_whitelist)).
145
- to contain_exactly(/\/unmanaged_2/, /\/managed_one\/unmanaged_1/)
154
+ to contain_exactly(/\/unmanaged_2/, /\/managed_one\/unmanaged_1/, /\/managed_one\/unmanaged_symlink_file/)
146
155
  end
147
156
  end
148
157
 
@@ -154,7 +163,8 @@ RSpec.describe R10K::Util::Purgeable do
154
163
  expect(subject.logger).to receive(:debug2).with(/unmanaged_2.*internal exclusion match/i)
155
164
 
156
165
  expect(subject.stale_contents(recurse, exclusions, whitelist)).
157
- to contain_exactly(/\/unmanaged_1/, /\/subdir_unmanaged_1/, /\/ignored_1/)
166
+ to contain_exactly(/\/unmanaged_1/, /\/unmanaged_symlink_file/, /\/subdir_unmanaged_1/, /\/ignored_1/,
167
+ /\/subdir_allowlisted_2/, /\/unmanaged_symlink_dir/)
158
168
  end
159
169
 
160
170
  it 'does not collect contents that match recursive globbed exclusion items as intermediate values' do
@@ -162,7 +172,7 @@ RSpec.describe R10K::Util::Purgeable do
162
172
  expect(subject.logger).not_to receive(:debug).with(/ignored_1/)
163
173
 
164
174
  expect(subject.stale_contents(recurse, recursive_exclusions, whitelist)).
165
- to contain_exactly(/\/unmanaged_2/, /\/managed_one\/unmanaged_1/)
175
+ to contain_exactly(/\/unmanaged_2/, /\/unmanaged_symlink_file/, /\/managed_one\/unmanaged_1/)
166
176
  end
167
177
  end
168
178
  end
@@ -199,6 +209,7 @@ RSpec.describe R10K::Util::Purgeable do
199
209
  it 'does not purge items matching glob at root level' do
200
210
  allow(FileUtils).to receive(:rm_r)
201
211
  expect(FileUtils).to_not receive(:rm_r).with(/\/unmanaged_[12]/, anything)
212
+ expect(FileUtils).to_not receive(:rm_r).with(/\/unmanaged_symlink_file/, anything)
202
213
  expect(subject.logger).to receive(:debug).with(/whitelist match/i).at_least(:once)
203
214
 
204
215
  subject.purge!(purge_opts)
@@ -209,7 +220,7 @@ RSpec.describe R10K::Util::Purgeable do
209
220
  context "recursive whitelist glob" do
210
221
  let(:whitelist) do
211
222
  managed_directories.flat_map do |dir|
212
- [File.join(dir, "**", "*unmanaged*"), File.join(dir, "**", "managed_subdir_2")]
223
+ [File.join(dir, "**", "*unmanaged*"), File.join(dir, "**", "subdir_allowlisted_2")]
213
224
  end
214
225
  end
215
226
  let(:purge_opts) { { recurse: true, whitelist: whitelist } }