r10k 3.7.0 → 3.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +1 -1
  3. data/.github/workflows/docker.yml +4 -1
  4. data/.github/workflows/release.yml +3 -2
  5. data/.github/workflows/rspec_tests.yml +1 -1
  6. data/.github/workflows/stale.yml +19 -0
  7. data/.travis.yml +8 -1
  8. data/CHANGELOG.mkd +32 -0
  9. data/CODEOWNERS +2 -2
  10. data/doc/common-patterns.mkd +1 -0
  11. data/doc/dynamic-environments/configuration.mkd +114 -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/lib/r10k/action/base.rb +10 -0
  27. data/lib/r10k/action/deploy/display.rb +49 -10
  28. data/lib/r10k/action/deploy/environment.rb +101 -51
  29. data/lib/r10k/action/deploy/module.rb +54 -30
  30. data/lib/r10k/action/puppetfile/check.rb +3 -1
  31. data/lib/r10k/action/puppetfile/install.rb +20 -23
  32. data/lib/r10k/action/puppetfile/purge.rb +8 -2
  33. data/lib/r10k/action/runner.rb +33 -0
  34. data/lib/r10k/cli/deploy.rb +13 -7
  35. data/lib/r10k/cli/puppetfile.rb +5 -5
  36. data/lib/r10k/content_synchronizer.rb +83 -0
  37. data/lib/r10k/deployment.rb +1 -1
  38. data/lib/r10k/environment/base.rb +29 -2
  39. data/lib/r10k/environment/git.rb +17 -5
  40. data/lib/r10k/environment/name.rb +22 -4
  41. data/lib/r10k/environment/svn.rb +11 -4
  42. data/lib/r10k/environment/with_modules.rb +46 -30
  43. data/lib/r10k/git.rb +1 -0
  44. data/lib/r10k/git/rugged/credentials.rb +39 -2
  45. data/lib/r10k/initializers.rb +1 -0
  46. data/lib/r10k/module.rb +1 -1
  47. data/lib/r10k/module/base.rb +17 -1
  48. data/lib/r10k/module/forge.rb +29 -11
  49. data/lib/r10k/module/git.rb +50 -27
  50. data/lib/r10k/module/local.rb +2 -1
  51. data/lib/r10k/module/svn.rb +24 -18
  52. data/lib/r10k/puppetfile.rb +66 -83
  53. data/lib/r10k/settings.rb +18 -2
  54. data/lib/r10k/source/base.rb +9 -0
  55. data/lib/r10k/source/git.rb +18 -7
  56. data/lib/r10k/source/hash.rb +5 -5
  57. data/lib/r10k/source/svn.rb +5 -3
  58. data/lib/r10k/util/cleaner.rb +21 -0
  59. data/lib/r10k/util/setopts.rb +33 -12
  60. data/lib/r10k/version.rb +1 -1
  61. data/locales/r10k.pot +98 -82
  62. data/r10k.gemspec +1 -1
  63. data/spec/fixtures/unit/action/r10k_creds.yaml +9 -0
  64. data/spec/r10k-mocks/mock_source.rb +1 -1
  65. data/spec/shared-examples/puppetfile-action.rb +7 -7
  66. data/spec/unit/action/deploy/display_spec.rb +35 -5
  67. data/spec/unit/action/deploy/environment_spec.rb +199 -38
  68. data/spec/unit/action/deploy/module_spec.rb +162 -28
  69. data/spec/unit/action/puppetfile/check_spec.rb +2 -2
  70. data/spec/unit/action/puppetfile/install_spec.rb +31 -10
  71. data/spec/unit/action/puppetfile/purge_spec.rb +25 -5
  72. data/spec/unit/action/runner_spec.rb +48 -1
  73. data/spec/unit/environment/git_spec.rb +19 -2
  74. data/spec/unit/environment/name_spec.rb +28 -0
  75. data/spec/unit/environment/svn_spec.rb +12 -0
  76. data/spec/unit/environment/with_modules_spec.rb +74 -0
  77. data/spec/unit/git/rugged/credentials_spec.rb +78 -1
  78. data/spec/unit/module/forge_spec.rb +21 -13
  79. data/spec/unit/module/git_spec.rb +63 -8
  80. data/spec/unit/module_spec.rb +77 -10
  81. data/spec/unit/puppetfile_spec.rb +63 -60
  82. data/spec/unit/util/purgeable_spec.rb +2 -8
  83. data/spec/unit/util/setopts_spec.rb +25 -1
  84. metadata +11 -12
  85. data/azure-pipelines.yml +0 -87
@@ -4,43 +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
27
  end
