r10k 3.9.3 → 3.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec_tests.yml +1 -1
  3. data/.travis.yml +0 -10
  4. data/CHANGELOG.mkd +33 -0
  5. data/README.mkd +6 -0
  6. data/doc/dynamic-environments/configuration.mkd +25 -0
  7. data/doc/dynamic-environments/usage.mkd +26 -0
  8. data/doc/puppetfile.mkd +18 -5
  9. data/integration/Rakefile +3 -1
  10. data/integration/tests/basic_functionality/basic_deployment.rb +176 -0
  11. data/integration/tests/user_scenario/basic_workflow/negative/neg_specify_deleted_forge_module.rb +3 -9
  12. data/integration/tests/user_scenario/basic_workflow/single_env_purge_unmanaged_modules.rb +21 -25
  13. data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +3 -3
  14. data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +3 -3
  15. data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +3 -3
  16. data/lib/r10k/action/base.rb +1 -1
  17. data/lib/r10k/action/deploy/deploy_helpers.rb +4 -0
  18. data/lib/r10k/action/deploy/display.rb +1 -1
  19. data/lib/r10k/action/deploy/environment.rb +22 -9
  20. data/lib/r10k/action/deploy/module.rb +41 -11
  21. data/lib/r10k/action/puppetfile/check.rb +7 -5
  22. data/lib/r10k/action/puppetfile/install.rb +22 -16
  23. data/lib/r10k/action/puppetfile/purge.rb +12 -9
  24. data/lib/r10k/action/runner.rb +45 -10
  25. data/lib/r10k/action/visitor.rb +3 -0
  26. data/lib/r10k/cli/deploy.rb +5 -0
  27. data/lib/r10k/cli/puppetfile.rb +0 -1
  28. data/lib/r10k/content_synchronizer.rb +16 -4
  29. data/lib/r10k/environment/base.rb +64 -11
  30. data/lib/r10k/environment/with_modules.rb +6 -10
  31. data/lib/r10k/git/cache.rb +1 -1
  32. data/lib/r10k/git/rugged/credentials.rb +77 -0
  33. data/lib/r10k/git/stateful_repository.rb +8 -0
  34. data/lib/r10k/git.rb +3 -0
  35. data/lib/r10k/initializers.rb +4 -0
  36. data/lib/r10k/module/base.rb +42 -1
  37. data/lib/r10k/module/definition.rb +64 -0
  38. data/lib/r10k/module/forge.rb +16 -3
  39. data/lib/r10k/module/git.rb +23 -1
  40. data/lib/r10k/module/local.rb +6 -3
  41. data/lib/r10k/module/svn.rb +11 -0
  42. data/lib/r10k/module.rb +20 -2
  43. data/lib/r10k/module_loader/puppetfile/dsl.rb +42 -0
  44. data/lib/r10k/module_loader/puppetfile.rb +276 -0
  45. data/lib/r10k/puppetfile.rb +82 -160
  46. data/lib/r10k/settings/definition.rb +1 -1
  47. data/lib/r10k/settings.rb +58 -2
  48. data/lib/r10k/source/base.rb +10 -0
  49. data/lib/r10k/source/git.rb +5 -0
  50. data/lib/r10k/source/svn.rb +4 -0
  51. data/lib/r10k/util/purgeable.rb +74 -8
  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 +165 -65
  56. data/r10k.gemspec +2 -0
  57. data/spec/fixtures/unit/action/r10k_forge_auth.yaml +4 -0
  58. data/spec/fixtures/unit/action/r10k_forge_auth_no_url.yaml +3 -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_subdir_2/ignored_1 +0 -0
  63. data/spec/fixtures/unit/util/purgeable/managed_two/.hidden/unmanaged_3 +0 -0
  64. data/spec/r10k-mocks/mock_env.rb +3 -0
  65. data/spec/r10k-mocks/mock_source.rb +7 -3
  66. data/spec/unit/action/deploy/environment_spec.rb +105 -30
  67. data/spec/unit/action/deploy/module_spec.rb +232 -42
  68. data/spec/unit/action/puppetfile/check_spec.rb +17 -5
  69. data/spec/unit/action/puppetfile/install_spec.rb +42 -36
  70. data/spec/unit/action/puppetfile/purge_spec.rb +15 -17
  71. data/spec/unit/action/runner_spec.rb +122 -26
  72. data/spec/unit/environment/base_spec.rb +30 -17
  73. data/spec/unit/environment/git_spec.rb +2 -2
  74. data/spec/unit/environment/svn_spec.rb +4 -3
  75. data/spec/unit/environment/with_modules_spec.rb +2 -1
  76. data/spec/unit/git/cache_spec.rb +14 -0
  77. data/spec/unit/git/rugged/credentials_spec.rb +29 -0
  78. data/spec/unit/git/stateful_repository_spec.rb +5 -0
  79. data/spec/unit/module/base_spec.rb +54 -8
  80. data/spec/unit/module/forge_spec.rb +59 -5
  81. data/spec/unit/module/git_spec.rb +67 -9
  82. data/spec/unit/module/svn_spec.rb +35 -5
  83. data/spec/unit/module_loader/puppetfile_spec.rb +405 -0
  84. data/spec/unit/module_spec.rb +12 -1
  85. data/spec/unit/puppetfile_spec.rb +125 -189
  86. data/spec/unit/settings_spec.rb +47 -2
  87. data/spec/unit/util/purgeable_spec.rb +38 -6
  88. metadata +29 -3
