r10k 3.6.0 → 3.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docker.yml +4 -1
  3. data/.github/workflows/release.yml +3 -2
  4. data/.github/workflows/rspec_tests.yml +81 -0
  5. data/.github/workflows/stale.yml +19 -0
  6. data/.travis.yml +8 -1
  7. data/CHANGELOG.mkd +33 -0
  8. data/CODEOWNERS +2 -2
  9. data/README.mkd +2 -2
  10. data/doc/common-patterns.mkd +1 -0
  11. data/doc/dynamic-environments/configuration.mkd +123 -42
  12. data/doc/dynamic-environments/usage.mkd +12 -11
  13. data/doc/puppetfile.mkd +23 -3
  14. data/docker/Gemfile +1 -1
  15. data/docker/Makefile +4 -3
  16. data/docker/docker-compose.yml +18 -0
  17. data/docker/r10k/Dockerfile +1 -1
  18. data/docker/r10k/docker-entrypoint.sh +0 -1
  19. data/docker/r10k/release.Dockerfile +1 -1
  20. data/docker/spec/dockerfile_spec.rb +26 -32
  21. data/integration/tests/git_source/git_source_repeated_remote.rb +2 -2
  22. data/integration/tests/user_scenario/basic_workflow/multi_env_custom_forge_git_module.rb +2 -1
  23. data/integration/tests/user_scenario/basic_workflow/multi_env_custom_forge_git_module_static.rb +2 -1
  24. data/integration/tests/user_scenario/basic_workflow/multi_source_custom_forge_git_module.rb +1 -1
  25. data/integration/tests/user_scenario/basic_workflow/single_env_custom_forge_git_module.rb +2 -1
  26. data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +1 -1
  27. data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +1 -1
  28. data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +1 -1
  29. data/lib/r10k/action/base.rb +10 -0
  30. data/lib/r10k/action/deploy/display.rb +49 -10
  31. data/lib/r10k/action/deploy/environment.rb +102 -51
  32. data/lib/r10k/action/deploy/module.rb +55 -30
  33. data/lib/r10k/action/puppetfile/check.rb +3 -1
  34. data/lib/r10k/action/puppetfile/install.rb +20 -23
  35. data/lib/r10k/action/puppetfile/purge.rb +8 -2
  36. data/lib/r10k/action/runner.rb +34 -0
  37. data/lib/r10k/cli/deploy.rb +14 -7
  38. data/lib/r10k/cli/puppetfile.rb +5 -5
  39. data/lib/r10k/content_synchronizer.rb +83 -0
  40. data/lib/r10k/deployment.rb +1 -1
  41. data/lib/r10k/environment/base.rb +30 -3
  42. data/lib/r10k/environment/git.rb +17 -5
  43. data/lib/r10k/environment/name.rb +22 -4
  44. data/lib/r10k/environment/svn.rb +11 -4
  45. data/lib/r10k/environment/with_modules.rb +46 -30
  46. data/lib/r10k/git.rb +1 -0
  47. data/lib/r10k/git/cache.rb +11 -1
  48. data/lib/r10k/git/rugged/credentials.rb +39 -2
  49. data/lib/r10k/initializers.rb +2 -0
  50. data/lib/r10k/module.rb +1 -1
  51. data/lib/r10k/module/base.rb +17 -1
  52. data/lib/r10k/module/forge.rb +29 -11
  53. data/lib/r10k/module/git.rb +50 -27
  54. data/lib/r10k/module/local.rb +2 -1
  55. data/lib/r10k/module/svn.rb +24 -18
  56. data/lib/r10k/puppetfile.rb +66 -83
  57. data/lib/r10k/settings.rb +29 -2
  58. data/lib/r10k/source/base.rb +9 -0
  59. data/lib/r10k/source/git.rb +18 -7
  60. data/lib/r10k/source/hash.rb +5 -5
  61. data/lib/r10k/source/svn.rb +5 -3
  62. data/lib/r10k/util/cleaner.rb +21 -0
  63. data/lib/r10k/util/setopts.rb +33 -12
  64. data/lib/r10k/version.rb +1 -1
  65. data/locales/r10k.pot +98 -82
  66. data/r10k.gemspec +1 -1
  67. data/spec/fixtures/unit/action/r10k_creds.yaml +9 -0
  68. data/spec/r10k-mocks/mock_source.rb +1 -1
  69. data/spec/shared-examples/puppetfile-action.rb +7 -7
  70. data/spec/shared-examples/subprocess-runner.rb +11 -5
  71. data/spec/unit/action/deploy/display_spec.rb +35 -5
  72. data/spec/unit/action/deploy/environment_spec.rb +207 -37
  73. data/spec/unit/action/deploy/module_spec.rb +173 -26
  74. data/spec/unit/action/puppetfile/check_spec.rb +2 -2
  75. data/spec/unit/action/puppetfile/install_spec.rb +31 -10
  76. data/spec/unit/action/puppetfile/purge_spec.rb +25 -5
  77. data/spec/unit/action/runner_spec.rb +48 -1
  78. data/spec/unit/environment/git_spec.rb +19 -2
  79. data/spec/unit/environment/name_spec.rb +28 -0
  80. data/spec/unit/environment/svn_spec.rb +12 -0
  81. data/spec/unit/environment/with_modules_spec.rb +74 -0
  82. data/spec/unit/git/cache_spec.rb +10 -0
  83. data/spec/unit/git/rugged/credentials_spec.rb +79 -2
  84. data/spec/unit/git_spec.rb +3 -3
  85. data/spec/unit/module/forge_spec.rb +21 -13
  86. data/spec/unit/module/git_spec.rb +64 -1
  87. data/spec/unit/module_spec.rb +60 -10
  88. data/spec/unit/puppetfile_spec.rb +63 -60
  89. data/spec/unit/settings_spec.rb +12 -0
  90. data/spec/unit/source/git_spec.rb +15 -3
  91. data/spec/unit/util/purgeable_spec.rb +2 -8
  92. data/spec/unit/util/setopts_spec.rb +25 -1
  93. metadata +11 -11
  94. data/azure-pipelines.yml +0 -87
