r10k 3.5.0 → 3.8.0

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +4 -1
  3. data/.github/workflows/docker.yml +25 -1
  4. data/.github/workflows/rspec_tests.yml +81 -0
  5. data/.travis.yml +14 -11
  6. data/CHANGELOG.mkd +42 -6
  7. data/CODEOWNERS +1 -1
  8. data/Gemfile +1 -1
  9. data/README.mkd +13 -4
  10. data/azure-pipelines.yml +2 -1
  11. data/doc/dynamic-environments/configuration.mkd +60 -3
  12. data/doc/dynamic-environments/usage.mkd +5 -4
  13. data/doc/faq.mkd +6 -1
  14. data/doc/puppetfile.mkd +2 -0
  15. data/docker/Makefile +16 -2
  16. data/docker/r10k/Dockerfile +17 -6
  17. data/docker/r10k/release.Dockerfile +23 -4
  18. data/integration/tests/git_source/git_source_repeated_remote.rb +68 -0
  19. data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +1 -1
  20. data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +1 -1
  21. data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +1 -1
  22. data/lib/r10k/action/deploy/environment.rb +3 -0
  23. data/lib/r10k/action/deploy/module.rb +4 -1
  24. data/lib/r10k/action/runner.rb +34 -0
  25. data/lib/r10k/cli/deploy.rb +9 -4
  26. data/lib/r10k/cli/puppetfile.rb +5 -5
  27. data/lib/r10k/environment/base.rb +8 -1
  28. data/lib/r10k/environment/with_modules.rb +27 -19
  29. data/lib/r10k/forge/module_release.rb +2 -2
  30. data/lib/r10k/git.rb +1 -0
  31. data/lib/r10k/git/cache.rb +12 -4
  32. data/lib/r10k/git/rugged/credentials.rb +32 -2
  33. data/lib/r10k/git/stateful_repository.rb +4 -0
  34. data/lib/r10k/initializers.rb +2 -0
  35. data/lib/r10k/module/base.rb +8 -0
  36. data/lib/r10k/module/forge.rb +1 -1
  37. data/lib/r10k/module/git.rb +20 -3
  38. data/lib/r10k/puppetfile.rb +30 -12
  39. data/lib/r10k/settings.rb +24 -2
  40. data/lib/r10k/source/git.rb +22 -2
  41. data/lib/r10k/version.rb +1 -1
  42. data/locales/r10k.pot +60 -36
  43. data/spec/fixtures/unit/action/r10k_creds.yaml +9 -0
  44. data/spec/shared-examples/subprocess-runner.rb +11 -5
  45. data/spec/unit/action/deploy/environment_spec.rb +43 -2
  46. data/spec/unit/action/deploy/module_spec.rb +40 -1
  47. data/spec/unit/action/puppetfile/install_spec.rb +1 -0
  48. data/spec/unit/action/runner_spec.rb +48 -1
  49. data/spec/unit/environment/git_spec.rb +3 -2
  50. data/spec/unit/environment/with_modules_spec.rb +74 -0
  51. data/spec/unit/forge/module_release_spec.rb +14 -10
  52. data/spec/unit/git/cache_spec.rb +10 -0
  53. data/spec/unit/git/rugged/credentials_spec.rb +69 -2
  54. data/spec/unit/git_spec.rb +3 -3
  55. data/spec/unit/module/git_spec.rb +55 -0
  56. data/spec/unit/puppetfile_spec.rb +61 -7
  57. data/spec/unit/settings_spec.rb +12 -0
  58. data/spec/unit/source/git_spec.rb +49 -1
  59. metadata +6 -2
@@ -27,6 +27,7 @@ describe R10K::Action::Puppetfile::Install do
27
27
  before do
28
28
  allow(puppetfile).to receive(:purge!)
29
29
  allow(puppetfile).to receive(:modules).and_return(modules)
30
+ allow(puppetfile).to receive(:modules_by_vcs_cachedir).and_return({none: modules})
30
31
  end
31
32
 
32
33
  it "syncs each module in the Puppetfile" do
@@ -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
@@ -62,9 +62,10 @@ describe R10K::Environment::Git do
62
62
 
63
63
  describe "enumerating modules" do
64
64
  it "loads the Puppetfile and returns modules in that puppetfile" do
65
+ mod = double('A module', :name => 'dbl')
65
66
  expect(subject.puppetfile).to receive(:load)