@@ -47,6 +47,26 @@ describe R10K::Action::Deploy::Environment do
47
47
  described_class.new({ 'oauth-token': '/nonexistent' }, [], {})
48
48
  end
49
49
 
50
+ it 'can accept an app id option' do
51
+ described_class.new({ 'github-app-id': '/nonexistent' }, [], {})
52
+ end
53
+
54
+ it 'can accept a ttl option' do
55
+ described_class.new({ 'github-app-ttl': '/nonexistent' }, [], {})
56
+ end
57
+
58
+ it 'can accept a ssl private key option' do
59
+ described_class.new({ 'github-app-key': '/nonexistent' }, [], {})
60
+ end
61
+
62
+ it 'can accept a exclude-spec option' do
63
+ described_class.new({ :'exclude-spec' => true }, [], {})
64
+ end
65
+
66
+ it 'can accept an incremental option' do
67
+ described_class.new({ :incremental => true }, [], {})
68
+ end
69
+
50
70
  describe "initializing errors" do
51
71
  let (:settings) { { deploy: { purge_levels: [:environment],
52
72
  purge_whitelist: ['coolfile', 'coolfile2'],
@@ -75,27 +95,70 @@ describe R10K::Action::Deploy::Environment do
75
95
 
76
96
  describe "with puppetfile or modules flag" do
77
97
  let(:deployment) { R10K::Deployment.new(mock_config) }
78
- let(:puppetfile) { instance_double("R10K::Puppetfile", modules: []).as_null_object }
98
+ let(:loader) do
99
+ instance_double("R10K::ModuleLoader::Puppetfile",
100
+ :load => {
101
+ :modules => ['foo'],
102
+ :purge_exclusions => [],
103
+ :managed_directories => [],
104
+ :desired_contents => []
105
+ }
106
+ ).as_null_object
107
+ end
79
108
 
80
109
  before do
81
110
  expect(R10K::Deployment).to receive(:new).and_return(deployment)
82
- expect(R10K::Puppetfile).to receive(:new).and_return(puppetfile).at_least(:once)
111
+ expect(R10K::ModuleLoader::Puppetfile).to receive(:new).
112
+ and_return(loader).at_least(:once)
83
113
  end
84
114
 
85
- it "syncs the puppetfile when given the puppetfile flag" do
86
- expect(puppetfile).to receive(:sync)
115
+ it "syncs the puppetfile content when given the puppetfile flag" do
116
+ expect(loader).to receive(:load).exactly(4).times
117
+ expect(R10K::ContentSynchronizer).to receive(:concurrent_sync).exactly(4).times
87
118
  action = described_class.new({config: "/some/nonexistent/path", puppetfile: true}, [], {})
88
119
  action.call
89
120
  end
90
121
 
91
122
  it "syncs the puppetfile when given the modules flag" do
92
- expect(puppetfile).to receive(:sync)
123
+ expect(loader).to receive(:load).exactly(4).times
124
+ expect(R10K::ContentSynchronizer).to receive(:concurrent_sync).exactly(4).times
93
125
  action = described_class.new({config: "/some/nonexistent/path", modules: true}, [], {})
94
126
  action.call
95
127
  end
128
+ end
129
+
130
+ describe "with incremental flag" do
131
+ let(:loader) do
132
+ instance_double("R10K::ModuleLoader::Puppetfile",
133
+ :load => {
134
+ :modules => ['foo'],
135
+ :purge_exclusions => [],
136
+ :managed_directories => [],
137
+ :desired_contents => []
138
+ }
139
+ ).as_null_object
140
+ end
96
141
 
142
+ before do
143
+ expect(R10K::Deployment).to receive(:new).and_wrap_original do |original, settings|
144
+ original.call(mock_config.merge(settings))
145
+ end
146
+ expect(R10K::ModuleLoader::Puppetfile).to receive(:new).
147
+ and_return(loader).at_least(:once)
148
+ end
149
+
150
+ it "incremental flag causes the module definitons to be preloaded by the loader" do
151
+ expect(loader).to receive(:load_metadata).exactly(4).times
152
+ action = described_class.new({:config => "/some/nonexistent/path",
153
+ :modules => true,
154
+ :incremental => true},
155
+ [],
156
+ {})
157
+ action.call
158
+ end
97
159
  end
98
160
 
161
+
99
162
  describe "with an environment that doesn't exist" do
100
163
  let(:deployment) do
101
164
  R10K::Deployment.new(mock_config)
@@ -208,19 +271,20 @@ describe R10K::Action::Deploy::Environment do
208
271
 
209
272
  describe "Purging white/allowlist" do
210
273
 
211
- let(:settings) { { deploy: { purge_levels: [:environment], purge_allowlist: ['coolfile', 'coolfile2'] } } }
212
- let(:overrides) { { environments: {}, modules: {}, purging: { purge_levels: [:environment], purge_allowlist: ['coolfile', 'coolfile2'] } } }
274
+ let(:settings) { { pool_size: 4, deploy: { purge_levels: [:environment], purge_allowlist: ['coolfile', 'coolfile2'] } } }
275
+ let(:overrides) { { environments: {}, modules: { pool_size: 4 }, purging: { purge_levels: [:environment], purge_allowlist: ['coolfile', 'coolfile2'] } } }
213
276
  let(:deployment) do
214
- R10K::Deployment.new(mock_config.merge(overrides))
277
+ R10K::Deployment.new(mock_config.merge({overrides: overrides}))
215
278
  end
216
279
  before do
217
280
  expect(R10K::Deployment).to receive(:new).and_return(deployment)
281
+ allow_any_instance_of(R10K::Environment::Base).to receive(:purge!)
218
282
  end
219
283
 
220
284
  subject { described_class.new({ config: "/some/nonexistent/path", modules: true }, %w[PREFIX_first], settings) }
221
285
 
222
286
  it "reads in the purge_allowlist setting and purges accordingly" do
223
- expect(subject.logger).to receive(:debug).with(/purging unmanaged content for environment/i)
287
+ expect(subject.logger).to receive(:debug).with(/Purging unmanaged content for environment/)
224
288
  expect(subject.settings[:overrides][:purging][:purge_allowlist]).to eq(['coolfile', 'coolfile2'])
225
289
  subject.call
226
290
  end
@@ -229,7 +293,7 @@ describe R10K::Action::Deploy::Environment do
229
293
  let (:settings) { { deploy: { purge_levels: [:environment], purge_whitelist: ['coolfile', 'coolfile2'] } } }
230
294
 
231
295
  it "reads in the purge_whitelist setting and still sets it to purge_allowlist and purges accordingly" do
232
- expect(subject.logger).to receive(:debug).with(/purging unmanaged content for environment/i)
296
+ expect(subject.logger).to receive(:debug).with(/Purging unmanaged content for environment/)
233
297
  expect(subject.settings[:overrides][:purging][:purge_allowlist]).to eq(['coolfile', 'coolfile2'])
234
298
  subject.call
235
299
  end
@@ -244,7 +308,8 @@ describe R10K::Action::Deploy::Environment do
244
308
  requested_environments: ['PREFIX_first']
245
309
  },
246
310
  modules: {
247
- deploy_modules: true
311
+ deploy_modules: true,
312
+ pool_size: 4
248
313
  },
249
314
  purging: {
250
315
  purge_levels: purge_levels
@@ -258,6 +323,7 @@ describe R10K::Action::Deploy::Environment do
258
323
 
259
324
  before do
260
325
  expect(R10K::Deployment).to receive(:new).and_return(deployment)
326
+ allow_any_instance_of(R10K::Environment::Base).to receive(:purge!)
261
327
  end
262
328
 
263
329
  subject { described_class.new({ config: "/some/nonexistent/path", modules: true }, %w[PREFIX_first], settings) }
@@ -265,14 +331,23 @@ describe R10K::Action::Deploy::Environment do
265
331
  describe "deployment purge level" do
266
332
  let(:purge_levels) { [:deployment] }
267
333
 
334
+
335
+ it "updates the source's cache before it purges environments" do
336
+ deployment.sources.each do |source|
337
+ expect(source).to receive(:reload!).ordered
338
+ end
339
+ expect(deployment).to receive(:purge!).ordered
340
+ subject.call
341
+ end
342
+
268
343
  it "only logs about purging deployment" do
269
344
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, env, &block|
270
- expect(env.logger).to_not receive(:debug).with(/purging unmanaged puppetfile content/i)
345
+ expect(env.logger).to_not receive(:debug).with(/Purging unmanaged puppetfile content/)
271
346
  original.call(env)
272
347
  end.at_least(:once)
273
348
 
274
- expect(subject.logger).to receive(:debug).with(/purging unmanaged environments for deployment/i)
275
- expect(subject.logger).to_not receive(:debug).with(/purging unmanaged content for environment/i)
349
+ expect(subject.logger).to receive(:debug).with(/Purging unmanaged environments for deployment/)
350
+ expect(subject.logger).to_not receive(:debug).with(/Purging unmanaged content for environment/)
276
351
 
277
352
  subject.call
278
353
  end
@@ -283,11 +358,11 @@ describe R10K::Action::Deploy::Environment do
283
358
 
284
359
  it "only logs about purging environment" do
285
360
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, env, &block|
286
- expect(env.logger).to_not receive(:debug).with(/purging unmanaged puppetfile content/i)
361
+ expect(env.logger).to_not receive(:debug).with(/Purging unmanaged puppetfile content/)
287
362
  original.call(env)
288
363
  end.at_least(:once)
289
- expect(subject.logger).to receive(:debug).with(/purging unmanaged content for environment/i)
290
- expect(subject.logger).to_not receive(:debug).with(/purging unmanaged environments for deployment/i)
364
+ expect(subject.logger).to receive(:debug).with(/Purging unmanaged content for environment/)
365
+ expect(subject.logger).to_not receive(:debug).with(/Purging unmanaged environments for deployment/)
291
366
 
292
367
  subject.call
293
368
  end
@@ -300,7 +375,7 @@ describe R10K::Action::Deploy::Environment do
300
375
  original.call(env)
301
376
  end.at_least(:once)
302
377
 
303
- expect(subject.logger).to receive(:debug).with(/not purging unmanaged content for environment/i)
378
+ expect(subject.logger).to receive(:debug).with(/Not purging unmanaged content for environment/)
304
379
 
305
380
  subject.call
306
381
  end
@@ -310,15 +385,16 @@ describe R10K::Action::Deploy::Environment do
310
385
  let(:purge_levels) { [:puppetfile] }
311
386
 
312
387
  it "only logs about purging puppetfile" do
388
+ allow(R10K::ContentSynchronizer).to receive(:concurrent_sync)
313
389
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, env, &block|
314
390
  if env.name =~ /first/
315
- expect(env.logger).to receive(:debug).with(/purging unmanaged puppetfile content/i)
391
+ expect(env.logger).to receive(:debug).with(/Purging unmanaged Puppetfile content/)
316
392
  end
317
393
  original.call(env)
318
394
  end.at_least(:once)
319
395
 
320
- expect(subject.logger).to_not receive(:debug).with(/purging unmanaged environments for deployment/i)
321
- expect(subject.logger).to_not receive(:debug).with(/purging unmanaged content for environment/i)
396
+ expect(subject.logger).to_not receive(:debug).with(/Purging unmanaged environments for deployment/)
397
+ expect(subject.logger).to_not receive(:debug).with(/Purging unmanaged content for environment/)
322
398
 
323
399
  subject.call
324
400
  end
@@ -335,6 +411,11 @@ describe R10K::Action::Deploy::Environment do
335
411
  basedir: '/some/nonexistent/path/control',
336
412
  environments: %w[first second]
337
413
  }
414
+ },
415
+ overrides: {
416
+ modules: {
417
+ pool_size: 4
418
+ }
338
419
  }
339
420
  )
