r10k 3.10.0 → 3.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -10
  3. data/CHANGELOG.mkd +33 -0
  4. data/README.mkd +6 -0
  5. data/doc/dynamic-environments/configuration.mkd +25 -7
  6. data/doc/dynamic-environments/usage.mkd +26 -0
  7. data/doc/puppetfile.mkd +18 -5
  8. data/integration/Rakefile +2 -0
  9. data/integration/tests/basic_functionality/basic_deployment.rb +176 -0
  10. data/integration/tests/user_scenario/basic_workflow/single_env_purge_unmanaged_modules.rb +15 -13
  11. data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +3 -3
  12. data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +3 -3
  13. data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +3 -3
  14. data/lib/r10k/action/base.rb +1 -1
  15. data/lib/r10k/action/deploy/deploy_helpers.rb +4 -0
  16. data/lib/r10k/action/deploy/display.rb +1 -1
  17. data/lib/r10k/action/deploy/environment.rb +19 -9
  18. data/lib/r10k/action/deploy/module.rb +41 -11
  19. data/lib/r10k/action/puppetfile/check.rb +7 -5
  20. data/lib/r10k/action/puppetfile/install.rb +22 -16
  21. data/lib/r10k/action/puppetfile/purge.rb +12 -9
  22. data/lib/r10k/action/runner.rb +40 -4
  23. data/lib/r10k/action/visitor.rb +3 -0
  24. data/lib/r10k/cli/deploy.rb +5 -0
  25. data/lib/r10k/cli/puppetfile.rb +0 -1
  26. data/lib/r10k/content_synchronizer.rb +16 -4
  27. data/lib/r10k/environment/bare.rb +4 -7
  28. data/lib/r10k/environment/base.rb +64 -11
  29. data/lib/r10k/environment/plain.rb +16 -0
  30. data/lib/r10k/environment/with_modules.rb +6 -10
  31. data/lib/r10k/environment.rb +1 -0
  32. data/lib/r10k/errors.rb +5 -0
  33. data/lib/r10k/git/rugged/credentials.rb +77 -0
  34. data/lib/r10k/git/stateful_repository.rb +8 -0
  35. data/lib/r10k/git.rb +3 -0
  36. data/lib/r10k/initializers.rb +14 -7
  37. data/lib/r10k/logging.rb +78 -1
  38. data/lib/r10k/module/base.rb +42 -1
  39. data/lib/r10k/module/definition.rb +64 -0
  40. data/lib/r10k/module/forge.rb +11 -2
  41. data/lib/r10k/module/git.rb +23 -1
  42. data/lib/r10k/module/local.rb +6 -3
  43. data/lib/r10k/module/svn.rb +11 -0
  44. data/lib/r10k/module.rb +20 -2
  45. data/lib/r10k/module_loader/puppetfile/dsl.rb +8 -3
  46. data/lib/r10k/module_loader/puppetfile.rb +109 -28
  47. data/lib/r10k/puppetfile.rb +11 -13
  48. data/lib/r10k/settings/definition.rb +1 -1
  49. data/lib/r10k/settings.rb +89 -1
  50. data/lib/r10k/source/yaml.rb +1 -1
  51. data/lib/r10k/util/purgeable.rb +6 -2
  52. data/lib/r10k/util/setopts.rb +2 -0
  53. data/lib/r10k/util/subprocess.rb +1 -0
  54. data/lib/r10k/version.rb +1 -1
  55. data/locales/r10k.pot +168 -68
  56. data/r10k.gemspec +2 -0
  57. data/r10k.yaml.example +28 -0
  58. data/spec/fixtures/unit/action/r10k_logging.yaml +12 -0
  59. data/spec/fixtures/unit/puppetfile/forge-override/Puppetfile +8 -0
  60. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile +10 -0
  61. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile.new +10 -0
  62. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_symlink_file +1 -0
  63. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/{managed_subdir_2 → subdir_allowlisted_2}/ignored_1 +0 -0
  64. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/unmanaged_symlink_dir +1 -0
  65. data/spec/fixtures/unit/util/purgeable/managed_one/managed_symlink_dir +1 -0
  66. data/spec/fixtures/unit/util/purgeable/managed_one/unmanaged_symlink_file +1 -0
  67. data/spec/integration/util/purageable_spec.rb +41 -0
  68. data/spec/r10k-mocks/mock_env.rb +3 -0
  69. data/spec/r10k-mocks/mock_source.rb +7 -3
  70. data/spec/unit/action/deploy/environment_spec.rb +96 -30
  71. data/spec/unit/action/deploy/module_spec.rb +217 -51
  72. data/spec/unit/action/puppetfile/check_spec.rb +17 -5
  73. data/spec/unit/action/puppetfile/install_spec.rb +42 -36
  74. data/spec/unit/action/puppetfile/purge_spec.rb +15 -17
  75. data/spec/unit/action/runner_spec.rb +132 -9
  76. data/spec/unit/environment/bare_spec.rb +13 -0
  77. data/spec/unit/environment/base_spec.rb +30 -17
  78. data/spec/unit/environment/git_spec.rb +2 -2
  79. data/spec/unit/environment/plain_spec.rb +8 -0
  80. data/spec/unit/environment/svn_spec.rb +4 -3
  81. data/spec/unit/environment/with_modules_spec.rb +3 -2
  82. data/spec/unit/git/rugged/credentials_spec.rb +29 -0
  83. data/spec/unit/git/stateful_repository_spec.rb +5 -0
  84. data/spec/unit/module/base_spec.rb +54 -8
  85. data/spec/unit/module/forge_spec.rb +51 -4
  86. data/spec/unit/module/git_spec.rb +67 -9
  87. data/spec/unit/module/svn_spec.rb +35 -5
  88. data/spec/unit/module_loader/puppetfile_spec.rb +108 -33
  89. data/spec/unit/module_spec.rb +12 -1
  90. data/spec/unit/puppetfile_spec.rb +33 -3
  91. data/spec/unit/settings_spec.rb +43 -2
  92. data/spec/unit/util/purgeable_spec.rb +22 -11
  93. metadata +31 -3