66
- expect(subject.puppetfile).to receive(:modules).and_return [:modules]
67
- expect(subject.modules).to eq([:modules])
67
+ expect(subject.puppetfile).to receive(:modules).and_return [mod]
68
+ expect(subject.modules).to eq([mod])
68
69
  end
69
70
  end
70
71
 
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'r10k/environment'
3
+
4
+ describe R10K::Environment::WithModules do
5
+ subject do
6
+ described_class.new(
7
+ 'release42',
8
+ '/some/nonexistent/environmentdir',
9
+ 'prefix_release42',
10
+ {
11
+ :type => 'bare',
12
+ :modules => {
13
+ 'puppetlabs-stdlib' => { local: true },
14
+ 'puppetlabs-concat' => { local: true },
15
+ 'puppetlabs-exec' => { local: true },
16
+ }
17
+ }.merge(subject_params)
18
+ )
19
+ end
20
+
21
+ # Default no additional params
22
+ let(:subject_params) { {} }
23
+
24
+ describe "dealing with module conflicts" do
25
+ context "with no module conflicts" do
26
+ it "validates when there are no conflicts" do
27
+ mod = instance_double('R10K::Module::Base', name: 'nonconflict', origin: :puppetfile)
28
+ expect(subject.module_conflicts?(mod)).to eq false
29
+ end
30
+ end
31
+
32
+ context "with module conflicts and default behavior" do
33
+ it "does not raise an error" do
34
+ mod = instance_double('R10K::Module::Base', name: 'stdlib', origin: :puppetfile)
35
+ expect(subject.logger).to receive(:warn).with(/Puppetfile.*both define.*ignored/i)
36
+ expect(subject.module_conflicts?(mod)).to eq true
37
+ end
38
+ end
39
+
40
+ context "with module conflicts and 'error' behavior" do
41
+ let(:subject_params) {{ :module_conflicts => 'error' }}
42
+ it "raises an error" do
43
+ mod = instance_double('R10K::Module::Base', name: 'stdlib', origin: :puppetfile)
44
+ expect { subject.module_conflicts?(mod) }.to raise_error(R10K::Error, /Puppetfile.*both define.*/i)
45
+ end
46
+ end
47
+
48
+ context "with module conflicts and 'override' behavior" do
49
+ let(:subject_params) {{ :module_conflicts => 'override' }}
50
+ it "does not raise an error" do
51
+ mod = instance_double('R10K::Module::Base', name: 'stdlib', origin: :puppetfile)
52
+ expect(subject.logger).to receive(:debug).with(/Puppetfile.*both define.*ignored/i)
53
+ expect(subject.module_conflicts?(mod)).to eq true
54
+ end
55
+ end
56
+
57
+ context "with module conflicts and invalid configuration" do
58
+ let(:subject_params) {{ :module_conflicts => 'batman' }}
59
+ it "raises an error" do
60
+ mod = instance_double('R10K::Module::Base', name: 'stdlib', origin: :puppetfile)
61
+ expect { subject.module_conflicts?(mod) }.to raise_error(R10K::Error, /Unexpected value.*module_conflicts.*/i)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "modules method" do
67
+ it "returns the configured modules, and Puppetfile modules" do
68
+ puppetfile_mod = instance_double('R10K::Module::Base', name: 'zebra')
69
+ expect(subject.puppetfile).to receive(:modules).and_return [puppetfile_mod]
70
+ returned_modules = subject.modules
71
+ expect(returned_modules.map(&:name).sort).to eq(%w[concat exec stdlib zebra])
72
+ end
73
+ end
74
+ end
@@ -166,33 +166,37 @@ describe R10K::Forge::ModuleRelease do
166
166
  end
167
167
 
168
168
  describe "#cleanup_unpack_path" do
169
- it "ignores the unpack_path if it doesn't exist" do
170
- expect(unpack_path).to receive(:exist?).and_return false
171
- expect(unpack_path).to_not receive(:parent)
169
+ it "ignores the unpack_path if the parent doesn't exist" do
170
+ parent = instance_double('Pathname')
171
+ expect(parent).to receive(:exist?).and_return false
172
+ expect(parent).to_not receive(:rmtree)
173
+ expect(unpack_path).to receive(:parent).and_return(parent)
172
174
  subject.cleanup_unpack_path