340
421
  )
@@ -342,9 +423,8 @@ describe R10K::Action::Deploy::Environment do
342
423
 
343
424
  before do
344
425
  allow(R10K::Deployment).to receive(:new).and_return(deployment)
345
- end
426
+ allow_any_instance_of(R10K::Environment::Base).to receive(:purge!)
346
427
 
347
- before(:each) do
348
428
  allow(subject).to receive(:write_environment_info!)
349
429
  expect(subject.logger).not_to receive(:error)
350
430
  end
@@ -506,7 +586,6 @@ describe R10K::Action::Deploy::Environment do
506
586
  })
507
587
  end
508
588
  let(:mock_forge_module_1) { double(:name => "their_shiny_module", :properties => { :expected => "2.0.0" }) }
509
- let(:mock_puppetfile) { instance_double("R10K::Puppetfile", :modules => [mock_git_module_1, mock_git_module_2, mock_forge_module_1]) }
510
589
 
511
590
  before(:all) do
512
591
  @tmp_path = "./tmp-r10k-test-dir/"
@@ -519,10 +598,8 @@ describe R10K::Action::Deploy::Environment do
519
598
  end
520
599
 
521
600
  it "writes the .r10k-deploy file correctly if all goes well" do
522
- allow(R10K::Puppetfile).to receive(:new).and_return(mock_puppetfile)
523
-
524
601
  fake_env = Fake_Environment.new(@tmp_path, {:name => "my_cool_environment", :signature => "pablo picasso"})