@@ -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/',
@@ -37,20 +37,16 @@ describe R10K::ModuleLoader::Puppetfile do
37
37
  it 'respects absolute paths' do
38
38
  absolute_options = options.merge({puppetfile: '/opt/puppetlabs/special/Puppetfile'})
39
39
  puppetfile = R10K::ModuleLoader::Puppetfile.new(**absolute_options)
40
- expect(puppetfile.instance_variable_get(:@puppetfile)).to eq('/opt/puppetlabs/special/Puppetfile')
40
+ expect(puppetfile.instance_variable_get(:@puppetfile_path)).to eq('/opt/puppetlabs/special/Puppetfile')
41
41
  end
42
42
 
43
43
  it 'roots the Puppetfile in the basepath if a relative path is specified' do
44
44
  relative_options = options.merge({puppetfile: 'Puppetfile.global'})
45
45
  puppetfile = R10K::ModuleLoader::Puppetfile.new(**relative_options)
46
- expect(puppetfile.instance_variable_get(:@puppetfile)).to eq('/test/basedir/env/Puppetfile.global')
46
+ expect(puppetfile.instance_variable_get(:@puppetfile_path)).to eq('/test/basedir/env/Puppetfile.global')
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
@@ -68,11 +64,7 @@ describe R10K::ModuleLoader::Puppetfile do
68
64
  end
69
65
 
70
66
  it 'has a Puppetfile rooted in the basedir' do
71
- expect(subject.instance_variable_get(:@puppetfile)).to eq('/test/basedir/Puppetfile')
72
- end
73
-
74
- it 'uses the public forge' do
75
- expect(subject.instance_variable_get(:@forge)).to eq('forgeapi.puppetlabs.com')
67
+ expect(subject.instance_variable_get(:@puppetfile_path)).to eq('/test/basedir/Puppetfile')
76
68
  end
77
69
 
78
70
  it 'creates an empty overrides' do
@@ -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')
@@ -107,10 +99,22 @@ describe R10K::ModuleLoader::Puppetfile do
107
99
  expect(subject.modules.collect(&:name)).not_to include('test_module')
108
100
  end
109
101
 
102
+ it 'should set :spec_deletable to true for modules in the basedir' do
103
+ module_opts = { git: 'git@example.com:puppet/test_module.git' }
104
+ subject.add_module('puppet/test_module', module_opts)
105
+ expect(subject.modules[0].spec_deletable).to be true
106
+ end
107
+
108
+ it 'should set :spec_deletable to false for modules outside the basedir' do
109
+ module_opts = { git: 'git@example.com:puppet/test_module.git', install_path: 'some/path' }
110
+ subject.add_module('puppet/test_module', module_opts)
111
+ expect(subject.modules[0].spec_deletable).to be false
112
+ end
113
+
110
114
  it 'should accept non-Forge modules with a hash arg' do