28
28
 
29
29
  it 'can accept a puppet-conf option' do
30
- described_class.new({ 'puppet-conf': '/nonexistent' }, [])
30
+ described_class.new({ 'puppet-conf': '/nonexistent' }, [], {})
31
31
  end
32
32
 
33
33
  it 'can accept a cachedir option' do
34
- 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' }, [], {})
35
43
  end
36
44
  end
37
45
 
38
46
  describe "with no-force" do
39
47
 
40
- subject { described_class.new({ config: "/some/nonexistent/path", :'no-force' => true}, [] )}
48
+ subject { described_class.new({ config: "/some/nonexistent/path", :'no-force' => true}, [], {}) }
41
49
 
42
50
  it "tries to preserve local modifications" do
43
- expect(subject.force).to equal(false)
51
+ expect(subject.settings[:overrides][:modules][:force]).to equal(false)
44
52
  end
45
53
  end
46
54
 
@@ -68,33 +76,33 @@ describe R10K::Action::Deploy::Module do
68
76
  config: '/some/nonexistent/path',
69
77
  'generate-types': true
70
78
  },
71
- %w[first]
79
+ %w[first],
80
+ {}
72
81
  )
73
82
  end
74
83
 
75
84
  before do
85
+ @modules = []
76
86
  allow(subject).to receive(:visit_environment).and_wrap_original do |original, environment, &block|
77
- expect(environment.puppetfile).to receive(:modules_by_vcs_cachedir).and_return(
78
- {none: [R10K::Module::Local.new(environment.name, '/fakedir', [], environment)]}
79
- )
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
80
95
  original.call(environment, &block)
81
96
  end
82
97
  end
83
98
 
84
99
  it 'generate_types is true' do
85
- expect(subject.instance_variable_get(:@generate_types)).to eq(true)
100
+ expect(subject.settings[:overrides][:environments][:generate_types]).to eq(true)
86
101
  end
87
102
 
88
103
  it 'only calls puppet generate types on environments with specified module' do
89
- expect(subject).to receive(:visit_module).and_wrap_original do |original, mod, &block|
90
- if mod.name == 'first'
91
- expect(mod.environment).to receive(:generate_types!)
92
- else
93
- expect(mod.environment).not_to receive(:generate_types!)
94
- end
95
- original.call(mod, &block)
96
- end.twice
97
104
  subject.call
105
+ expect(@modules.length).to be(2)
98
106
  end
99
107
  end
100
108
 
@@ -105,12 +113,13 @@ describe R10K::Action::Deploy::Module do
105
113
  config: '/some/nonexistent/path',
106
114
  'generate-types': false
107
115
  },
108
- %w[first]
116
+ %w[first],
117
+ {}
109
118
  )
110
119
  end
111
120
 
112
121
  it 'generate_types is false' do
113
- expect(subject.instance_variable_get(:@generate_types)).to eq(false)
122
+ expect(subject.settings[:overrides][:environments][:generate_types]).to eq(false)
114
123
  end
115
124
 
116
125
  it 'does not call puppet generate types' do |it|
@@ -125,7 +134,7 @@ describe R10K::Action::Deploy::Module do
125
134
 
126
135
  describe 'with puppet-path' do
127
136
 
128
- subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-path': '/nonexistent' }, []) }
137
+ subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-path': '/nonexistent' }, [], {}) }
129
138
 
130
139
  it 'sets puppet_path' do
131
140
  expect(subject.instance_variable_get(:@puppet_path)).to eq('/nonexistent')
@@ -134,7 +143,7 @@ describe R10K::Action::Deploy::Module do
134
143
 
135
144
  describe 'with puppet-conf' do
136
145
 
137
- subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-conf': '/nonexistent' }, []) }
146
+ subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-conf': '/nonexistent' }, [], {}) }
138
147
 
139
148
  it 'sets puppet_conf' do
140
149
  expect(subject.instance_variable_get(:@puppet_conf)).to eq('/nonexistent')
@@ -143,10 +152,135 @@ describe R10K::Action::Deploy::Module do
143
152
 
144
153
  describe 'with cachedir' do
145
154
 
146
- subject { described_class.new({ config: '/some/nonexistent/path', cachedir: '/nonexistent' }, []) }
155
+ subject { described_class.new({ config: '/some/nonexistent/path', cachedir: '/nonexistent' }, [], {}) }
147
156
 
148
- it 'sets puppet_path' do
157
+ it 'sets cachedir' do
149
158
  expect(subject.instance_variable_get(:@cachedir)).to eq('/nonexistent')
150
159
  end
151
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
152
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