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
@@ -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) }
@@ -276,12 +342,12 @@ describe R10K::Action::Deploy::Environment do
276
342
 
277
343
  it "only logs about purging deployment" do
278
344
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, env, &block|
279
- 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/)
280
346
  original.call(env)
281
347
  end.at_least(:once)
282
348
 
283
- expect(subject.logger).to receive(:debug).with(/purging unmanaged environments for deployment/i)
284
- 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/)
285
351
 
286
352
  subject.call
287
353
  end
@@ -292,11 +358,11 @@ describe R10K::Action::Deploy::Environment do
292
358
 
293
359
  it "only logs about purging environment" do
294
360
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, env, &block|
295
- 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/)
296
362
  original.call(env)
297
363
  end.at_least(:once)
298
- expect(subject.logger).to receive(:debug).with(/purging unmanaged content for environment/i)
299
- 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/)
300
366
 
301
367
  subject.call
302
368
  end
@@ -309,7 +375,7 @@ describe R10K::Action::Deploy::Environment do
309
375
  original.call(env)
310
376
  end.at_least(:once)
311
377
 
312
- 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/)
313
379
 
314
380
  subject.call
315
381
  end
@@ -319,15 +385,16 @@ describe R10K::Action::Deploy::Environment do
319
385
  let(:purge_levels) { [:puppetfile] }
320
386
 
321
387
  it "only logs about purging puppetfile" do
388
+ allow(R10K::ContentSynchronizer).to receive(:concurrent_sync)
322
389
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, env, &block|
323
390
  if env.name =~ /first/
324
- expect(env.logger).to receive(:debug).with(/purging unmanaged puppetfile content/i)
391
+ expect(env.logger).to receive(:debug).with(/Purging unmanaged Puppetfile content/)
325
392
  end
326
393
  original.call(env)
327
394
  end.at_least(:once)
328
395
 
329
- expect(subject.logger).to_not receive(:debug).with(/purging unmanaged environments for deployment/i)
330
- 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/)
331
398
 
332
399
  subject.call
333
400
  end
@@ -344,6 +411,11 @@ describe R10K::Action::Deploy::Environment do
344
411
  basedir: '/some/nonexistent/path/control',
345
412
  environments: %w[first second]
346
413
  }
414
+ },
415
+ overrides: {
416
+ modules: {
417
+ pool_size: 4
418
+ }
347
419
  }
348
420
  )
349
421
  )
@@ -351,9 +423,8 @@ describe R10K::Action::Deploy::Environment do
351
423
 
352
424
  before do
353
425
  allow(R10K::Deployment).to receive(:new).and_return(deployment)
354
- end
426
+ allow_any_instance_of(R10K::Environment::Base).to receive(:purge!)
355
427
 
356
- before(:each) do
357
428
  allow(subject).to receive(:write_environment_info!)
358
429
  expect(subject.logger).not_to receive(:error)
359
430
  end
@@ -515,7 +586,6 @@ describe R10K::Action::Deploy::Environment do
515
586
  })
516
587
  end
517
588
  let(:mock_forge_module_1) { double(:name => "their_shiny_module", :properties => { :expected => "2.0.0" }) }
518
- let(:mock_puppetfile) { instance_double("R10K::Puppetfile", :modules => [mock_git_module_1, mock_git_module_2, mock_forge_module_1]) }
519
589
 
520
590
  before(:all) do
521
591
  @tmp_path = "./tmp-r10k-test-dir/"
@@ -528,10 +598,8 @@ describe R10K::Action::Deploy::Environment do
528
598
  end
529
599
 
530
600
  it "writes the .r10k-deploy file correctly if all goes well" do
531
- allow(R10K::Puppetfile).to receive(:new).and_return(mock_puppetfile)
532
-
533
601
  fake_env = Fake_Environment.new(@tmp_path, {:name => "my_cool_environment", :signature => "pablo picasso"})
534
- 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])
535
603
  subject.send(:write_environment_info!, fake_env, "2019-01-01 23:23:22 +0000", true)
536
604
 
537
605
  file_contents = File.read("#{@tmp_path}/.r10k-deploy.json")