525
- allow(fake_env).to receive(:modules).and_return(mock_puppetfile.modules)
602
+ allow(fake_env).to receive(:modules).and_return([mock_git_module_1, mock_git_module_2, mock_forge_module_1])
526
603
  subject.send(:write_environment_info!, fake_env, "2019-01-01 23:23:22 +0000", true)
527
604
 
528
605
  file_contents = File.read("#{@tmp_path}/.r10k-deploy.json")
@@ -545,10 +622,8 @@ describe R10K::Action::Deploy::Environment do
545
622
  end
546
623
 
547
624
  it "writes the .r10k-deploy file correctly if there's a failure" do
548
- allow(R10K::Puppetfile).to receive(:new).and_return(mock_puppetfile)
549
-
550
625
  fake_env = Fake_Environment.new(@tmp_path, {:name => "my_cool_environment", :signature => "pablo picasso"})
551
- allow(fake_env).to receive(:modules).and_return(mock_puppetfile.modules)
626
+ allow(fake_env).to receive(:modules).and_return([mock_git_module_1, mock_git_module_2, mock_forge_module_1])
552
627
  allow(mock_forge_module_1).to receive(:properties).and_raise(StandardError)
553
628
  subject.send(:write_environment_info!, fake_env, "2019-01-01 23:23:22 +0000", true)