173
175
  end
174
176
 
175
177
  it "removes the containing directory of unpack_path if it exists" do
176
178
  parent = instance_double('Pathname')
177
179
  expect(parent).to receive(:rmtree)
178
- expect(unpack_path).to receive(:exist?).and_return true
179
- expect(unpack_path).to receive(:parent).and_return(parent)
180
+ expect(parent).to receive(:exist?).and_return true
181
+ expect(unpack_path).to receive(:parent).and_return(parent).exactly(2).times
180
182
  subject.cleanup_unpack_path
181
183
  end
182
184
  end
183
185
 
184
186
  describe "#cleanup_download_path" do
185
- it "ignores the download_path if it doesn't exist" do
186
- expect(download_path).to receive(:exist?).and_return false
187
- expect(download_path).to_not receive(:parent)
187
+ it "ignores the download_path if the parent doesn't exist" do
188
+ parent = instance_double('Pathname')
189
+ expect(parent).to receive(:exist?).and_return false
190
+ expect(parent).to_not receive(:rmtree)
191
+ expect(download_path).to receive(:parent).and_return(parent)
188
192
  subject.cleanup_download_path
189
193
  end
190
194
 
191
195
  it "removes the containing directory of download_path if it exists" do
192
196
  parent = instance_double('Pathname')
193
197
  expect(parent).to receive(:rmtree)
194
- expect(download_path).to receive(:exist?).and_return true
195
- expect(download_path).to receive(:parent).and_return(parent)
198
+ expect(parent).to receive(:exist?).and_return true
199
+ expect(download_path).to receive(:parent).and_return(parent).exactly(2).times
196
200
  subject.cleanup_download_path
197
201
  end
198
202
  end
@@ -3,6 +3,16 @@ require 'r10k/git/cache'
3
3
 
4
4
  describe R10K::Git::Cache do
5
5
 
6
+ describe 'the default cache_root' do
7
+ it 'is in the right location in linux', unless: R10K::Util::Platform.windows? do
8
+ expect(described_class.defaults[:cache_root]).to match(/\.r10k\/git/)
9
+ end
10
+
11
+ it 'is in the right location for windows', if: R10K::Util::Platform.windows? do
12
+ expect(described_class.defaults[:cache_root]).to match(/[^.]r10k\/git/)
13
+ end
14
+ end
15
+
6
16
  let(:subclass) do
7
17
  Class.new(described_class) do
8
18
  def self.bare_repository
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby? do
3
+ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby? || R10K::Util::Platform.windows? do
4
4
  before(:all) do
5
5
  require 'r10k/git/rugged/credentials'
6
6
  require 'rugged/credentials'
@@ -10,7 +10,7 @@ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby?
10
10
 
11
11
  subject { described_class.new(repo) }
12
12
 
13
- after(:all) { R10K::Git.settings.reset! }
13
+ after(:each) { R10K::Git.settings.reset! }
14
14
 
15
15
  describe "determining the username" do
16
16
  before { R10K::Git.settings[:username] = "moderns" }
@@ -39,6 +39,7 @@ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby?
39
39
 
40
40
  it "prefers a per-repository SSH private key" do
41
41
  allow(File).to receive(:readable?).with("/etc/puppetlabs/r10k/ssh/tessier-ashpool-id_rsa").and_return true
42
+ R10K::Git.settings[:private_key] = "/etc/puppetlabs/r10k/ssh/id_rsa"
42
43
  R10K::Git.settings[:repositories] = [{ remote: "ssh://git@tessier-ashpool.freeside/repo.git",
43
44
  private_key: "/etc/puppetlabs/r10k/ssh/tessier-ashpool-id_rsa"}]
44
45
  creds = subject.get_ssh_key_credentials("ssh://git@tessier-ashpool.freeside/repo.git", nil)
@@ -78,6 +79,72 @@ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby?
78
79
  end
79
80
  end
80
81
 