@@ -4,39 +4,51 @@ require 'r10k/action/deploy/module'
4
4
 
5
5
  describe R10K::Action::Deploy::Module do
6
6
 
7
- subject { described_class.new({config: "/some/nonexistent/path"}, []) }
7
+ subject { described_class.new({config: "/some/nonexistent/path"}, [], {}) }
8
8
 
9
9
  it_behaves_like "a deploy action that requires a config file"
10
10
  it_behaves_like "a deploy action that can be write locked"
11
11
 
12
12
  describe "initializing" do
13
13
  it "accepts an environment option" do
14
- described_class.new({environment: "production"}, [])
14
+ described_class.new({environment: "production"}, [], {})
15
15
  end
16
16
 
17
17
  it "can accept a no-force option" do
18
- described_class.new({:'no-force' => true}, [])
18
+ described_class.new({:'no-force' => true}, [], {})
19
19
  end
20
20
 
21
21
  it 'can accept a generate-types option' do
22
- described_class.new({ 'generate-types': true }, [])
22
+ described_class.new({ 'generate-types': true }, [], {})
23
23
  end
24
24
 
25
25
  it 'can accept a puppet-path option' do
26
- described_class.new({ 'puppet-path': '/nonexistent' }, [])
26
+ described_class.new({ 'puppet-path': '/nonexistent' }, [], {})
27
+ end
28
+
29
+ it 'can accept a puppet-conf option' do
30
+ described_class.new({ 'puppet-conf': '/nonexistent' }, [], {})
27
31
  end
28
32
 
29
33
  it 'can accept a cachedir option' do
30
- described_class.new({ cachedir: '/nonexistent' }, [])
34
+ described_class.new({ cachedir: '/nonexistent' }, [], {})
35
+ end
36
+
37
+ it 'can accept a private-key option' do
38
+ described_class.new({ 'private-key': '/nonexistent' }, [], {})
39
+ end
40
+
41
+ it 'can accept a token option' do
42
+ described_class.new({ 'oauth-token': '/nonexistent' }, [], {})
31
43
  end