554
629
 
@@ -41,6 +41,22 @@ describe R10K::Action::Deploy::Module do
41
41
  it 'can accept a token option' do
42
42
  described_class.new({ 'oauth-token': '/nonexistent' }, [], {})
43
43
  end
44
+
45
+ it 'can accept an app id option' do
46
+ described_class.new({ 'github-app-id': '/nonexistent' }, [], {})
47
+ end
48
+
49
+ it 'can accept a ttl option' do
50
+ described_class.new({ 'github-app-ttl': '/nonexistent' }, [], {})
51
+ end
52
+
53
+ it 'can accept a ssl private key option' do
54
+ described_class.new({ 'github-app-key': '/nonexistent' }, [], {})
55
+ end
56
+
57
+ it 'can accept a exclude-spec option' do
58
+ described_class.new({ :'exclude-spec' => true }, [], {})
59
+ end
44
60
  end
45
61
 
46
62
  describe "with no-force" do
@@ -61,6 +77,11 @@ describe R10K::Action::Deploy::Module do
61
77
  basedir: '/some/nonexistent/path/control',
62
78
  environments: %w[first second]
63
79
  }
80
+ },
81
+ overrides: {
82
+ modules: {
83
+ pool_size: 4
84
+ }
64
85
  }
65
86
  )
66
87
  end
@@ -81,28 +102,22 @@ describe R10K::Action::Deploy::Module do
81
102
  )
82
103
  end
83
104
 