111
115
  module_opts = { git: 'git@example.com:puppet/test_module.git' }
112
116
 
113
- 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
114
118
 
115
119
  expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
116
120
  expect(subject.modules.collect(&:name)).to include('test_module')
@@ -122,7 +126,7 @@ describe R10K::ModuleLoader::Puppetfile do
122
126
  git: 'git@example.com:puppet/test_module.git',
123
127
  }
124
128
 
125
- 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
126
130
 
127
131
  expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
128
132
  expect(subject.modules.collect(&:name)).to include('test_module')
@@ -136,7 +140,7 @@ describe R10K::ModuleLoader::Puppetfile do
136
140
  git: 'git@example.com:puppet/test_module.git',
137
141
  }
138
142
 
139
- 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
140
144
 
141
145
  expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
142
146
  expect(subject.modules.collect(&:name)).to include('test_module')
@@ -165,8 +169,9 @@ describe R10K::ModuleLoader::Puppetfile do
165
169
  mod = instance_double('R10K::Module::Base', name: 'conflict', origin: :puppetfile, 'origin=': nil)
166
170
  loader = R10K::ModuleLoader::Puppetfile.new(basedir: basedir, environment: env)
167
171
  allow(env).to receive(:'module_conflicts?').with(mod).and_return(true)
172
+ allow(mod).to receive(:spec_deletable=)
168
173
 
169
- expect(R10K::Module).to receive(:new).with('conflict', anything, anything, anything).and_return(mod)
174
+ expect(R10K::Module).to receive(:from_metadata).with('conflict', anything, anything, anything).and_return(mod)
170
175
  expect { loader.add_module('conflict', {}) }.not_to change { loader.modules }
171
176
  end
172
177
  end
@@ -201,9 +206,9 @@ describe R10K::ModuleLoader::Puppetfile do
201
206
  end
202
207
 
203
208
  it 'returns an array of paths that #purge! will operate within' do
204
- expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
209
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
205
210
  subject.add_module('puppet/test_module', '1.2.3')
206
- subject.load
211
+ subject.load!
207
212
 
208
213
  expect(subject.modules.length).to be 1
209
214
  expect(subject.managed_directories).to match_array([subject.moduledir])
@@ -213,9 +218,9 @@ describe R10K::ModuleLoader::Puppetfile do
213
218
  it "basedir isn't in the list of paths to purge" do
214
219
  module_opts = { install_path: '', git: 'git@example.com:puppet/test_module.git' }
215
220
 
216
- expect(R10K::Module).to receive(:new).with('puppet/test_module', basedir, module_opts, anything).and_call_original
221
+ expect(R10K::Module).to receive(:from_metadata).with('puppet/test_module', basedir, module_opts, anything).and_call_original
217
222
  subject.add_module('puppet/test_module', module_opts)
218
- subject.load
223
+ subject.load!
219
224
 
220
225
  expect(subject.modules.length).to be 1
221
226
  expect(subject.managed_directories).to be_empty
@@ -236,7 +241,7 @@ describe R10K::ModuleLoader::Puppetfile do
236
241
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'invalid-syntax')
237
242
  pf_path = File.join(@path, 'Puppetfile')
238
243
  expect {
239
- subject.load
244
+ subject.load!
240
245
  }.to raise_error do |e|
241
246
  expect_wrapped_error(e, pf_path, SyntaxError)
242
247
  end
@@ -246,7 +251,7 @@ describe R10K::ModuleLoader::Puppetfile do
246
251
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'load-error')
247
252
  pf_path = File.join(@path, 'Puppetfile')
248
253
  expect {
249
- subject.load
254
+ subject.load!
250
255
  }.to raise_error do |e|
251
256
  expect_wrapped_error(e, pf_path, LoadError)
252
257
  end
@@ -256,17 +261,38 @@ describe R10K::ModuleLoader::Puppetfile do
256
261
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'argument-error')
257
262
  pf_path = File.join(@path, 'Puppetfile')
258
263
  expect {
259
- subject.load
264
+ subject.load!
260
265
  }.to raise_error do |e|
261
266
  expect_wrapped_error(e, pf_path, ArgumentError)
262
267
  end
263
268
  end
264
269
 