82
+ describe "generating token credentials" do
83
+ it 'errors if token file does not exist' do
84
+ R10K::Git.settings[:oauth_token] = "/missing/token/file"
85
+ expect(File).to receive(:readable?).with("/missing/token/file").and_return false
86
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
87
+ expect {
88
+ subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
89
+ }.to raise_error(R10K::Git::GitError, /cannot load OAuth token/)
90
+ end
91
+
92
+ it 'errors if the token on stdin is not a valid OAuth token' do
93
+ allow($stdin).to receive(:read).and_return("<bad>token")
94
+ R10K::Git.settings[:oauth_token] = "-"
95
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
96
+ expect {
97
+ subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
98
+ }.to raise_error(R10K::Git::GitError, /invalid characters/)
99
+ end
100
+
101
+ it 'errors if the token in the file is not a valid OAuth token' do
102
+ token_file = Tempfile.new('token')
103
+ token_file.write('my bad \ntoken')
104
+ token_file.close
105
+ R10K::Git.settings[:oauth_token] = token_file.path
106
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
107
+ expect {
108
+ subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
109
+ }.to raise_error(R10K::Git::GitError, /invalid characters/)
110
+ end
111
+
112
+ it 'prefers per-repo token file' do
113
+ token_file = Tempfile.new('token')
114
+ token_file.write('my_token')
115
+ token_file.close
116
+ R10K::Git.settings[:oauth_token] = "/do/not/use"
117
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git",
118
+ oauth_token: token_file.path }]
119
+ creds = subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
120
+ expect(creds).to be_a_kind_of(Rugged::Credentials::UserPassword)
121
+ expect(creds.instance_variable_get(:@password)).to eq("my_token")
122
+ expect(creds.instance_variable_get(:@username)).to eq("x-oauth-token")
123
+ end
124
+
125
+ it 'uses the token from a file as a password' do
126
+ token_file = Tempfile.new('token')
127
+ token_file.write('my_token')
128
+ token_file.close
129
+ R10K::Git.settings[:oauth_token] = token_file.path
130
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
131
+ creds = subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
132
+ expect(creds).to be_a_kind_of(Rugged::Credentials::UserPassword)
133
+ expect(creds.instance_variable_get(:@password)).to eq("my_token")
134
+ expect(creds.instance_variable_get(:@username)).to eq("x-oauth-token")
135
+ end
136
+
137
+ it 'uses the token from stdin as a password' do
138
+ allow($stdin).to receive(:read).and_return("my_token")
139
+ R10K::Git.settings[:oauth_token] = '-'
140
+ R10K::Git.settings[:repositories] = [{remote: "https://tessier-ashpool.freeside/repo.git"}]
141
+ creds = subject.get_plaintext_credentials("https://tessier-ashpool.freeside/repo.git", nil)
142
+ expect(creds).to be_a_kind_of(Rugged::Credentials::UserPassword)
143
+ expect(creds.instance_variable_get(:@password)).to eq("my_token")
144
+ expect(creds.instance_variable_get(:@username)).to eq("x-oauth-token")
145
+ end
146
+ end
147
+
81
148
  describe "generating default credentials" do
82
149
  it "generates the rugged default credential type" do
83
150
  creds = subject.get_default_credentials("https://azurediamond:hunter2@tessier-ashpool.freeside/repo.git", "azurediamond")
@@ -11,7 +11,7 @@ describe R10K::Git do
11
11
  expect(described_class.default_name).to eq :shellgit
12
12
  end
13
13
 
14
- context 'under c-based rubies', :unless => R10K::Util::Platform.jruby? do
14
+ context 'under c-based rubies with rugged available', :unless => R10K::Util::Platform.jruby? || R10K::Util::Platform.windows? do
15
15
  it 'returns rugged when the git executable is absent and the rugged library is present' do
16
16
  expect(R10K::Features).to receive(:available?).with(:shellgit).and_return false
17
17
  expect(R10K::Features).to receive(:available?).with(:rugged).and_return true
@@ -53,7 +53,7 @@ describe R10K::Git do
53
53
  }.to raise_error(R10K::Error, "Git provider 'shellgit' is not functional.")
54
54
  end
55
55
 
56
- context 'under c-based rubies', :unless => R10K::Util::Platform.jruby? do
56
+ context 'under c-based rubies with rugged available', :unless => R10K::Util::Platform.jruby? || R10K::Util::Platform.windows? do
57
57
  it "sets the current provider if the provider exists and is functional" do
58
58
  expect(R10K::Features).to receive(:available?).with(:rugged).and_return true
59
59
  described_class.provider = :rugged
@@ -71,7 +71,7 @@ describe R10K::Git do
71
71
  end
72
72
 
73
73
  describe "retrieving the current provider" do