84
- before do
85
- @modules = []
105
+ it 'generate_types is true' do
106
+ expect(subject.settings[:overrides][:environments][:generate_types]).to eq(true)
107
+ end
108
+
109
+ it 'only calls puppet generate types on environments where the specified module was updated' do
86
110
  allow(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
87
- mod = R10K::Module::Local.new(environment.name, '/fakedir', {}, environment)
88
- if mod.name == 'first'
111
+ if environment.name == 'first'
112
+ expect(environment).to receive(:deploy).and_return(['first'])
89
113
  expect(environment).to receive(:generate_types!)
90
114
  else
115
+ expect(environment).to receive(:deploy).and_return([])
91
116
  expect(environment).not_to receive(:generate_types!)
92
117
  end
93
- @modules << mod
94
- expect(environment.puppetfile).to receive(:modules).and_return([mod]).twice
95
118
  original.call(environment, &block)
96
119
  end
97
- end
98
-
99
- it 'generate_types is true' do
100
- expect(subject.settings[:overrides][:environments][:generate_types]).to eq(true)
101
- end
102
-
103
- it 'only calls puppet generate types on environments with specified module' do
104
120
  subject.call
105
- expect(@modules.length).to be(2)
106
121
  end
107
122
  end
108
123
 
@@ -177,12 +192,39 @@ describe R10K::Action::Deploy::Module do
177
192
  end
178
193
  end
179
194
 
195
+ describe 'with github-app-id' do
196
+
197
+ subject { described_class.new({ config: '/some/nonexistent/path', 'github-app-id': '/nonexistent' }, [], {}) }
198
+
199
+ it 'sets github-app-id' do
200
+ expect(subject.instance_variable_get(:@github_app_id)).to eq('/nonexistent')
201
+ end
202
+ end
203
+
204
+ describe 'with github-app-key' do
205
+
206
+ subject { described_class.new({ config: '/some/nonexistent/path', 'github-app-key': '/nonexistent' }, [], {}) }
207
+
208
+ it 'sets github-app-key' do
209
+ expect(subject.instance_variable_get(:@github_app_key)).to eq('/nonexistent')
210
+ end
211
+ end
212
+
213
+ describe 'with github-app-ttl' do
214
+
215
+ subject { described_class.new({ config: '/some/nonexistent/path', 'github-app-ttl': '/nonexistent' }, [], {}) }
216
+
217
+ it 'sets github-app-ttl' do
218
+ expect(subject.instance_variable_get(:@github_app_ttl)).to eq('/nonexistent')
219
+ end
220
+ end
221
+
180
222
  describe 'with modules' do
181
223
 
182
224
  subject { described_class.new({ config: '/some/nonexistent/path' }, ['mod1', 'mod2'], {}) }
183
225
 
184
226
  let(:cache) { instance_double("R10K::Git::Cache", 'sanitized_dirname' => 'foo', 'cached?' => true, 'sync' => true) }
185
- let(:repo) { instance_double("R10K::Git::StatefulRepository", cache: cache, resolve: 'main') }
227
+ let(:repo) { instance_double("R10K::Git::StatefulRepository", cache: cache, resolve: 'main', tracked_paths: []) }
186
228
 
187
229
  it 'does not sync modules not given' do
188
230
  allow(R10K::Deployment).to receive(:new).and_wrap_original do |original, settings, &block|
@@ -202,20 +244,30 @@ describe R10K::Action::Deploy::Module do
202
244
  allow_any_instance_of(R10K::Source::Git).to receive(:branch_names).and_return([R10K::Environment::Name.new('first', {})])
203
245
 
204
246
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
205
- pf = environment.puppetfile
206
- expect(pf).to receive(:load) do
207
- pf.add_module('mod1', { git: 'git://remote' })
208
- pf.add_module('mod2', { git: 'git://remote' })
209
- pf.add_module('mod3', { git: 'git://remote' })
210
- end
211
-
212
- pf.modules.each do |mod|
213
- if ['mod1', 'mod2'].include?(mod.name)
214
- expect(mod.should_sync?).to be(true)
215
- else
216
- expect(mod.should_sync?).to be(false)
247
+ # For this test we want to have realistic Modules and access to
248
+ # their internal Repos to validate the sync. Unfortunately, to
249
+ # do so we do some invasive mocking, effectively implementing
250
+ # our own R10K::ModuleLoader::Puppetfile#load. We directly update
251
+ # the Environment's internal ModuleLoader and then call `load` on
252
+ # it so it will create the correct loaded_content.
253
+ loader = environment.loader
254
+ allow(loader).to receive(:puppetfile_content).and_return('')
255
+ expect(loader).to receive(:load) do
256
+ loader.add_module('mod1', { git: 'git://remote' })
257
+ loader.add_module('mod2', { git: 'git://remote' })
258
+ loader.add_module('mod3', { git: 'git://remote' })
259
+
260
+ loaded_content = loader.load!
261
+ loaded_content[:modules].each do |mod|
262
+ if ['mod1', 'mod2'].include?(mod.name)
263
+ expect(mod.should_sync?).to be(true)
264
+ else
265
+ expect(mod.should_sync?).to be(false)
266
+ end
267
+ expect(mod).to receive(:sync).and_call_original
217
268
  end
218
- expect(mod).to receive(:sync).and_call_original
269
+
270
+ loaded_content
219
271
  end
220
272
 
221
273
  original.call(environment, &block)
@@ -231,7 +283,7 @@ describe R10K::Action::Deploy::Module do
231
283
  subject { described_class.new({ config: '/some/nonexistent/path', environment: 'first' }, ['mod1'], {}) }
232
284
 
233
285
  let(:cache) { instance_double("R10K::Git::Cache", 'sanitized_dirname' => 'foo', 'cached?' => true, 'sync' => true) }
234
- let(:repo) { instance_double("R10K::Git::StatefulRepository", cache: cache, resolve: 'main') }
286
+ let(:repo) { instance_double("R10K::Git::StatefulRepository", cache: cache, resolve: 'main', tracked_paths: []) }
235
287
 
236
288
  it 'only syncs to the given environments' do
237
289
  allow(R10K::Deployment).to receive(:new).and_wrap_original do |original, settings, &block|
@@ -252,24 +304,35 @@ describe R10K::Action::Deploy::Module do
252
304
  R10K::Environment::Name.new('second', {})])