32
44
  end
33
45
 
34
46
  describe "with no-force" do
35
47
 
36
- subject { described_class.new({ config: "/some/nonexistent/path", :'no-force' => true}, [] )}
48
+ subject { described_class.new({ config: "/some/nonexistent/path", :'no-force' => true}, [], {}) }
37
49
 
38
50
  it "tries to preserve local modifications" do
39
- expect(subject.force).to equal(false)
51
+ expect(subject.settings[:overrides][:modules][:force]).to equal(false)
40
52
  end
41
53
  end
42
54
 
@@ -64,33 +76,33 @@ describe R10K::Action::Deploy::Module do
64
76
  config: '/some/nonexistent/path',
65
77
  'generate-types': true
66
78
  },
67
- %w[first]
79
+ %w[first],
80
+ {}
68
81
  )
69
82
  end
70
83
 
71
84
  before do
85
+ @modules = []
72
86
  allow(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
73
- expect(environment.puppetfile).to receive(:modules_by_vcs_cachedir).and_return(
74
- {none: [R10K::Module::Local.new(environment.name, '/fakedir', [], environment)]}
75
- )
87
+ mod = R10K::Module::Local.new(environment.name, '/fakedir', {}, environment)
88
+ if mod.name == 'first'
89
+ expect(environment).to receive(:generate_types!)
90
+ else
91
+ expect(environment).not_to receive(:generate_types!)
92
+ end
93
+ @modules << mod
94
+ expect(environment.puppetfile).to receive(:modules).and_return([mod]).twice
76
95
  original.call(environment, &block)
77
96
  end
78
97
  end
79
98
 
80
99
  it 'generate_types is true' do
81
- expect(subject.instance_variable_get(:@generate_types)).to eq(true)
100
+ expect(subject.settings[:overrides][:environments][:generate_types]).to eq(true)
82
101
  end
83
102
 
84
103
  it 'only calls puppet generate types on environments with specified module' do
85
- expect(subject).to receive(:visit_module).and_wrap_original do |original, mod, &block|
86
- if mod.name == 'first'
87
- expect(mod.environment).to receive(:generate_types!)
88
- else
89
- expect(mod.environment).not_to receive(:generate_types!)
90
- end
91
- original.call(mod, &block)
92
- end.twice
93
104
  subject.call
105
+ expect(@modules.length).to be(2)
94
106
  end
95
107
  end
96
108
 
@@ -101,12 +113,13 @@ describe R10K::Action::Deploy::Module do
101
113
  config: '/some/nonexistent/path',
102
114
  'generate-types': false
103
115
  },
104
- %w[first]
116
+ %w[first],
117
+ {}
105
118
  )
106
119
  end
107
120
 
108
121
  it 'generate_types is false' do
109
- expect(subject.instance_variable_get(:@generate_types)).to eq(false)
122
+ expect(subject.settings[:overrides][:environments][:generate_types]).to eq(false)
110
123
  end
111
124
 
112
125
  it 'does not call puppet generate types' do |it|
@@ -121,19 +134,153 @@ describe R10K::Action::Deploy::Module do
121
134
 
122
135
  describe 'with puppet-path' do
123
136
 
124
- subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-path': '/nonexistent' }, []) }
137
+ subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-path': '/nonexistent' }, [], {}) }
125
138
 
126
139
  it 'sets puppet_path' do
127
140
  expect(subject.instance_variable_get(:@puppet_path)).to eq('/nonexistent')
128
141
  end
129
142
  end
130
143
 
144
+ describe 'with puppet-conf' do
145
+
146
+ subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-conf': '/nonexistent' }, [], {}) }
147
+
148
+ it 'sets puppet_conf' do
149
+ expect(subject.instance_variable_get(:@puppet_conf)).to eq('/nonexistent')
150
+ end
151
+ end
152
+
131
153
  describe 'with cachedir' do