74
- context 'under c-based rubies', :unless => R10K::Util::Platform.jruby? do
74
+ context 'under c-based rubies', :unless => R10K::Util::Platform.jruby? || R10K::Util::Platform.windows? do
75
75
  it "uses the default if a provider has not been set" do
76
76
  expect(described_class).to receive(:default_name).and_return :rugged
77
77
  expect(described_class.provider).to eq(R10K::Git::Rugged)
@@ -268,6 +268,61 @@ describe R10K::Module::Git do
268
268
  end
269
269
  end
270
270
  end
271
+
272
+ context "when using default_branch_override" do
273
+ before(:each) do
274
+ allow(mock_repo).to receive(:resolve).with(mock_env.ref).and_return(nil)
275
+ end
276
+
277
+ context "and the default branch override is resolvable" do
278
+ it "uses the override" do
279
+ expect(mock_repo).to receive(:resolve).with('default_override').and_return('5566aabb')
280
+ mod = test_module({branch: :control_branch,
281
+ default_branch: 'default',
282
+ default_branch_override: 'default_override'},
283
+ mock_env)
284
+ expect(mod.properties).to include(expected: 'default_override')
285
+ end
286
+ end
287
+
288
+ context "and the default branch override is not resolvable" do
289
+ context "and default branch is provided" do
290
+ it "falls back to the default" do
291
+ expect(mock_repo).to receive(:resolve).with('default_override').and_return(nil)
292
+ expect(mock_repo).to receive(:resolve).with('default').and_return('5566aabb')
293
+ mod = test_module({branch: :control_branch,
294
+ default_branch: 'default',
295
+ default_branch_override: 'default_override'},
296
+ mock_env)
297
+ expect(mod.properties).to include(expected: 'default')
298
+ end
299
+ end
300
+
301
+ context "and default branch is not provided" do
302
+ it "raises the appropriate error" do
303
+ expect(mock_repo).to receive(:resolve).with('default_override').and_return(nil)
304
+ mod = test_module({branch: :control_branch,
305
+ default_branch_override: 'default_override'},
306
+ mock_env)
307
+
308
+ expect { mod.properties }.to raise_error(ArgumentError, /unable to manage.*or resolve the default branch override.*no default provided/i)
309
+ end
310
+ end
311
+
312
+ context "and default branch is not resolvable" do
313
+ it "raises the appropriate error" do
314
+ expect(mock_repo).to receive(:resolve).with('default_override').and_return(nil)
315
+ expect(mock_repo).to receive(:resolve).with('default').and_return(nil)
316
+ mod = test_module({branch: :control_branch,
317
+ default_branch: 'default',
318
+ default_branch_override: 'default_override'},
319
+ mock_env)
320
+
321
+ expect { mod.properties }.to raise_error(ArgumentError, /unable to manage.*or resolve the default branch override.*or resolve default/i)
322
+ end
323
+ end
324
+ end
325
+ end
271
326
  end
272
327
  end
273
328
  end
@@ -127,6 +127,17 @@ describe R10K::Puppetfile do
127
127
 
128
128
  expect { subject.add_module('puppet/test_module', module_opts) }.to raise_error(R10K::Error, /cannot manage content.*is not within/i).and not_change { subject.modules }
129
129
  end
130
+
131
+ it "should disable and not add modules that conflict with the environment" do
132
+ env = instance_double('R10K::Environment::Base')
133
+ mod = instance_double('R10K::Module::Base', name: 'conflict', origin: :puppetfile)
134
+ allow(mod).to receive(:origin=).and_return(nil)
135
+ allow(subject).to receive(:environment).and_return(env)
136
+ allow(env).to receive(:'module_conflicts?').with(mod).and_return(true)
137
+
138
+ allow(R10K::Module).to receive(:new).with('test', anything, anything, anything).and_return(mod)
139
+ expect { subject.add_module('test', {}) }.not_to change { subject.modules }
140
+ end
130
141
  end
131
142
 
132
143
  describe "#purge_exclusions" do
@@ -154,6 +165,26 @@ describe R10K::Puppetfile do
154
165
  end
155
166
  end
156
167
 