253
305
 
254
306
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
255
- pf = environment.puppetfile
307
+ loader = environment.loader
256
308
 
257
309
  if environment.name == 'first'
258
- expect(pf).to receive(:load) do
259
- pf.add_module('mod1', { git: 'git://remote' })
260
- pf.add_module('mod2', { git: 'git://remote' })
261
- end
262
-
263
- pf.modules.each do |mod|
264
- if mod.name == 'mod1'
265
- expect(mod.should_sync?).to be(true)
266
- else
267
- expect(mod.should_sync?).to be(false)
310
+ # For this test we want to have realistic Modules and access to
311
+ # their internal Repos to validate the sync. Unfortunately, to
312
+ # do so we do some invasive mocking, effectively implementing
313
+ # our own R10K::ModuleLoader::Puppetfile#load. We directly update
314
+ # the Environment's internal ModuleLoader and then call `load` on
315
+ # it so it will create the correct loaded_content.
316
+ allow(loader).to receive(:puppetfile_content).and_return('')
317
+ expect(loader).to receive(:load) do
318
+ loader.add_module('mod1', { git: 'git://remote' })
319
+ loader.add_module('mod2', { git: 'git://remote' })
320
+
321
+ loaded_content = loader.load!
322
+ loaded_content[:modules].each do |mod|
323
+ if mod.name == 'mod1'
324
+ expect(mod.should_sync?).to be(true)
325
+ else
326
+ expect(mod.should_sync?).to be(false)
327
+ end
328
+ expect(mod).to receive(:sync).and_call_original
268
329
  end
269
- expect(mod).to receive(:sync).and_call_original
330
+
331
+ loaded_content
270
332
  end
333
+
271
334
  else
272
- expect(pf).not_to receive(:load)
335
+ expect(loader).not_to receive(:load)
273
336
  end
274
337
 
275
338
  original.call(environment, &block)
@@ -282,5 +345,132 @@ describe R10K::Action::Deploy::Module do
282
345
  subject.call
283
346
  end
284
347
  end