270
+ describe 'forge declaration' do
271
+ before(:each) do
272
+ PuppetForge.host = ""
273
+ end
274
+
275
+ it 'is respected if `allow_puppetfile_override` is true' do
276
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'forge-override')
277
+ puppetfile = R10K::ModuleLoader::Puppetfile.new(basedir: @path, overrides: { forge: { allow_puppetfile_override: true } })
278
+ puppetfile.load!
279
+ expect(PuppetForge.host).to eq("my.custom.forge.com/")
280
+ end
281
+
282
+ it 'is ignored if `allow_puppetfile_override` is false' do
283
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'forge-override')
284
+ puppetfile = R10K::ModuleLoader::Puppetfile.new(basedir: @path, overrides: { forge: { allow_puppetfile_override: false } })
285
+ expect(PuppetForge).not_to receive(:host=).with("my.custom.forge.com")
286
+ puppetfile.load!
287
+ expect(PuppetForge.host).to eq("/")
288
+ end
289
+ end
290
+
265
291
  it 'rejects Puppetfiles with duplicate module names' do
266
292
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'duplicate-module-error')
267
293
  pf_path = File.join(@path, 'Puppetfile')
268
294
  expect {
269
- subject.load
295
+ subject.load!
270
296
  }.to raise_error(R10K::Error, /Puppetfiles cannot contain duplicate module names/i)
271
297
  end
272
298
 
@@ -274,7 +300,7 @@ describe R10K::ModuleLoader::Puppetfile do
274
300
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'name-error')
275
301
  pf_path = File.join(@path, 'Puppetfile')
276
302
  expect {
277
- subject.load
303
+ subject.load!
278
304
  }.to raise_error do |e|
279
305
  expect_wrapped_error(e, pf_path, NameError)
280
306
  end
@@ -283,21 +309,21 @@ describe R10K::ModuleLoader::Puppetfile do
283
309
  it 'accepts a forge module with a version' do
284
310
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
285
311
  pf_path = File.join(@path, 'Puppetfile')
286
- expect { subject.load }.not_to raise_error
312
+ expect { subject.load! }.not_to raise_error
287
313
  end
288
314
 
289
315
  describe 'setting a custom moduledir' do
290
316
  it 'allows setting an absolute moduledir' do
291
317
  @path = '/fake/basedir'
292
318
  allow(subject).to receive(:puppetfile_content).and_return('moduledir "/fake/moduledir"')
293
- subject.load
319
+ subject.load!
294
320
  expect(subject.instance_variable_get(:@moduledir)).to eq('/fake/moduledir')
295
321
  end
296
322
 
297
323
  it 'roots relative moduledirs in the basedir' do
298
324
  @path = '/fake/basedir'
299
325
  allow(subject).to receive(:puppetfile_content).and_return('moduledir "my/moduledir"')
300
- subject.load
326
+ subject.load!
301
327
  expect(subject.instance_variable_get(:@moduledir)).to eq(File.join(@path, 'my/moduledir'))
302
328
  end
303
329
  end
@@ -305,13 +331,13 @@ describe R10K::ModuleLoader::Puppetfile do
305
331
  it 'accepts a forge module without a version' do
306
332
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-without-version')
307
333
  pf_path = File.join(@path, 'Puppetfile')
308
- expect { subject.load }.not_to raise_error
334
+ expect { subject.load! }.not_to raise_error
309
335
  end
310
336
 
311
337
  it 'creates a git module and applies the default branch specified in the Puppetfile' do
312
338
  @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'default-branch-override')
313
339
  pf_path = File.join(@path, 'Puppetfile')
314
- expect { subject.load }.not_to raise_error
340
+ expect { subject.load! }.not_to raise_error
315
341
  git_module = subject.modules[0]
316
342
  expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
317
343
  end
@@ -321,10 +347,59 @@ describe R10K::ModuleLoader::Puppetfile do
321
347
  pf_path = File.join(@path, 'Puppetfile')
322
348
  default_branch_override = 'default_branch_override_name'
323
349
  subject.default_branch_override = default_branch_override
324
- expect { subject.load }.not_to raise_error
350
+ expect { subject.load! }.not_to raise_error
325
351
  git_module = subject.modules[0]
326
352
  expect(git_module.default_override_ref).to eq default_branch_override
327
353
  expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
328
354
  end