168
+ describe '#managed_directories' do
169
+ it 'returns an array of paths that can be purged' do
170
+ allow(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, '1.2.3', anything).and_call_original
171
+
172
+ subject.add_module('puppet/test_module', '1.2.3')
173
+ expect(subject.managed_directories).to match_array(["/some/nonexistent/basedir/modules"])
174
+ end
175
+
176
+ context 'with a module with install_path == \'\'' do
177
+ it 'basedir isn\'t in the list of paths to purge' do
178
+ module_opts = { install_path: '', git: 'git@example.com:puppet/test_module.git' }
179
+
180
+ allow(R10K::Module).to receive(:new).with('puppet/test_module', subject.basedir, module_opts, anything).and_call_original
181
+
182
+ subject.add_module('puppet/test_module', module_opts)
183
+ expect(subject.managed_directories).to be_empty
184
+ end
185
+ end
186
+ end
187
+
157
188
  describe "evaluating a Puppetfile" do
158
189
  def expect_wrapped_error(orig, pf_path, wrapped_error)
159
190
  expect(orig).to be_a_kind_of(R10K::Error)
@@ -244,7 +275,8 @@ describe R10K::Puppetfile do
244
275
  default_branch_override = 'default_branch_override_name'
245
276
  expect { subject.load!(default_branch_override) }.not_to raise_error
246
277
  git_module = subject.modules[0]
247
- expect(git_module.default_ref).to eq default_branch_override
278
+ expect(git_module.default_override_ref).to eq default_branch_override
279
+ expect(git_module.default_ref).to eq "here_lies_the_default_branch"
248
280
  end
249
281
  end
250
282
 
@@ -263,12 +295,12 @@ describe R10K::Puppetfile do
263
295
  block.call
264
296
  end
265
297
 
266
- mod1 = spy('module')
298
+ mod1 = instance_double('R10K::Module::Base', :cachedir => :none)
299
+ mod2 = instance_double('R10K::Module::Base', :cachedir => :none)
267
300
  expect(mod1).to receive(:accept).with(visitor)
268
- mod2 = spy('module')
269
301
  expect(mod2).to receive(:accept).with(visitor)
270
-
271
302
  expect(subject).to receive(:modules).and_return([mod1, mod2])
303
+
272
304
  subject.accept(visitor)
273
305
  end
274
306
 
@@ -284,11 +316,10 @@ describe R10K::Puppetfile do
284
316
  block.call
285
317
  end
286
318
 
287
- mod1 = spy('module')
319
+ mod1 = instance_double('R10K::Module::Base', :cachedir => :none)
320
+ mod2 = instance_double('R10K::Module::Base', :cachedir => :none)
288
321
  expect(mod1).to receive(:accept).with(visitor)
289
- mod2 = spy('module')
290
322
  expect(mod2).to receive(:accept).with(visitor)
291
-
292
323
  expect(subject).to receive(:modules).and_return([mod1, mod2])
293
324
 
294
325
  expect(Thread).to receive(:new).exactly(pool_size).and_call_original
@@ -296,5 +327,28 @@ describe R10K::Puppetfile do
296
327
 
297
328
  subject.accept(visitor)
298
329
  end
330
+
331
+ it "Creates queues of modules grouped by cachedir" do
332
+ visitor = spy('visitor')
333
+ expect(visitor).to receive(:visit) do |type, other, &block|
334
+ expect(type).to eq :puppetfile
335
+ expect(other).to eq subject
336
+ block.call
337
+ end
338
+
339
+ m1 = instance_double('R10K::Module::Base', :cachedir => '/dev/null/A')
340
+ m2 = instance_double('R10K::Module::Base', :cachedir => '/dev/null/B')
341
+ m3 = instance_double('R10K::Module::Base', :cachedir => '/dev/null/C')
342
+ m4 = instance_double('R10K::Module::Base', :cachedir => '/dev/null/C')
343
+ m5 = instance_double('R10K::Module::Base', :cachedir => '/dev/null/D')
344
+ m6 = instance_double('R10K::Module::Base', :cachedir => '/dev/null/D')
345
+
346
+ expect(subject).to receive(:modules).and_return([m1, m2, m3, m4, m5, m6])
347
+
348
+ queue = subject.modules_queue(visitor)
349
+ expect(queue.length).to be 4
350
+ queue_array = 4.times.map { queue.pop }
351
+ expect(queue_array).to match_array([[m1], [m2], [m3, m4], [m5, m6]])
352
+ end
299
353
  end
300
354
  end