r10k 3.9.1 → 3.11.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.
- checksums.yaml +4 -4
- data/.github/workflows/rspec_tests.yml +1 -1
- data/.travis.yml +0 -10
- data/CHANGELOG.mkd +28 -0
- data/README.mkd +6 -0
- data/doc/dynamic-environments/configuration.mkd +21 -0
- data/doc/puppetfile.mkd +15 -1
- data/integration/Rakefile +3 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_specify_deleted_forge_module.rb +3 -9
- data/integration/tests/user_scenario/basic_workflow/single_env_purge_unmanaged_modules.rb +21 -25
- data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +3 -3
- data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +3 -3
- data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +3 -3
- data/lib/r10k/action/base.rb +6 -3
- data/lib/r10k/action/deploy/display.rb +6 -3
- data/lib/r10k/action/deploy/environment.rb +15 -4
- data/lib/r10k/action/deploy/module.rb +37 -8
- data/lib/r10k/action/runner.rb +45 -10
- data/lib/r10k/cli/deploy.rb +4 -0
- data/lib/r10k/git.rb +3 -0
- data/lib/r10k/git/cache.rb +1 -1
- data/lib/r10k/git/rugged/credentials.rb +77 -0
- data/lib/r10k/git/stateful_repository.rb +1 -0
- data/lib/r10k/initializers.rb +10 -0
- data/lib/r10k/module/base.rb +37 -0
- data/lib/r10k/module/forge.rb +7 -2
- data/lib/r10k/module/git.rb +2 -1
- data/lib/r10k/module/svn.rb +2 -1
- data/lib/r10k/module_loader/puppetfile.rb +206 -0
- data/lib/r10k/module_loader/puppetfile/dsl.rb +37 -0
- data/lib/r10k/puppetfile.rb +83 -160
- data/lib/r10k/settings.rb +47 -2
- data/lib/r10k/settings/definition.rb +1 -1
- data/lib/r10k/source/base.rb +10 -0
- data/lib/r10k/source/git.rb +5 -0
- data/lib/r10k/source/svn.rb +4 -0
- data/lib/r10k/util/purgeable.rb +70 -8
- data/lib/r10k/version.rb +1 -1
- data/locales/r10k.pot +129 -57
- data/r10k.gemspec +2 -0
- data/spec/fixtures/unit/action/r10k_forge_auth.yaml +4 -0
- data/spec/fixtures/unit/action/r10k_forge_auth_no_url.yaml +3 -0
- data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_subdir_2/ignored_1 +0 -0
- data/spec/fixtures/unit/util/purgeable/managed_two/.hidden/unmanaged_3 +0 -0
- data/spec/unit/action/deploy/environment_spec.rb +25 -0
- data/spec/unit/action/deploy/module_spec.rb +216 -14
- data/spec/unit/action/runner_spec.rb +129 -25
- data/spec/unit/git/cache_spec.rb +14 -0
- data/spec/unit/git/rugged/credentials_spec.rb +29 -0
- data/spec/unit/git/stateful_repository_spec.rb +5 -0
- data/spec/unit/module/base_spec.rb +46 -0
- data/spec/unit/module/forge_spec.rb +27 -1
- data/spec/unit/module/git_spec.rb +17 -8
- data/spec/unit/module/svn_spec.rb +18 -0
- data/spec/unit/module_loader/puppetfile_spec.rb +343 -0
- data/spec/unit/module_spec.rb +28 -0
- data/spec/unit/puppetfile_spec.rb +127 -191
- data/spec/unit/settings_spec.rb +24 -2
- data/spec/unit/util/purgeable_spec.rb +38 -6
- metadata +23 -2
data/spec/unit/git/cache_spec.rb
CHANGED
@@ -62,4 +62,18 @@ describe R10K::Git::Cache do
|
|
62
62
|
subject.cached?
|
63
63
|
end
|
64
64
|
end
|
65
|
+
|
66
|
+
describe "dirname sanitization" do
|
67
|
+
it 'sanitizes cache directory name' do
|
68
|
+
expect(subject.sanitized_dirname).to eq('git---some-git-remote')
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with username and password' do
|
72
|
+
subject { subclass.new('https://"user:pa$$w0rd:@some/git/remote') }
|
73
|
+
|
74
|
+
it 'sanitizes cache directory name' do
|
75
|
+
expect(subject.sanitized_dirname).to eq('https---some-git-remote')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
65
79
|
end
|
@@ -79,6 +79,35 @@ describe R10K::Git::Rugged::Credentials, :unless => R10K::Util::Platform.jruby?
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
describe "generating github app tokens" do
|
83
|
+
it 'errors if app id has invalid characters' do
|
84
|
+
expect { subject.github_app_token("123A567890", "fake", "300")
|
85
|
+
}.to raise_error(R10K::Git::GitError, /App id contains invalid characters/)
|
86
|
+
end
|
87
|
+
it 'errors if app ttl has invalid characters' do
|
88
|
+
expect { subject.github_app_token("123456", "fake", "abc")
|
89
|
+
}.to raise_error(R10K::Git::GitError, /Github App token ttl contains/)
|
90
|
+
end
|
91
|
+
it 'errors if private file does not exist' do
|
92
|
+
R10K::Git.settings[:github_app_key] = "/missing/token/file"
|
93
|
+
expect(File).to receive(:readable?).with(R10K::Git.settings[:github_app_key]).and_return false
|
94
|
+
expect {
|
95
|
+
subject.github_app_token("123456", R10K::Git.settings[:github_app_key], "300")
|
96
|
+
}.to raise_error(R10K::Git::GitError, /App key is missing or unreadable/)
|
97
|
+
end
|
98
|
+
it 'errors if file is not a valid SSL key' do
|
99
|
+
token_file = Tempfile.new('token')
|
100
|
+
token_file.write('my_token')
|
101
|
+
token_file.close
|
102
|
+
R10K::Git.settings[:github_app_key] = token_file.path
|
103
|
+
expect(File).to receive(:readable?).with(token_file.path).and_return true
|
104
|
+
expect {
|
105
|
+
subject.github_app_token("123456", R10K::Git.settings[:github_app_key], "300")
|
106
|
+
}.to raise_error(R10K::Git::GitError, /App key is not a valid SSL key/)
|
107
|
+
token_file.unlink
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
82
111
|
describe "generating token credentials" do
|
83
112
|
it 'errors if token file does not exist' do
|
84
113
|
R10K::Git.settings[:oauth_token] = "/missing/token/file"
|
@@ -19,6 +19,11 @@ describe R10K::Git::StatefulRepository do
|
|
19
19
|
expect(subject.sync_cache?(ref)).to eq true
|
20
20
|
end
|
21
21
|
|
22
|
+
it "is true if the ref is HEAD" do
|
23
|
+
expect(cache).to receive(:exist?).and_return true
|
24
|
+
expect(subject.sync_cache?('HEAD')).to eq true
|
25
|
+
end
|
26
|
+
|
22
27
|
it "is true if the ref is unresolvable" do
|
23
28
|
expect(cache).to receive(:exist?).and_return true
|
24
29
|
expect(cache).to receive(:ref_type).with('0.9.x').and_return(:unknown)
|
@@ -28,6 +28,52 @@ describe R10K::Module::Base do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
describe 'deleting the spec dir' do
|
32
|
+
let(:module_org) { "coolorg" }
|
33
|
+
let(:module_name) { "coolmod" }
|
34
|
+
let(:title) { "#{module_org}-#{module_name}" }
|
35
|
+
let(:dirname) { Pathname.new(Dir.mktmpdir) }
|
36
|
+
let(:spec_path) { dirname + module_name + 'spec' }
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
logger = double("logger")
|
40
|
+
allow_any_instance_of(described_class).to receive(:logger).and_return(logger)
|
41
|
+
allow(logger).to receive(:debug2).with(any_args)
|
42
|
+
allow(logger).to receive(:info).with(any_args)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not remove the spec directory by default' do
|
46
|
+
FileUtils.mkdir_p(spec_path)
|
47
|
+
m = described_class.new(title, dirname, {})
|
48
|
+
m.maybe_delete_spec_dir
|
49
|
+
expect(Dir.exist?(spec_path)).to eq true
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'detects a symlink and deletes the target' do
|
53
|
+
Dir.mkdir(dirname + module_name)
|
54
|
+
target_dir = Dir.mktmpdir
|
55
|
+
FileUtils.ln_s(target_dir, spec_path)
|
56
|
+
m = described_class.new(title, dirname, {exclude_spec: true})
|
57
|
+
m.maybe_delete_spec_dir
|
58
|
+
expect(Dir.exist?(target_dir)).to eq false
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'removes the spec directory if exclude_spec is set' do
|
62
|
+
FileUtils.mkdir_p(spec_path)
|
63
|
+
m = described_class.new(title, dirname, {exclude_spec: true})
|
64
|
+
m.maybe_delete_spec_dir
|
65
|
+
expect(Dir.exist?(spec_path)).to eq false
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'does not remove the spec directory if spec_deletable is false' do
|
69
|
+
FileUtils.mkdir_p(spec_path)
|
70
|
+
m = described_class.new(title, dirname, {})
|
71
|
+
m.spec_deletable = false
|
72
|
+
m.maybe_delete_spec_dir
|
73
|
+
expect(Dir.exist?(spec_path)).to eq true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
31
77
|
describe "path variables" do
|
32
78
|
it "uses the module name as the name" do
|
33
79
|
m = described_class.new('eight_hundred', '/moduledir', [])
|
@@ -78,6 +78,7 @@ describe R10K::Module::Forge do
|
|
78
78
|
allow_any_instance_of(described_class).to receive(:logger).and_return(logger_dbl)
|
79
79
|
|
80
80
|
allow(logger_dbl).to receive(:info).with(/Deploying module to.*/)
|
81
|
+
allow(logger_dbl).to receive(:debug2).with(/No spec dir detected/)
|
81
82
|
expect(logger_dbl).to receive(:warn).with(/puppet forge module.*puppetlabs-corosync.*has been deprecated/i)
|
82
83
|
|
83
84
|
subject.sync
|
@@ -90,6 +91,7 @@ describe R10K::Module::Forge do
|
|
90
91
|
allow_any_instance_of(described_class).to receive(:logger).and_return(logger_dbl)
|
91
92
|
|
92
93
|
allow(logger_dbl).to receive(:info).with(/Deploying module to.*/)
|
94
|
+
allow(logger_dbl).to receive(:debug2).with(/No spec dir detected/)
|
93
95
|
expect(logger_dbl).to_not receive(:warn).with(/puppet forge module.*puppetlabs-corosync.*has been deprecated/i)
|
94
96
|
|
95
97
|
subject.sync
|
@@ -104,9 +106,16 @@ describe R10K::Module::Forge do
|
|
104
106
|
|
105
107
|
it "uses the latest version from the forge when the version is :latest" do
|
106
108
|
subject = described_class.new('branan/eight_hundred', fixture_modulepath, { version: :latest })
|
107
|
-
|
109
|
+
release = double("Module Release", version: '8.8.8')
|
110
|
+
expect(subject.v3_module).to receive(:current_release).and_return(release).twice
|
108
111
|
expect(subject.expected_version).to eq '8.8.8'
|
109
112
|
end
|
113
|
+
|
114
|
+
it "throws when there are no available versions" do
|
115
|
+
subject = described_class.new('branan/eight_hundred', fixture_modulepath, { version: :latest })
|
116
|
+
expect(subject.v3_module).to receive(:current_release).and_return(nil)
|
117
|
+
expect { subject.expected_version }.to raise_error(PuppetForge::ReleaseNotFound)
|
118
|
+
end
|
110
119
|
end
|
111
120
|
|
112
121
|
describe "determining the status" do
|
@@ -158,6 +167,23 @@ describe R10K::Module::Forge do
|
|
158
167
|
describe "#sync" do
|
159
168
|
subject { described_class.new('branan/eight_hundred', fixture_modulepath, { version: '8.0.0' }) }
|
160
169
|
|
170
|
+
context "syncing the repo" do
|
171
|
+
let(:module_org) { "coolorg" }
|
172
|
+
let(:module_name) { "coolmod" }
|
173
|
+
let(:title) { "#{module_org}-#{module_name}" }
|
174
|
+
let(:dirname) { Pathname.new(Dir.mktmpdir) }
|
175
|
+
let(:spec_path) { dirname + module_name + 'spec' }
|
176
|
+
subject { described_class.new(title, dirname, {}) }
|
177
|
+
|
178
|
+
it 'defaults to keeping the spec dir' do
|
179
|
+
FileUtils.mkdir_p(spec_path)
|
180
|
+
expect(subject).to receive(:status).and_return(:absent)
|
181
|
+
expect(subject).to receive(:install)
|
182
|
+
subject.sync
|
183
|
+
expect(Dir.exist?(spec_path)).to eq true
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
161
187
|
it 'does nothing when the module is in sync' do
|
162
188
|
allow(subject).to receive(:status).and_return :insync
|
163
189
|
|
@@ -89,6 +89,23 @@ describe R10K::Module::Git do
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
describe 'syncing the repo' do
|
93
|
+
let(:module_org) { "coolorg" }
|
94
|
+
let(:module_name) { "coolmod" }
|
95
|
+
let(:title) { "#{module_org}-#{module_name}" }
|
96
|
+
let(:dirname) { Pathname.new(Dir.mktmpdir) }
|
97
|
+
let(:spec_path) { dirname + module_name + 'spec' }
|
98
|
+
subject { described_class.new(title, dirname, {}) }
|
99
|
+
|
100
|
+
it 'defaults to keeping the spec dir' do
|
101
|
+
FileUtils.mkdir_p(spec_path)
|
102
|
+
allow(mock_repo).to receive(:resolve).with('master').and_return('abc123')
|
103
|
+
allow(mock_repo).to receive(:sync)
|
104
|
+
subject.sync
|
105
|
+
expect(Dir.exist?(spec_path)).to eq true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
92
109
|
describe "determining the status" do
|
93
110
|
subject do
|
94
111
|
described_class.new(
|
@@ -119,14 +136,6 @@ describe R10K::Module::Git do
|
|
119
136
|
allow(mock_repo).to receive(:head).and_return('abc123')
|
120
137
|
end
|
121
138
|
|
122
|
-
context "when option is unrecognized" do
|
123
|
-
let(:opts) { { unrecognized: true } }
|
124
|
-
|
125
|
-
it "raises an error" do
|
126
|
-
expect { test_module(opts) }.to raise_error(ArgumentError, /cannot handle option 'unrecognized'/)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
139
|
describe "desired ref" do
|
131
140
|
context "when no desired ref is given" do
|
132
141
|
it "defaults to master" do
|
@@ -119,6 +119,24 @@ describe R10K::Module::SVN do
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
describe 'the default spec dir' do
|
123
|
+
let(:module_org) { "coolorg" }
|
124
|
+
let(:module_name) { "coolmod" }
|
125
|
+
let(:title) { "#{module_org}-#{module_name}" }
|
126
|
+
let(:dirname) { Pathname.new(Dir.mktmpdir) }
|
127
|
+
let(:spec_path) { dirname + module_name + 'spec' }
|
128
|
+
subject { described_class.new(title, dirname, {}) }
|
129
|
+
|
130
|
+
it 'is kept by default' do
|
131
|
+
|
132
|
+
FileUtils.mkdir_p(spec_path)
|
133
|
+
expect(subject).to receive(:status).and_return(:absent)
|
134
|
+
expect(subject).to receive(:install).and_return(nil)
|
135
|
+
subject.sync
|
136
|
+
expect(Dir.exist?(spec_path)).to eq true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
122
140
|
describe "synchronizing" do
|
123
141
|
|
124
142
|
subject { described_class.new('foo', '/moduledir', :svn => 'https://github.com/adrienthebo/r10k-fixture-repo', :rev => 123) }
|
@@ -0,0 +1,343 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'r10k/module_loader/puppetfile'
|
3
|
+
|
4
|
+
describe R10K::ModuleLoader::Puppetfile do
|
5
|
+
describe 'initial parameters' do
|
6
|
+
describe 'honor' do
|
7
|
+
let(:options) do
|
8
|
+
{
|
9
|
+
basedir: '/test/basedir/env',
|
10
|
+
forge: 'localforge.internal.corp',
|
11
|
+
overrides: { modules: { deploy_modules: true } },
|
12
|
+
environment: R10K::Environment::Git.new('env',
|
13
|
+
'/test/basedir/',
|
14
|
+
'env',
|
15
|
+
{ remote: 'git://foo/remote',
|
16
|
+
ref: 'env' })
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
subject { R10K::ModuleLoader::Puppetfile.new(**options) }
|
21
|
+
|
22
|
+
describe 'the moduledir' do
|
23
|
+
it 'respects absolute paths' do
|
24
|
+
absolute_options = options.merge({moduledir: '/opt/puppetlabs/special/modules'})
|
25
|
+
puppetfile = R10K::ModuleLoader::Puppetfile.new(**absolute_options)
|
26
|
+
expect(puppetfile.instance_variable_get(:@moduledir)).to eq('/opt/puppetlabs/special/modules')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'roots the moduledir in the basepath if a relative path is specified' do
|
30
|
+
relative_options = options.merge({moduledir: 'my/special/modules'})
|
31
|
+
puppetfile = R10K::ModuleLoader::Puppetfile.new(**relative_options)
|
32
|
+
expect(puppetfile.instance_variable_get(:@moduledir)).to eq('/test/basedir/env/my/special/modules')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'the Puppetfile' do
|
37
|
+
it 'respects absolute paths' do
|
38
|
+
absolute_options = options.merge({puppetfile: '/opt/puppetlabs/special/Puppetfile'})
|
39
|
+
puppetfile = R10K::ModuleLoader::Puppetfile.new(**absolute_options)
|
40
|
+
expect(puppetfile.instance_variable_get(:@puppetfile_path)).to eq('/opt/puppetlabs/special/Puppetfile')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'roots the Puppetfile in the basepath if a relative path is specified' do
|
44
|
+
relative_options = options.merge({puppetfile: 'Puppetfile.global'})
|
45
|
+
puppetfile = R10K::ModuleLoader::Puppetfile.new(**relative_options)
|
46
|
+
expect(puppetfile.instance_variable_get(:@puppetfile_path)).to eq('/test/basedir/env/Puppetfile.global')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'the forge' do
|
51
|
+
expect(subject.instance_variable_get(:@forge)).to eq('localforge.internal.corp')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'the overrides' do
|
55
|
+
expect(subject.instance_variable_get(:@overrides)).to eq({ modules: { deploy_modules: true }})
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'the environment' do
|
59
|
+
expect(subject.instance_variable_get(:@environment).name).to eq('env')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'sane defaults' do
|
64
|
+
subject { R10K::ModuleLoader::Puppetfile.new(basedir: '/test/basedir') }
|
65
|
+
|
66
|
+
it 'has a moduledir rooted in the basedir' do
|
67
|
+
expect(subject.instance_variable_get(:@moduledir)).to eq('/test/basedir/modules')
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'has a Puppetfile rooted in the basedir' do
|
71
|
+
expect(subject.instance_variable_get(:@puppetfile_path)).to eq('/test/basedir/Puppetfile')
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'uses the public forge' do
|
75
|
+
expect(subject.instance_variable_get(:@forge)).to eq('forgeapi.puppetlabs.com')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'creates an empty overrides' do
|
79
|
+
expect(subject.instance_variable_get(:@overrides)).to eq({})
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'does not require an environment' do
|
83
|
+
expect(subject.instance_variable_get(:@environment)).to eq(nil)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'adding modules' do
|
89
|
+
let(:basedir) { '/test/basedir' }
|
90
|
+
|
91
|
+
subject { R10K::ModuleLoader::Puppetfile.new(basedir: basedir) }
|
92
|
+
|
93
|
+
it 'should transform Forge modules with a string arg to have a version key' do
|
94
|
+
expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
|
95
|
+
|
96
|
+
expect { subject.add_module('puppet/test_module', '1.2.3') }.to change { subject.modules }
|
97
|
+
expect(subject.modules.collect(&:name)).to include('test_module')
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should not accept Forge modules with a version comparison' do
|
101
|
+
expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '< 1.2.0'), anything).and_call_original
|
102
|
+
|
103
|
+
expect {
|
104
|
+
subject.add_module('puppet/test_module', '< 1.2.0')
|
105
|
+
}.to raise_error(RuntimeError, /module puppet\/test_module.*doesn't have an implementation/i)
|
106
|
+
|
107
|
+
expect(subject.modules.collect(&:name)).not_to include('test_module')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'should set :spec_deletable to true for modules in the basedir' do
|
111
|
+
module_opts = { git: 'git@example.com:puppet/test_module.git' }
|
112
|
+
subject.add_module('puppet/test_module', module_opts)
|
113
|
+
expect(subject.modules[0].spec_deletable).to be true
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should set :spec_deletable to false for modules outside the basedir' do
|
117
|
+
module_opts = { git: 'git@example.com:puppet/test_module.git', install_path: 'some/path' }
|
118
|
+
subject.add_module('puppet/test_module', module_opts)
|
119
|
+
expect(subject.modules[0].spec_deletable).to be false
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should accept non-Forge modules with a hash arg' do
|
123
|
+
module_opts = { git: 'git@example.com:puppet/test_module.git' }
|
124
|
+
|
125
|
+
expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, module_opts, anything).and_call_original
|
126
|
+
|
127
|
+
expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
|
128
|
+
expect(subject.modules.collect(&:name)).to include('test_module')
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should accept non-Forge modules with a valid relative :install_path option' do
|
132
|
+
module_opts = {
|
133
|
+
install_path: 'vendor',
|
134
|
+
git: 'git@example.com:puppet/test_module.git',
|
135
|
+
}
|
136
|
+
|
137
|
+
expect(R10K::Module).to receive(:new).with('puppet/test_module', File.join(basedir, 'vendor'), module_opts, anything).and_call_original
|
138
|
+
|
139
|
+
expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
|
140
|
+
expect(subject.modules.collect(&:name)).to include('test_module')
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should accept non-Forge modules with a valid absolute :install_path option' do
|
144
|
+
install_path = File.join(basedir, 'vendor')
|
145
|
+
|
146
|
+
module_opts = {
|
147
|
+
install_path: install_path,
|
148
|
+
git: 'git@example.com:puppet/test_module.git',
|
149
|
+
}
|
150
|
+
|
151
|
+
expect(R10K::Module).to receive(:new).with('puppet/test_module', install_path, module_opts, anything).and_call_original
|
152
|
+
|
153
|
+
expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
|
154
|
+
expect(subject.modules.collect(&:name)).to include('test_module')
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should reject non-Forge modules with an invalid relative :install_path option' do
|
158
|
+
module_opts = {
|
159
|
+
install_path: '../../vendor',
|
160
|
+
git: 'git@example.com:puppet/test_module.git',
|
161
|
+
}
|
162
|
+
|
163
|
+
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 }
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should reject non-Forge modules with an invalid absolute :install_path option' do
|
167
|
+
module_opts = {
|
168
|
+
install_path: '/tmp/mydata/vendor',
|
169
|
+
git: 'git@example.com:puppet/test_module.git',
|
170
|
+
}
|
171
|
+
|
172
|
+
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 }
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should disable and not add modules that conflict with the environment' do
|
176
|
+
env = instance_double('R10K::Environment::Base')
|
177
|
+
mod = instance_double('R10K::Module::Base', name: 'conflict', origin: :puppetfile, 'origin=': nil)
|
178
|
+
loader = R10K::ModuleLoader::Puppetfile.new(basedir: basedir, environment: env)
|
179
|
+
allow(env).to receive(:'module_conflicts?').with(mod).and_return(true)
|
180
|
+
allow(mod).to receive(:spec_deletable=)
|
181
|
+
|
182
|
+
expect(R10K::Module).to receive(:new).with('conflict', anything, anything, anything).and_return(mod)
|
183
|
+
expect { loader.add_module('conflict', {}) }.not_to change { loader.modules }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe '#purge_exclusions' do
|
188
|
+
let(:managed_dirs) { ['dir1', 'dir2'] }
|
189
|
+
subject { R10K::ModuleLoader::Puppetfile.new(basedir: '/test/basedir') }
|
190
|
+
|
191
|
+
it 'includes managed_directories' do
|
192
|
+
expect(subject.send(:determine_purge_exclusions, managed_dirs)).to match_array(managed_dirs)
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'when belonging to an environment' do
|
196
|
+
let(:env_contents) { ['env1', 'env2' ] }
|
197
|
+
let(:env) { double(:environment, desired_contents: env_contents) }
|
198
|
+
|
199
|
+
subject { R10K::ModuleLoader::Puppetfile.new(basedir: '/test/basedir', environment: env) }
|
200
|
+
|
201
|
+
it "includes environment's desired_contents" do
|
202
|
+
expect(subject.send(:determine_purge_exclusions, managed_dirs)).to match_array(managed_dirs + env_contents)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe '#managed_directories' do
|
208
|
+
|
209
|
+
let(:basedir) { '/test/basedir' }
|
210
|
+
subject { R10K::ModuleLoader::Puppetfile.new(basedir: basedir) }
|
211
|
+
|
212
|
+
before do
|
213
|
+
allow(subject).to receive(:puppetfile_content).and_return('')
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'returns an array of paths that #purge! will operate within' do
|
217
|
+
expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
|
218
|
+
subject.add_module('puppet/test_module', '1.2.3')
|
219
|
+
subject.load
|
220
|
+
|
221
|
+
expect(subject.modules.length).to be 1
|
222
|
+
expect(subject.managed_directories).to match_array([subject.moduledir])
|
223
|
+
end
|
224
|
+
|
225
|
+
context "with a module with install_path == ''" do
|
226
|
+
it "basedir isn't in the list of paths to purge" do
|
227
|
+
module_opts = { install_path: '', git: 'git@example.com:puppet/test_module.git' }
|
228
|
+
|
229
|
+
expect(R10K::Module).to receive(:new).with('puppet/test_module', basedir, module_opts, anything).and_call_original
|
230
|
+
subject.add_module('puppet/test_module', module_opts)
|
231
|
+
subject.load
|
232
|
+
|
233
|
+
expect(subject.modules.length).to be 1
|
234
|
+
expect(subject.managed_directories).to be_empty
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe 'evaluating a Puppetfile' do
|
240
|
+
def expect_wrapped_error(error, pf_path, error_type)
|
241
|
+
expect(error).to be_a_kind_of(R10K::Error)
|
242
|
+
expect(error.message).to eq("Failed to evaluate #{pf_path}")
|
243
|
+
expect(error.original).to be_a_kind_of(error_type)
|
244
|
+
end
|
245
|
+
|
246
|
+
subject { described_class.new(basedir: @path) }
|
247
|
+
|
248
|
+
it 'wraps and re-raises syntax errors' do
|
249
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'invalid-syntax')
|
250
|
+
pf_path = File.join(@path, 'Puppetfile')
|
251
|
+
expect {
|
252
|
+
subject.load
|
253
|
+
}.to raise_error do |e|
|
254
|
+
expect_wrapped_error(e, pf_path, SyntaxError)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'wraps and re-raises load errors' do
|
259
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'load-error')
|
260
|
+
pf_path = File.join(@path, 'Puppetfile')
|
261
|
+
expect {
|
262
|
+
subject.load
|
263
|
+
}.to raise_error do |e|
|
264
|
+
expect_wrapped_error(e, pf_path, LoadError)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'wraps and re-raises argument errors' do
|
269
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'argument-error')
|
270
|
+
pf_path = File.join(@path, 'Puppetfile')
|
271
|
+
expect {
|
272
|
+
subject.load
|
273
|
+
}.to raise_error do |e|
|
274
|
+
expect_wrapped_error(e, pf_path, ArgumentError)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'rejects Puppetfiles with duplicate module names' do
|
279
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'duplicate-module-error')
|
280
|
+
pf_path = File.join(@path, 'Puppetfile')
|
281
|
+
expect {
|
282
|
+
subject.load
|
283
|
+
}.to raise_error(R10K::Error, /Puppetfiles cannot contain duplicate module names/i)
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'wraps and re-raises name errors' do
|
287
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'name-error')
|
288
|
+
pf_path = File.join(@path, 'Puppetfile')
|
289
|
+
expect {
|
290
|
+
subject.load
|
291
|
+
}.to raise_error do |e|
|
292
|
+
expect_wrapped_error(e, pf_path, NameError)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'accepts a forge module with a version' do
|
297
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
|
298
|
+
pf_path = File.join(@path, 'Puppetfile')
|
299
|
+
expect { subject.load }.not_to raise_error
|
300
|
+
end
|
301
|
+
|
302
|
+
describe 'setting a custom moduledir' do
|
303
|
+
it 'allows setting an absolute moduledir' do
|
304
|
+
@path = '/fake/basedir'
|
305
|
+
allow(subject).to receive(:puppetfile_content).and_return('moduledir "/fake/moduledir"')
|
306
|
+
subject.load
|
307
|
+
expect(subject.instance_variable_get(:@moduledir)).to eq('/fake/moduledir')
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'roots relative moduledirs in the basedir' do
|
311
|
+
@path = '/fake/basedir'
|
312
|
+
allow(subject).to receive(:puppetfile_content).and_return('moduledir "my/moduledir"')
|
313
|
+
subject.load
|
314
|
+
expect(subject.instance_variable_get(:@moduledir)).to eq(File.join(@path, 'my/moduledir'))
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
it 'accepts a forge module without a version' do
|
319
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-without-version')
|
320
|
+
pf_path = File.join(@path, 'Puppetfile')
|
321
|
+
expect { subject.load }.not_to raise_error
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'creates a git module and applies the default branch specified in the Puppetfile' do
|
325
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'default-branch-override')
|
326
|
+
pf_path = File.join(@path, 'Puppetfile')
|
327
|
+
expect { subject.load }.not_to raise_error
|
328
|
+
git_module = subject.modules[0]
|
329
|
+
expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'creates a git module and applies the provided default_branch_override' do
|
333
|
+
@path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'default-branch-override')
|
334
|
+
pf_path = File.join(@path, 'Puppetfile')
|
335
|
+
default_branch_override = 'default_branch_override_name'
|
336
|
+
subject.default_branch_override = default_branch_override
|
337
|
+
expect { subject.load }.not_to raise_error
|
338
|
+
git_module = subject.modules[0]
|
339
|
+
expect(git_module.default_override_ref).to eq default_branch_override
|
340
|
+
expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|