355
+
356
+ describe 'using module metadata' do
357
+ it 'properly loads module metadata' do
358
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'various-modules')
359
+ metadata = subject.load_metadata[:modules].map { |mod| [ mod.name, mod.version ] }.to_h
360
+ expect(metadata['apt']).to eq('2.1.1')
361
+ expect(metadata['stdlib']).to eq(nil)
362
+ expect(metadata['concat']).to eq(nil)
363
+ expect(metadata['rpm']).to eq('2.1.1-pre1')
364
+ expect(metadata['foo']).to eq(nil)
365
+ expect(metadata['bar']).to eq('v1.2.3')
366
+ expect(metadata['baz']).to eq('123abc456')
367
+ expect(metadata['fizz']).to eq('1234567890abcdef1234567890abcdef12345678')
368
+ expect(metadata['buzz']).to eq(nil)
369
+ expect(metadata['canary']).to eq('0.0.0')
370
+ end
371
+
372
+ it 'does not load module implementations for static versioned' do
373
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'various-modules')
374
+ subject.load_metadata
375
+ modules = subject.load[:modules].map { |mod| [ mod.name, mod ] }.to_h
376
+ expect(modules['apt']).to be_a_kind_of(R10K::Module::Definition)
377
+ expect(modules['stdlib']).to be_a_kind_of(R10K::Module::Forge)
378
+ expect(modules['concat']).to be_a_kind_of(R10K::Module::Forge)
379
+ expect(modules['rpm']).to be_a_kind_of(R10K::Module::Definition)
380
+ expect(modules['foo']).to be_a_kind_of(R10K::Module::Git)
381
+ expect(modules['bar']).to be_a_kind_of(R10K::Module::Definition)
382
+ expect(modules['baz']).to be_a_kind_of(R10K::Module::Definition)
383
+ expect(modules['fizz']).to be_a_kind_of(R10K::Module::Definition)
384
+ expect(modules['buzz']).to be_a_kind_of(R10K::Module::Git)
385
+ expect(modules['canary']).to be_a_kind_of(R10K::Module::Definition)
386
+ end
387
+
388
+ it 'loads module implementations whose static versions are different' do
389
+ fixture_path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'various-modules')
390
+ @path = Dir.mktmpdir
391
+ unsynced_pf_path = File.join(fixture_path, 'Puppetfile')
392
+ FileUtils.cp(unsynced_pf_path, @path)
393
+
394
+ subject.load_metadata
395
+
396
+ synced_pf_path = File.join(fixture_path, 'Puppetfile.new')
397
+ FileUtils.cp(synced_pf_path, File.join(@path, 'Puppetfile'))
398
+
399
+ modules = subject.load[:modules].map { |mod| [ mod.name, mod ] }.to_h
400
+
401
+ expect(modules['apt']).to be_a_kind_of(R10K::Module::Forge)
402
+ end
403
+ end
329
404
  end
330
405
  end
@@ -77,7 +77,7 @@ describe R10K::Module do
77
77
  }.to raise_error RuntimeError, /doesn't have an implementation/
78
78
  end
79
79
 
80
- describe 'when a user passes a `default_branch_override`' do
80
+ describe 'Given a set of initialization parameters for R10K::Module' do
81
81
  [ ['name', {git: 'git url'}],
82
82
  ['name', {type: 'git', source: 'git url'}],
83
83
  ['name', {svn: 'svn url'}],
@@ -91,6 +91,17 @@ describe R10K::Module do
91
91
  expect(obj).to be_a_kind_of(R10K::Module::Base)
92
92
  }.not_to raise_error
93
93
  end
94
+ describe 'the exclude_spec setting' do
95
+ it 'sets the exclude_spec instance variable' do
96
+ obj = R10K::Module.new(name, '/modulepath', options.merge({exclude_spec: true}))
97
+ expect(obj.instance_variable_get("@exclude_spec")).to eq(true)
98
+ end
99
+ it 'can be overridden by the overrides map' do
100
+ options = options.merge({exclude_spec: false, overrides: {modules: {exclude_spec: true}}})
101
+ obj = R10K::Module.new(name, '/modulepath', options)
102
+ expect(obj.instance_variable_get("@exclude_spec")).to eq(true)
103
+ end
104
+ end
94
105
  end
95
106
  end
96
107
  end
@@ -10,7 +10,7 @@ describe R10K::Puppetfile do
10
10
  )
11
11
  end
12
12
 
13
- describe "a custom puppetfile Puppetfile.r10k" do
13
+ describe "a custom puppetfile_name" do
14
14
  it "is the basedir joined with '/Puppetfile.r10k' path" do
15
15
  expect(subject.puppetfile_path).to eq '/some/nonexistent/basedir/Puppetfile.r10k'
16
16
  end
@@ -18,6 +18,25 @@ describe R10K::Puppetfile do
18
18
 
19
19
  end
20
20
 