132
154
 
133
- subject { described_class.new({ config: '/some/nonexistent/path', cachedir: '/nonexistent' }, []) }
155
+ subject { described_class.new({ config: '/some/nonexistent/path', cachedir: '/nonexistent' }, [], {}) }
134
156
 
135
- it 'sets puppet_path' do
157
+ it 'sets cachedir' do
136
158
  expect(subject.instance_variable_get(:@cachedir)).to eq('/nonexistent')
137
159
  end
138
160
  end
161
+
162
+ describe 'with private-key' do
163
+
164
+ subject { described_class.new({ config: '/some/nonexistent/path', 'private-key': '/nonexistent' }, [], {}) }
165
+
166
+ it 'sets private_key' do
167
+ expect(subject.instance_variable_get(:@private_key)).to eq('/nonexistent')
168
+ end
169
+ end
170
+
171
+ describe 'with oauth-token' do
172
+
173
+ subject { described_class.new({ config: '/some/nonexistent/path', 'oauth-token': '/nonexistent' }, [], {}) }
174
+
175
+ it 'sets token_path' do
176
+ expect(subject.instance_variable_get(:@oauth_token)).to eq('/nonexistent')
177
+ end
178
+ end
179
+
180
+ describe 'with modules' do
181
+
182
+ subject { described_class.new({ config: '/some/nonexistent/path' }, ['mod1', 'mod2'], {}) }
183
+
184
+ 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') }
186
+
187
+ it 'does not sync modules not given' do
188
+ allow(R10K::Deployment).to receive(:new).and_wrap_original do |original, settings, &block|
189
+ original.call(settings.merge({
190
+ sources: {
191
+ main: {
192
+ remote: 'git://not/a/remote',
193
+ basedir: '/not/a/basedir',
194
+ type: 'git'
195
+ }
196
+ }
197
+ }))
198
+ end
199
+
200
+ allow(R10K::Git::StatefulRepository).to receive(:new).and_return(repo)
201
+ allow(R10K::Git).to receive_message_chain(:cache, :generate).and_return(cache)
202
+ allow_any_instance_of(R10K::Source::Git).to receive(:branch_names).and_return([R10K::Environment::Name.new('first', {})])
203
+
204
+ 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)
217
+ end
218
+ expect(mod).to receive(:sync).and_call_original
219
+ end
220
+
221
+ original.call(environment, &block)
222
+ end
223
+
224
+ expect(repo).to receive(:sync).twice
225
+
226
+ subject.call
227
+ end
228
+ end
229
+
230
+ describe 'with environments' do
231
+ subject { described_class.new({ config: '/some/nonexistent/path', environment: 'first' }, ['mod1'], {}) }
232
+
233
+ 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') }
235
+
236
+ it 'only syncs to the given environments' do
237
+ allow(R10K::Deployment).to receive(:new).and_wrap_original do |original, settings, &block|
238
+ original.call(settings.merge({
239
+ sources: {
240
+ main: {
241
+ remote: 'git://not/a/remote',
242
+ basedir: '/not/a/basedir',
243
+ type: 'git'
244
+ }
245
+ }
246
+ }))
247
+ end
248
+
249
+ allow(R10K::Git::StatefulRepository).to receive(:new).and_return(repo)
250
+ allow(R10K::Git).to receive_message_chain(:cache, :generate).and_return(cache)
251
+ allow_any_instance_of(R10K::Source::Git).to receive(:branch_names).and_return([R10K::Environment::Name.new('first', {}),
252
+ R10K::Environment::Name.new('second', {})])
253
+
254
+ expect(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
255
+ pf = environment.puppetfile
256
+
257
+ 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)
268
+ end
269
+ expect(mod).to receive(:sync).and_call_original
270
+ end
271
+ else
272
+ expect(pf).not_to receive(:load)
273
+ end
274
+
275
+ original.call(environment, &block)
276
+ end.twice
277
+
278
+ expect(repo).to receive(:sync).once
279
+ expect(subject.logger).to receive(:debug1).with(/Updating modules.*in environment.*first/i)
280
+ expect(subject.logger).to receive(:debug1).with(/skipping environment.*second/i)
281
+
282
+ subject.call
283
+ end
284
+ end
139
285
  end