@@ -554,10 +622,8 @@ describe R10K::Action::Deploy::Environment do
554
622
  end
555
623
 
556
624
  it "writes the .r10k-deploy file correctly if there's a failure" do
557
- allow(R10K::Puppetfile).to receive(:new).and_return(mock_puppetfile)
558
-
559
625
  fake_env = Fake_Environment.new(@tmp_path, {:name => "my_cool_environment", :signature => "pablo picasso"})
560
- 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])
561
627
  allow(mock_forge_module_1).to receive(:properties).and_raise(StandardError)
562
628
  subject.send(:write_environment_info!, fake_env, "2019-01-01 23:23:22 +0000", true)
563
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,6 +192,33 @@ 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'], {}) }
@@ -205,29 +247,27 @@ describe R10K::Action::Deploy::Module do
205
247
  # For this test we want to have realistic Modules and access to
206
248
  # their internal Repos to validate the sync. Unfortunately, to
207
249
  # do so we do some invasive mocking, effectively implementing
208
- # our own R10K::Puppetfile#load. We directly update the Puppetfile's
209
- # internal ModuleLoader and then call `load` on it so it will create
210
- # the correct loaded_content.
211
- puppetfile = environment.puppetfile
212
- loader = puppetfile.loader
213
- expect(puppetfile).to receive(:load) do
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
214
256
  loader.add_module('mod1', { git: 'git://remote' })
215
257
  loader.add_module('mod2', { git: 'git://remote' })
216
258
  loader.add_module('mod3', { git: 'git://remote' })
217
259
 
218
- allow(loader).to receive(:puppetfile_content).and_return('')
219
- loaded_content = loader.load
220
- puppetfile.instance_variable_set(:@loaded_content, loaded_content)
221
- puppetfile.instance_variable_set(:@loaded, true)
222
- end
223
-
224
- puppetfile.modules.each do |mod|
225
- if ['mod1', 'mod2'].include?(mod.name)
226
- expect(mod.should_sync?).to be(true)
227
- else
228
- expect(mod.should_sync?).to be(false)
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
229
268
  end
230
- expect(mod).to receive(:sync).and_call_original
269
+
270
+ loaded_content
231
271
  end
232
272
 
233
273
  original.call(environment, &block)
@@ -264,36 +304,35 @@ describe R10K::Action::Deploy::Module do
264
304
  R10K::Environment::Name.new('second', {})])
265
305
 
266
306
  expect(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
267
- puppetfile = environment.puppetfile
307
+ loader = environment.loader
268
308
 
269
309
  if environment.name == 'first'
270
310
  # For this test we want to have realistic Modules and access to
271
311
  # their internal Repos to validate the sync. Unfortunately, to
272
312
  # do so we do some invasive mocking, effectively implementing
273
- # our own R10K::Puppetfile#load. We directly update the Puppetfile's
274
- # internal ModuleLoader and then call `load` on it so it will create
275
- # the correct loaded_content.
276
- loader = puppetfile.loader
277
- expect(puppetfile).to receive(:load) do
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
278
318
  loader.add_module('mod1', { git: 'git://remote' })
279
319
  loader.add_module('mod2', { git: 'git://remote' })
280
320
 
281
- allow(loader).to receive(:puppetfile_content).and_return('')
282
- loaded_content = loader.load
283
- puppetfile.instance_variable_set(:@loaded_content, loaded_content)
284
- puppetfile.instance_variable_set(:@loaded, true)
285
- end
286
-
287
- puppetfile.modules.each do |mod|
288
- if mod.name == 'mod1'
289
- expect(mod.should_sync?).to be(true)
290
- else
291
- expect(mod.should_sync?).to be(false)
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
292
329
  end
293
- expect(mod).to receive(:sync).and_call_original
330
+
331
+ loaded_content
294
332
  end
333
+
295
334
  else
296
- expect(puppetfile).not_to receive(:load)
335
+ expect(loader).not_to receive(:load)
297
336
  end
298
337
 
299
338
  original.call(environment, &block)
@@ -306,5 +345,132 @@ describe R10K::Action::Deploy::Module do
306
345
  subject.call
307
346
  end
308
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
309
475
  end
310
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