21
+ describe R10K::Puppetfile do
22
+
23
+ describe "a custom relative puppetfile_path" do
24
+ it "is the basedir joined with the puppetfile_path" do
25
+ relative_subject = described_class.new('/some/nonexistent/basedir',
26
+ {puppetfile_path: 'relative/Puppetfile'})
27
+ expect(relative_subject.puppetfile_path).to eq '/some/nonexistent/basedir/relative/Puppetfile'
28
+ end
29
+ end
30
+
31
+ describe "a custom absolute puppetfile_path" do
32
+ it "is the puppetfile_path as given" do
33
+ absolute_subject = described_class.new('/some/nonexistent/basedir',
34
+ {puppetfile_path: '/some/absolute/custom/Puppetfile'})
35
+ expect(absolute_subject.puppetfile_path).to eq '/some/absolute/custom/Puppetfile'
36
+ end
37
+ end
38
+ end
39
+
21
40
  describe R10K::Puppetfile do
22
41
 
23
42
  subject do
@@ -80,11 +99,22 @@ describe R10K::Puppetfile do
80
99
  expect(has_some_data).to be true
81
100
  end
82
101
 
102
+ it "handles a relative basedir" do
103
+ path = File.join('spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
104
+ subject = described_class.new(path, {})
105
+
106
+ loaded_content = subject.load
107
+ expect(loaded_content).to be_an_instance_of(Hash)
108
+
109
+ has_some_data = loaded_content.values.none?(&:empty?)
110
+ expect(has_some_data).to be true
111
+ end
112
+
83
113
  it "is idempotent" do
84
114
  path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
85
115
  subject = described_class.new(path, {})
86
116
 
87
- expect(subject.loader).to receive(:load).and_call_original.once
117
+ expect(subject.loader).to receive(:load!).and_call_original.once
88
118
 
89
119
  loaded_content1 = subject.load
90
120
  expect(subject.loaded?).to be true
@@ -243,7 +273,7 @@ describe R10K::Puppetfile do
243
273
  expect(subject).to receive(:modules).and_return([mod1, mod2])
244
274
 
245
275
  expect(Thread).to receive(:new).exactly(pool_size).and_call_original
246
- expect(Queue).to receive(:new).and_call_original
276
+ expect(Queue).to receive(:new).and_call_original.twice
247
277
 
248
278
  subject.accept(visitor)
249
279
  end
@@ -90,11 +90,50 @@ 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
96
117
  subject { described_class.deploy_settings }
97
118
 
119
+ describe 'exclude_spec' do
120
+ it 'is false by default' do
121
+ expect(subject.evaluate({})[:exclude_spec]).to eq(false)
122
+ end
123
+ it 'can be set to true' do
124
+ expect(subject.evaluate({"exclude_spec" => true})[:exclude_spec]).to eq(true)
125
+ end
126
+ it "raises an error for non-boolean values" do
127
+ expect {
128
+ subject.evaluate({"exclude_spec" => 'invalid_string'})
129
+ }.to raise_error do |err|
130
+ expect(err.message).to match(/Validation failed for 'deploy' settings group/)
131
+ expect(err.errors.size).to eq 1
132
+ expect(err.errors[:exclude_spec]).to be_a_kind_of(ArgumentError)
133
+ expect(err.errors[:exclude_spec].message).to match(/`exclude_spec` can only be a boolean value, not 'invalid_string'/)
134
+ end
135
+ end
136
+ end
98
137
  describe "write_lock" do
99
138
  it "accepts a string with a reason for the write lock" do
100
139
  output = subject.evaluate("write_lock" => "No maintenance window active, code freeze till 2038-01-19")
@@ -252,10 +291,12 @@ describe R10K::Settings do
252
291
  it "passes settings through to the forge settings" do
253
292
  output = subject.evaluate("forge" => {"baseurl" => "https://forge.tessier-ashpool.freeside",
254
293
  "proxy" => "https://proxy.tessier-ashpool.freesize:3128",
255
- "authorization_token" => "faketoken"})
294
+ "authorization_token" => "faketoken",
295
+ "allow_puppetfile_override" => true})
256
296
  expect(output[:forge]).to eq(:baseurl => "https://forge.tessier-ashpool.freeside",
257
297
  :proxy => "https://proxy.tessier-ashpool.freesize:3128",
258
- :authorization_token => "faketoken")
298
+ :authorization_token => "faketoken",
299
+ :allow_puppetfile_override => true)
259
300
  end
260
301
  end
261
302
  end
@@ -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 } }