348
+
349
+
350
+ describe "postrun" do
351
+ let(:mock_config) do
352
+ R10K::Deployment::MockConfig.new(
353
+ :sources => {
354
+ :control => {
355
+ :type => :mock,
356
+ :basedir => '/some/nonexistent/path/control',
357
+ :environments => %w[first second third],
358
+ }
359
+ }
360
+ )
361
+ end
362
+
363
+ context "basic postrun hook" do
364
+ let(:settings) { { postrun: ["/path/to/executable", "arg1", "arg2"] } }
365
+ let(:deployment) { R10K::Deployment.new(mock_config.merge(settings)) }
366
+
367
+ before do
368
+ expect(R10K::Deployment).to receive(:new).and_return(deployment)
369
+ end
370
+
371
+ subject do
372
+ described_class.new({config: "/some/nonexistent/path" },
373
+ ['mod1'], settings)
374
+ end
375
+
376
+ it "is passed to Subprocess" do
377
+ mock_subprocess = double
378
+ allow(mock_subprocess).to receive(:logger=)
379
+ expect(mock_subprocess).to receive(:execute)
380
+
381
+ expect(R10K::Util::Subprocess).to receive(:new).
382
+ with(["/path/to/executable", "arg1", "arg2"]).
383
+ and_return(mock_subprocess)
384
+
385
+ expect(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
386
+ modified = subject.instance_variable_get(:@modified_envs) << environment
387
+ subject.instance_variable_set(:modified_envs, modified)
388
+ end.exactly(3).times
389
+
390
+ subject.call
391
+ end
392
+ end
393
+
394
+ context "supports environments" do
395
+ context "with one environment" do
396
+ let(:settings) { { postrun: ["/generate/types/wrapper", "$modifiedenvs"] } }
397
+ let(:deployment) { R10K::Deployment.new(mock_config.merge(settings)) }
398
+
399
+ before do
400
+ expect(R10K::Deployment).to receive(:new).and_return(deployment)
401
+ end
402
+
403
+ subject do
404
+ described_class.new({ config: '/some/nonexistent/path',
405
+ environment: 'first' },
406
+ ['mod1'], settings)
407
+ end
408
+
409
+ it "properly substitutes the environment" do
410
+ mock_subprocess = double
411
+ allow(mock_subprocess).to receive(:logger=)
412
+ expect(mock_subprocess).to receive(:execute)
413
+
414
+ expect(R10K::Util::Subprocess).to receive(:new).
415
+ with(["/generate/types/wrapper", "first"]).
416
+ and_return(mock_subprocess)
417
+
418
+ expect(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
419
+ if environment.name == 'first'
420
+ expect(environment).to receive(:deploy).and_return(['first'])
421
+ end
422
+ original.call(environment, &block)
423
+ end.exactly(3).times
424
+
425
+ subject.call
426
+ end
427
+ end
428
+
429
+ context "with all environments" do
430
+ let(:settings) { { postrun: ["/generate/types/wrapper", "$modifiedenvs"] } }
431
+ let(:deployment) { R10K::Deployment.new(mock_config.merge(settings)) }
432
+
433
+ before do
434
+ expect(R10K::Deployment).to receive(:new).and_return(deployment)
435
+ end
436
+
437
+ subject do
438
+ described_class.new({ config: '/some/nonexistent/path' },
439
+ ['mod1'], settings)
440
+ end
441
+
442
+ it "properly substitutes the environment where modules were deployed" do
443
+ mock_subprocess = double
444
+ allow(mock_subprocess).to receive(:logger=)
445
+ expect(mock_subprocess).to receive(:execute)
446
+
447
+ expect(R10K::Util::Subprocess).to receive(:new).
448
+ with(["/generate/types/wrapper", "first third"]).
449
+ and_return(mock_subprocess)
450
+
451
+ expect(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
452
+ if ['first', 'third'].include?(environment.name)
453
+ expect(environment).to receive(:deploy).and_return(['mod1'])
454
+ end
455
+ original.call(environment, &block)
456
+ end.exactly(3).times
457
+
458
+ subject.call
459
+ end
460
+
461
+ it "does not execute the command if no envs had the module" do
462
+ expect(R10K::Util::Subprocess).not_to receive(:new)
463
+
464
+ mock_mod2 = double('mock_mod', name: 'mod2')
465
+ expect(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
466
+ expect(environment).to receive(:deploy).and_return([])
467
+ original.call(environment, &block)
468
+ end.exactly(3).times
469
+
470
+ subject.call
471
+ end
472
+ end
473
+ end
474
+ end
285
475
  end
286
476
 
@@ -3,7 +3,7 @@ require 'r10k/action/puppetfile/check'
3
3
 
4
4
  describe R10K::Action::Puppetfile::Check do
5
5
  let(:default_opts) { {root: "/some/nonexistent/path"} }
6
- let(:puppetfile) { instance_double('R10K::Puppetfile', :load! => true) }
6
+ let(:loader) { instance_double('R10K::ModuleLoader::Puppetfile', :load! => {}) }
7
7
 
8
8
  def checker(opts = {}, argv = [], settings = {})
9
9
  opts = default_opts.merge(opts)
@@ -11,7 +11,11 @@ describe R10K::Action::Puppetfile::Check do
11
11
  end
12
12
 
13
13
  before(:each) do
14
- allow(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", {moduledir: nil, puppetfile_path: nil}).and_return(puppetfile)
14
+ allow(R10K::ModuleLoader::Puppetfile).
15
+ to receive(:new).
16
+ with({
17
+ basedir: "/some/nonexistent/path",
18
+ }).and_return(loader)
15
19
  end
16
20
 
17
21
  it_behaves_like "a puppetfile action"
@@ -23,8 +27,11 @@ describe R10K::Action::Puppetfile::Check do
23
27
  end
24
28
 
25
29
  it "prints an error message when validating the Puppetfile syntax raised an error" do
26
- allow(puppetfile).to receive(:load!).and_raise(R10K::Error.new("Boom!"))
27
- allow(R10K::Errors::Formatting).to receive(:format_exception).with(instance_of(R10K::Error), anything).and_return("Formatted error message")
30
+ allow(loader).to receive(:load!).and_raise(R10K::Error.new("Boom!"))
31
+ allow(R10K::Errors::Formatting).
32
+ to receive(:format_exception).
33
+ with(instance_of(R10K::Error), anything).
34
+ and_return("Formatted error message")
28
35
 
29
36
  expect($stderr).to receive(:puts).with("Formatted error message")
30
37
 
@@ -34,7 +41,12 @@ describe R10K::Action::Puppetfile::Check do
34
41
  it "respects --puppetfile option" do
35
42
  allow($stderr).to receive(:puts)
36
43
 
37
- expect(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", {moduledir: nil, puppetfile_path: "/custom/puppetfile/path"}).and_return(puppetfile)
44
+ expect(R10K::ModuleLoader::Puppetfile).
45
+ to receive(:new).
46
+ with({
47
+ basedir: "/some/nonexistent/path",
48
+ puppetfile: "/custom/puppetfile/path"
49
+ }).and_return(loader)
38
50
 
39
51
  checker({puppetfile: "/custom/puppetfile/path"}).call
40
52
  end