286
+
@@ -11,7 +11,7 @@ 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", nil, nil).and_return(puppetfile)
14
+ allow(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", {moduledir: nil, puppetfile_path: nil}).and_return(puppetfile)
15
15
  end
16
16
 
17
17
  it_behaves_like "a puppetfile action"
@@ -34,7 +34,7 @@ describe R10K::Action::Puppetfile::Check do
34
34
  it "respects --puppetfile option" do
35
35
  allow($stderr).to receive(:puts)
36
36
 
37
- expect(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", nil, "/custom/puppetfile/path").and_return(puppetfile)
37
+ expect(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", {moduledir: nil, puppetfile_path: "/custom/puppetfile/path"}).and_return(puppetfile)
38
38
 
39
39
  checker({puppetfile: "/custom/puppetfile/path"}).call
40
40
  end
@@ -2,8 +2,11 @@ require 'spec_helper'
2
2
  require 'r10k/action/puppetfile/install'
3
3
 
4
4
  describe R10K::Action::Puppetfile::Install do
5
- let(:default_opts) { {root: "/some/nonexistent/path"} }
6
- let(:puppetfile) { R10K::Puppetfile.new('/some/nonexistent/path', nil, nil) }
5
+ let(:default_opts) { { root: "/some/nonexistent/path" } }
6
+ let(:puppetfile) {
7
+ R10K::Puppetfile.new('/some/nonexistent/path',
8
+ {:moduledir => nil, :puppetfile_path => nil, :force => false})
9
+ }
7
10
 
8
11
  def installer(opts = {}, argv = [], settings = {})
9
12
  opts = default_opts.merge(opts)
@@ -12,7 +15,10 @@ describe R10K::Action::Puppetfile::Install do
12
15
 
13
16
  before(:each) do
14
17
  allow(puppetfile).to receive(:load!).and_return(nil)
15
- allow(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", nil, nil, nil, nil).and_return(puppetfile)
18
+ allow(R10K::Puppetfile).to receive(:new).
19
+ with("/some/nonexistent/path",
20
+ {:moduledir => nil, :puppetfile_path => nil, :force => false}).
21
+ and_return(puppetfile)
16
22
  end
17
23
 
18
24
  it_behaves_like "a puppetfile install action"
@@ -20,12 +26,11 @@ describe R10K::Action::Puppetfile::Install do
20
26
  describe "installing modules" do
21
27
  let(:modules) do
22
28
  (1..4).map do |idx|
23
- R10K::Module::Base.new("author/modname#{idx}", "/some/nonexistent/path/modname#{idx}", nil)
29
+ R10K::Module::Base.new("author/modname#{idx}", "/some/nonexistent/path/modname#{idx}", {})
24
30
  end
25
31
  end
26
32
 
27
33
  before do
28
- allow(puppetfile).to receive(:purge!)
29
34
  allow(puppetfile).to receive(:modules).and_return(modules)
30
35
  allow(puppetfile).to receive(:modules_by_vcs_cachedir).and_return({none: modules})
31
36
  end
@@ -50,7 +55,15 @@ describe R10K::Action::Puppetfile::Install do
50
55
  end
51
56
 
52
57
  it "purges the moduledir after installation" do
53
- expect(puppetfile).to receive(:purge!)
58
+ mock_cleaner = double("cleaner")
59
+ allow(puppetfile).to receive(:desired_contents).and_return(["root/foo"])
60
+ allow(puppetfile).to receive(:managed_directories).and_return(["root"])
61
+ allow(puppetfile).to receive(:purge_exclusions).and_return(["root/**/**.rb"])
62
+
63
+ expect(R10K::Util::Cleaner).to receive(:new).
64
+ with(["root"], ["root/foo"], ["root/**/**.rb"]).
65
+ and_return(mock_cleaner)
66
+ expect(mock_cleaner).to receive(:purge!)
54
67
 
55
68
  installer.call
56
69
  end
@@ -58,13 +71,19 @@ describe R10K::Action::Puppetfile::Install do
58
71
 
59
72
  describe "using custom paths" do
60
73
  it "can use a custom puppetfile path" do
61
- expect(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", nil, "/some/other/path/Puppetfile", nil, nil).and_return(puppetfile)
74
+ expect(R10K::Puppetfile).to receive(:new).
75
+ with("/some/nonexistent/path",
76
+ {:moduledir => nil, :force => false, puppetfile_path: "/some/other/path/Puppetfile"}).
77
+ and_return(puppetfile)
62
78
 
63
79
  installer({puppetfile: "/some/other/path/Puppetfile"}).call
64
80
  end
65
81
 
66
82
  it "can use a custom moduledir path" do
67
- expect(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", "/some/other/path/site-modules", nil, nil, nil).and_return(puppetfile)
83
+ expect(R10K::Puppetfile).to receive(:new).
84
+ with("/some/nonexistent/path",
85
+ {:puppetfile_path => nil, :force => false, moduledir: "/some/other/path/site-modules"}).
86
+ and_return(puppetfile)
68
87
 
69
88
  installer({moduledir: "/some/other/path/site-modules"}).call
70
89
  end
@@ -76,8 +95,10 @@ describe R10K::Action::Puppetfile::Install do
76
95
  end
77
96
 
78
97
  it "can use the force overwrite option" do
79
- subject = described_class.new({root: "/some/nonexistent/path", force: true}, [])
80
- expect(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", nil, nil, nil, true).and_return(puppetfile)
98
+ subject = described_class.new({root: "/some/nonexistent/path", force: true}, [], {})
99
+ expect(R10K::Puppetfile).to receive(:new).
100
+ with("/some/nonexistent/path", {:moduledir => nil, :puppetfile_path => nil, :force => true}).
101
+ and_return(puppetfile)
81
102
  subject.call
82
103
  end
83
104
 
@@ -3,7 +3,13 @@ require 'r10k/action/puppetfile/purge'
3
3
 
4
4
  describe R10K::Action::Puppetfile::Purge do
5
5
  let(:default_opts) { {root: "/some/nonexistent/path"} }
6
- let(:puppetfile) { instance_double('R10K::Puppetfile', :load! => nil) }
6
+ let(:puppetfile) do
7
+ instance_double('R10K::Puppetfile',
8
+ :load! => nil,
9
+ :managed_directories => %w{foo},
10
+ :desired_contents => %w{bar},
11
+ :purge_exclusions => %w{baz})
12
+ end
7
13
 
8
14
  def purger(opts = {}, argv = [], settings = {})
9
15
  opts = default_opts.merge(opts)
@@ -11,13 +17,21 @@ describe R10K::Action::Puppetfile::Purge do
11
17
  end
12
18
 
13
19
  before(:each) do
14
- allow(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", nil, nil).and_return(puppetfile)
20
+ allow(R10K::Puppetfile).to receive(:new).
21
+ with("/some/nonexistent/path", {moduledir: nil, puppetfile_path: nil}).
22
+ and_return(puppetfile)
15
23
  end
16
24
 
17
25
  it_behaves_like "a puppetfile action"
18
26
 
19
27
  it "purges unmanaged entries in the Puppetfile moduledir" do
20
- expect(puppetfile).to receive(:purge!)
28
+ mock_cleaner = double("cleaner")
29
+
30
+ expect(R10K::Util::Cleaner).to receive(:new).
31
+ with(["foo"], ["bar"], ["baz"]).
32
+ and_return(mock_cleaner)
33
+
34
+ expect(mock_cleaner).to receive(:purge!)
21
35
 
22
36
  purger.call
23
37
  end
@@ -28,13 +42,19 @@ describe R10K::Action::Puppetfile::Purge do
28
42
  end
29
43
 
30
44
  it "can use a custom puppetfile path" do
31
- expect(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", nil, "/some/other/path/Puppetfile").and_return(puppetfile)
45
+ expect(R10K::Puppetfile).to receive(:new).
46
+ with("/some/nonexistent/path",
47
+ {moduledir: nil, puppetfile_path: "/some/other/path/Puppetfile"}).
48
+ and_return(puppetfile)
32
49
 
33
50
  purger({puppetfile: "/some/other/path/Puppetfile"}).call
34
51
  end
35
52
 
36
53
  it "can use a custom moduledir path" do
37
- expect(R10K::Puppetfile).to receive(:new).with("/some/nonexistent/path", "/some/other/path/site-modules", nil).and_return(puppetfile)
54
+ expect(R10K::Puppetfile).to receive(:new).
55
+ with("/some/nonexistent/path",
56
+ {moduledir: "/some/other/path/site-modules", puppetfile_path: nil}).
57
+ and_return(puppetfile)
38
58
 
39
59
  purger({moduledir: "/some/other/path/site-modules"}).call
40
60
  end
@@ -10,11 +10,12 @@ describe R10K::Action::Runner do
10
10
  Class.new do
11
11
  attr_reader :opts
12
12
  attr_reader :argv
13
+ attr_reader :settings
13
14
 
14
15
  def initialize(opts, argv, settings = {})
15
16
  @opts = opts
16
17
  @argv = argv
17
- @settings = {}
18
+ @settings = settings
18
19
  end
19
20
 
20
21
  def call
@@ -170,6 +171,52 @@ describe R10K::Action::Runner do
170
171
  end
171
172
  end
172
173
 
174
+ describe "configuring git credentials" do
175
+ it 'errors if both token and key paths are passed' do
176
+ runner = described_class.new({ 'oauth-token': '/nonexistent',
177
+ 'private-key': '/also/fake' }, %w[args yes], action_class)
178
+ expect{ runner.call }.to raise_error(R10K::Error, /Cannot specify both/)
179
+ end
180
+
181
+ it 'saves the sshkey path in settings hash' do
182
+ runner = described_class.new({ 'private-key': '/my/ssh/key' }, %w[args yes], action_class)
183
+ runner.call
184
+ expect(runner.instance.settings[:git][:private_key]).to eq('/my/ssh/key')
185
+ end
186
+
187
+ it 'overrides per-repo sshkey in settings hash' do
188
+ runner = described_class.new({ config: "spec/fixtures/unit/action/r10k_creds.yaml",
189
+ 'private-key': '/my/ssh/key' },
190
+ %w[args yes],
191
+ action_class)
192
+ runner.call
193
+ expect(runner.instance.settings[:git][:private_key]).to eq('/my/ssh/key')
194
+ expect(runner.instance.settings[:git][:repositories].count).to eq(2)
195
+ runner.instance.settings[:git][:repositories].each do |repo_settings|
196
+ expect(repo_settings[:private_key]).to eq('/my/ssh/key')
197
+ end
198
+ end
199
+
200
+ it 'saves the token path in settings hash' do
201
+ runner = described_class.new({ 'oauth-token': '/my/token/path' }, %w[args yes], action_class)
202
+ runner.call
203
+ expect(runner.instance.settings[:git][:oauth_token]).to eq('/my/token/path')
204
+ end
205
+
206
+ it 'overrides per-repo oauth token in settings hash' do
207
+ runner = described_class.new({ config: "spec/fixtures/unit/action/r10k_creds.yaml",
208
+ 'oauth-token': '/my/token' },
209
+ %w[args yes],
210
+ action_class)
211
+ runner.call
212
+ expect(runner.instance.settings[:git][:oauth_token]).to eq('/my/token')
213
+ expect(runner.instance.settings[:git][:repositories].count).to eq(2)
214
+ runner.instance.settings[:git][:repositories].each do |repo_settings|
215
+ expect(repo_settings[:oauth_token]).to eq('/my/token')
216
+ end
217
+ end
218
+ end
219
+
173
220
  describe "configuration authorization" do
174
221
  context "when license is not present" do
175
222
  before(:each) do