chef-apply 0.4.6 → 0.4.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -7
- data/chef-apply.gemspec +3 -3
- data/lib/chef_apply/version.rb +1 -1
- metadata +3 -38
- data/README.md +0 -62
- data/spec/fixtures/custom_config.toml +0 -2
- data/spec/integration/chef-run_spec.rb +0 -41
- data/spec/integration/fixtures/chef_help.out +0 -70
- data/spec/integration/fixtures/chef_version.out +0 -1
- data/spec/integration/spec_helper.rb +0 -55
- data/spec/spec_helper.rb +0 -154
- data/spec/support/matchers/output_to_terminal.rb +0 -36
- data/spec/unit/action/base_spec.rb +0 -60
- data/spec/unit/action/converge_target/ccr_failure_mapper_spec.rb +0 -106
- data/spec/unit/action/converge_target_spec.rb +0 -400
- data/spec/unit/action/generate_local_policy_spec.rb +0 -114
- data/spec/unit/action/generate_temp_cookbook/recipe_lookup_spec.rb +0 -122
- data/spec/unit/action/generate_temp_cookbook/temp_cookbook_spec.rb +0 -198
- data/spec/unit/action/generate_temp_cookbook_spec.rb +0 -73
- data/spec/unit/action/install_chef/minimum_chef_version_spec.rb +0 -90
- data/spec/unit/action/install_chef_spec.rb +0 -164
- data/spec/unit/cli/options_spec.rb +0 -75
- data/spec/unit/cli/validation_spec.rb +0 -81
- data/spec/unit/cli_spec.rb +0 -475
- data/spec/unit/config_spec.rb +0 -70
- data/spec/unit/file_fetcher_spec.rb +0 -40
- data/spec/unit/fixtures/multi-error.out +0 -2
- data/spec/unit/log_spec.rb +0 -37
- data/spec/unit/startup_spec.rb +0 -323
- data/spec/unit/target_host/linux_spec.rb +0 -57
- data/spec/unit/target_host/windows_spec.rb +0 -43
- data/spec/unit/target_host_spec.rb +0 -297
- data/spec/unit/target_resolver_spec.rb +0 -380
- data/spec/unit/telemeter/sender_spec.rb +0 -140
- data/spec/unit/telemeter_spec.rb +0 -191
- data/spec/unit/text/error_translation_spec.rb +0 -109
- data/spec/unit/ui/error_printer_spec.rb +0 -196
- data/spec/unit/ui/terminal_spec.rb +0 -119
- data/spec/unit/version_spec.rb +0 -31
@@ -1,57 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "chef_apply/target_host"
|
3
|
-
require "chef_apply/target_host/linux"
|
4
|
-
|
5
|
-
RSpec.describe ChefApply::TargetHost::Linux do
|
6
|
-
let(:user) { "testuser" }
|
7
|
-
let(:host) { "mock://#{user}@example.com" }
|
8
|
-
let(:family) { "linux" }
|
9
|
-
let(:name) { "linux" }
|
10
|
-
let(:path) { "/tmp/blah" }
|
11
|
-
|
12
|
-
subject do
|
13
|
-
ChefApply::TargetHost.mock_instance(host, family: family, name: name)
|
14
|
-
end
|
15
|
-
|
16
|
-
context "#make_temp_dir" do
|
17
|
-
it "creates the directory using a properly formed make_temp_dir" do
|
18
|
-
expect(subject).to receive(:run_command!)
|
19
|
-
.with("bash -c '#{ChefApply::TargetHost::Linux::MKTEMP_COMMAND}'")
|
20
|
-
.and_return(instance_double("result", stdout: "/tmp/blah"))
|
21
|
-
expect(subject.make_temp_dir).to eq "/tmp/blah"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context "#mkdir" do
|
26
|
-
it "uses a properly formed mkdir to create the directory and changes ownership to connected user" do
|
27
|
-
expect(subject).to receive(:run_command!).with("mkdir -p /tmp/dir")
|
28
|
-
subject.mkdir("/tmp/dir")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context "#chown" do
|
33
|
-
it "uses a properly formed chown to change owning user to the provided user" do
|
34
|
-
expect(subject).to receive(:run_command!).with("chown newowner '/tmp/dir'")
|
35
|
-
subject.chown("/tmp/dir", "newowner")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
context "#install_package" do
|
40
|
-
context "when it receives an RPM package" do
|
41
|
-
let(:expected_command) { "rpm -Uvh /my/package.rpm" }
|
42
|
-
it "should run the correct rpm command" do
|
43
|
-
expect(subject).to receive(:run_command!).with expected_command
|
44
|
-
subject.install_package("/my/package.rpm")
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
context "when it receives a DEB package" do
|
50
|
-
let(:expected_command) { "dpkg -i /my/package.deb" }
|
51
|
-
it "should run the correct dpkg command" do
|
52
|
-
expect(subject).to receive(:run_command!).with expected_command
|
53
|
-
subject.install_package("/my/package.deb")
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
|
2
|
-
require "spec_helper"
|
3
|
-
require "chef_apply/target_host"
|
4
|
-
require "chef_apply/target_host/windows"
|
5
|
-
|
6
|
-
RSpec.describe ChefApply::TargetHost::Windows do
|
7
|
-
let(:host) { "mock://user@example.com" }
|
8
|
-
let(:family) { "windows" }
|
9
|
-
let(:name) { "windows" }
|
10
|
-
let(:path) { "C:\\temp\\blah" }
|
11
|
-
|
12
|
-
subject do
|
13
|
-
ChefApply::TargetHost.mock_instance(host, family: family, name: name)
|
14
|
-
end
|
15
|
-
|
16
|
-
context "#make_temp_dir" do
|
17
|
-
it "creates the temporary directory using the correct PowerShell command and returns the path" do
|
18
|
-
expect(subject).to receive(:run_command!)
|
19
|
-
.with(ChefApply::TargetHost::Windows::MKTEMP_COMMAND)
|
20
|
-
.and_return(instance_double("result", stdout: path))
|
21
|
-
expect(subject.make_temp_dir).to eq(path)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context "#mkdir" do
|
26
|
-
it "creates the directory using the correct command PowerShell command" do
|
27
|
-
expect(subject).to receive(:run_command!).with("New-Item -ItemType Directory -Force -Path C:\\temp\\dir")
|
28
|
-
subject.mkdir("C:\\temp\\dir")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context "#chown" do
|
33
|
-
xit "does nothing - this is not implemented on Windows until we need it"
|
34
|
-
end
|
35
|
-
|
36
|
-
context "#install_package" do
|
37
|
-
it "runs the correct MSI package install command" do
|
38
|
-
expected_command = "cmd /c msiexec /package C:\\My\\Package.msi /quiet"
|
39
|
-
expect(subject).to receive(:run_command!).with(expected_command)
|
40
|
-
subject.install_package("C:/My/Package.msi")
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,297 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Copyright:: Copyright (c) 2018-2019 Chef Software Inc.
|
3
|
-
# License:: Apache License, Version 2.0
|
4
|
-
#
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
-
# you may not use this file except in compliance with the License.
|
7
|
-
# You may obtain a copy of the License at
|
8
|
-
#
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
-
#
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
-
# See the License for the specific language governing permissions and
|
15
|
-
# limitations under the License.
|
16
|
-
#
|
17
|
-
|
18
|
-
require "spec_helper"
|
19
|
-
require "ostruct"
|
20
|
-
require "chef_apply/target_host"
|
21
|
-
|
22
|
-
RSpec.describe ChefApply::TargetHost do
|
23
|
-
let(:host) { "mock://user@example.com" }
|
24
|
-
let(:family) { "debian" }
|
25
|
-
let(:name) { "ubuntu" }
|
26
|
-
|
27
|
-
subject do
|
28
|
-
ChefApply::TargetHost.mock_instance(host, family: family, name: name)
|
29
|
-
end
|
30
|
-
|
31
|
-
context "#base_os" do
|
32
|
-
context "for a windows os" do
|
33
|
-
let(:family) { "windows" }
|
34
|
-
let(:name) { "windows" }
|
35
|
-
it "reports :windows" do
|
36
|
-
expect(subject.base_os).to eq :windows
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context "for a linux os" do
|
41
|
-
let(:family) { "debian" }
|
42
|
-
let(:name) { "ubuntu" }
|
43
|
-
it "reports :linux" do
|
44
|
-
expect(subject.base_os).to eq :linux
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
context "for an unsupported OS" do
|
49
|
-
let(:family) { "unknown" }
|
50
|
-
let(:name) { "unknown" }
|
51
|
-
it "reports :other" do
|
52
|
-
expect(subject.base_os).to eq :other
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context "#installed_chef_version" do
|
58
|
-
context "when no version manifest is present" do
|
59
|
-
it "raises ChefNotInstalled" do
|
60
|
-
expect(subject).to receive(:read_chef_version_manifest).and_raise(ChefApply::TargetHost::ChefNotInstalled.new)
|
61
|
-
expect { subject.installed_chef_version }.to raise_error(ChefApply::TargetHost::ChefNotInstalled)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context "when version manifest is present" do
|
66
|
-
let(:manifest) { { "build_version" => "14.0.1" } }
|
67
|
-
it "reports version based on the build_version field" do
|
68
|
-
expect(subject).to receive(:read_chef_version_manifest).and_return manifest
|
69
|
-
expect(subject.installed_chef_version).to eq Gem::Version.new("14.0.1")
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
context "connect!" do
|
75
|
-
# For all other tets, target_host is a mocked instance that is already connected
|
76
|
-
# In this case, we want to build a new one that is not yet connected to test connect! itself.
|
77
|
-
let(:target_host) { ChefApply::TargetHost.new(host, sudo: false) }
|
78
|
-
let(:train_connection_mock) { double("train connection") }
|
79
|
-
before do
|
80
|
-
allow(target_host).to receive(:train_connection).and_return(train_connection_mock)
|
81
|
-
end
|
82
|
-
context "when an Train::UserError occurs" do
|
83
|
-
it "raises a ConnectionFailure" do
|
84
|
-
allow(train_connection_mock).to receive(:connection).and_raise Train::UserError
|
85
|
-
expect { target_host.connect! }.to raise_error(ChefApply::TargetHost::ConnectionFailure)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
context "when a Train::Error occurs" do
|
89
|
-
it "raises a ConnectionFailure" do
|
90
|
-
allow(train_connection_mock).to receive(:connection).and_raise Train::Error
|
91
|
-
expect { target_host.connect! }.to raise_error(ChefApply::TargetHost::ConnectionFailure)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context "#mix_in_target_platform!" do
|
97
|
-
let(:base_os) { :none }
|
98
|
-
before do
|
99
|
-
allow(subject).to receive(:base_os).and_return base_os
|
100
|
-
end
|
101
|
-
|
102
|
-
context "when base_os is linux" do
|
103
|
-
let(:base_os) { :linux }
|
104
|
-
it "mixes in Linux support" do
|
105
|
-
expect(subject.class).to receive(:include).with(ChefApply::TargetHost::Linux)
|
106
|
-
subject.mix_in_target_platform!
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
context "when base_os is windows" do
|
111
|
-
let(:base_os) { :windows }
|
112
|
-
it "mixes in Windows support" do
|
113
|
-
expect(subject.class).to receive(:include).with(ChefApply::TargetHost::Windows)
|
114
|
-
subject.mix_in_target_platform!
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
context "when base_os is other" do
|
119
|
-
let(:base_os) { :other }
|
120
|
-
it "raises UnsupportedTargetOS" do
|
121
|
-
expect { subject.mix_in_target_platform! }.to raise_error(ChefApply::TargetHost::UnsupportedTargetOS)
|
122
|
-
end
|
123
|
-
|
124
|
-
end
|
125
|
-
context "after it connects" do
|
126
|
-
context "to a Windows host" do
|
127
|
-
it "includes the Windows TargetHost mixin" do
|
128
|
-
end
|
129
|
-
|
130
|
-
end
|
131
|
-
|
132
|
-
context "and the platform is linux" do
|
133
|
-
it "includes the Windows TargetHost mixin" do
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
end
|
140
|
-
|
141
|
-
context "#user" do
|
142
|
-
before do
|
143
|
-
allow(subject).to receive(:config).and_return(user: user)
|
144
|
-
end
|
145
|
-
context "when a user has been configured" do
|
146
|
-
let(:user) { "testuser" }
|
147
|
-
it "returns that user" do
|
148
|
-
expect(subject.user).to eq user
|
149
|
-
end
|
150
|
-
end
|
151
|
-
context "when no user has been configured" do
|
152
|
-
let(:user) { nil }
|
153
|
-
it "returns the correct default from train" do
|
154
|
-
expect(subject.user).to eq Train::Transports::SSH.default_options[:user][:default]
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
context "#run_command!" do
|
160
|
-
let(:backend) { double("backend") }
|
161
|
-
let(:exit_status) { 0 }
|
162
|
-
let(:result) { RemoteExecResult.new(exit_status, "", "an error occurred") }
|
163
|
-
let(:command) { "cmd" }
|
164
|
-
|
165
|
-
before do
|
166
|
-
allow(subject).to receive(:backend).and_return(backend)
|
167
|
-
allow(backend).to receive(:run_command).with(command).and_return(result)
|
168
|
-
end
|
169
|
-
|
170
|
-
context "when no error occurs" do
|
171
|
-
let(:exit_status) { 0 }
|
172
|
-
it "returns the result" do
|
173
|
-
expect(subject.run_command!(command)).to eq result
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
context "when an error occurs" do
|
178
|
-
let(:exit_status) { 1 }
|
179
|
-
it "raises a RemoteExecutionFailed error" do
|
180
|
-
expected_error = ChefApply::TargetHost::RemoteExecutionFailed
|
181
|
-
expect { subject.run_command!(command) }.to raise_error(expected_error)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
context "#read_chef_version_manifest" do
|
187
|
-
let(:manifest_content) { '{"build_version" : "1.2.3"}' }
|
188
|
-
before do
|
189
|
-
allow(subject).to receive(:fetch_file_contents).and_return(manifest_content)
|
190
|
-
allow(subject).to receive(:omnibus_manifest_path).and_return("/path/to/manifest.json")
|
191
|
-
end
|
192
|
-
|
193
|
-
context "when manifest is missing" do
|
194
|
-
let(:manifest_content) { nil }
|
195
|
-
it "raises ChefNotInstalled" do
|
196
|
-
expect { subject.read_chef_version_manifest }.to raise_error(ChefApply::TargetHost::ChefNotInstalled)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
context "when manifest is present" do
|
201
|
-
let(:manifest_content) { '{"build_version" : "1.2.3"}' }
|
202
|
-
it "should return the parsed manifest" do
|
203
|
-
expect(subject.read_chef_version_manifest).to eq({ "build_version" => "1.2.3" })
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
# What we test:
|
209
|
-
# - file contents can be retrieved, and invalid conditions results in no content
|
210
|
-
# What we mock:
|
211
|
-
# - the train `backend`
|
212
|
-
# - the backend `file` method
|
213
|
-
# Why?
|
214
|
-
# - in this unit test, we're not testing round-trip behavior of the train API, only
|
215
|
-
# that we are invoking the API and interpreting its results correctly.
|
216
|
-
context "#fetch_file_contents" do
|
217
|
-
let(:path) { "/path/to/file" }
|
218
|
-
let(:sample_content) { "content" }
|
219
|
-
let(:backend_mock) { double("backend") }
|
220
|
-
let(:path_exists) { true }
|
221
|
-
let(:path_is_file) { true }
|
222
|
-
let(:remote_file_mock) do
|
223
|
-
double("remote_file", exist?: path_exists,
|
224
|
-
file?: path_is_file, content: sample_content)
|
225
|
-
end
|
226
|
-
before do
|
227
|
-
expect(subject).to receive(:backend).and_return backend_mock
|
228
|
-
expect(backend_mock).to receive(:file).with(path).and_return remote_file_mock
|
229
|
-
end
|
230
|
-
|
231
|
-
context "when path exists" do
|
232
|
-
let(:path_exists) { true }
|
233
|
-
before do
|
234
|
-
end
|
235
|
-
|
236
|
-
context "but is not a file" do
|
237
|
-
let(:path_is_file) { false }
|
238
|
-
it "returns nil" do
|
239
|
-
expect(subject.fetch_file_contents(path)).to be_nil
|
240
|
-
end
|
241
|
-
end
|
242
|
-
context "and is a file" do
|
243
|
-
it "returns the expected file contents" do
|
244
|
-
expect(subject.fetch_file_contents(path)).to eq sample_content
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
context "when path does not exist" do
|
249
|
-
let(:path_exists) { false }
|
250
|
-
it "returns nil" do
|
251
|
-
expect(subject.fetch_file_contents(path)).to be_nil
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
context "#apply_ssh_config" do
|
257
|
-
let(:ssh_host_config) { { user: "testuser", port: 1000, proxy: double("Net:SSH::Proxy::Command") } }
|
258
|
-
let(:connection_config) { { user: "user1", port: 8022, proxy: nil } }
|
259
|
-
before do
|
260
|
-
allow(subject).to receive(:ssh_config_for_host).and_return ssh_host_config
|
261
|
-
end
|
262
|
-
|
263
|
-
ChefApply::TargetHost::SSH_CONFIG_OVERRIDE_KEYS.each do |key|
|
264
|
-
context "when a value is not explicitly provided in options" do
|
265
|
-
it "replaces config config[:#{key}] with the ssh config value" do
|
266
|
-
subject.apply_ssh_config(connection_config, key => nil)
|
267
|
-
expect(connection_config[key]).to eq(ssh_host_config[key])
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
context "when a value is explicitly provided in options" do
|
272
|
-
it "the connection configuration isnot updated with a value from ssh config" do
|
273
|
-
original_config = connection_config.clone
|
274
|
-
subject.apply_ssh_config(connection_config, { key => "testvalue" } )
|
275
|
-
expect(connection_config[key]).to eq original_config[key]
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
context "#temp_dir" do
|
282
|
-
it "creates the temp directory and changes ownership" do
|
283
|
-
expect(subject).to receive(:make_temp_dir).and_return("/tmp/dir")
|
284
|
-
expect(subject).to receive(:chown).with("/tmp/dir", subject.user)
|
285
|
-
subject.temp_dir
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
context "#make_directory" do
|
290
|
-
it "creates the directory and sets ownership to connecting user" do
|
291
|
-
expect(subject).to receive(:mkdir).with("/tmp/mkdir")
|
292
|
-
expect(subject).to receive(:chown).with("/tmp/mkdir", subject.user)
|
293
|
-
subject.make_directory("/tmp/mkdir")
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
end
|
@@ -1,380 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Copyright:: Copyright (c) 2018 Chef Software Inc.
|
3
|
-
# License:: Apache License, Version 2.0
|
4
|
-
#
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
-
# you may not use this file except in compliance with the License.
|
7
|
-
# You may obtain a copy of the License at
|
8
|
-
#
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
-
#
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
-
# See the License for the specific language governing permissions and
|
15
|
-
# limitations under the License.
|
16
|
-
#
|
17
|
-
|
18
|
-
require "spec_helper"
|
19
|
-
require "chef_apply/target_resolver"
|
20
|
-
|
21
|
-
RSpec.describe ChefApply::TargetResolver do
|
22
|
-
let(:target_string) { "" }
|
23
|
-
let(:default_protocol) { "ssh" }
|
24
|
-
let(:connection_options) { {} }
|
25
|
-
subject { ChefApply::TargetResolver.new(target_string, default_protocol, connection_options) }
|
26
|
-
|
27
|
-
context "#targets" do
|
28
|
-
context "when no target is provided" do
|
29
|
-
let(:target_string) { "" }
|
30
|
-
it "returns an empty array" do
|
31
|
-
expect(subject.targets).to eq []
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context "when a single target is provided" do
|
36
|
-
let(:target_string) { "ssh://localhost" }
|
37
|
-
it "returns any array with one target" do
|
38
|
-
actual_targets = subject.targets
|
39
|
-
expect(actual_targets[0].config[:host]).to eq "localhost"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context "when a comma-separated list of targets is provided" do
|
44
|
-
let(:target_string) { "ssh://node1.example.com,winrm://node2.example.com" }
|
45
|
-
it "returns an array with correct TargetHost instances" do
|
46
|
-
actual_targets = subject.targets
|
47
|
-
expect(actual_targets[0].config[:host]).to eq "node1.example.com"
|
48
|
-
expect(actual_targets[1].config[:host]).to eq "node2.example.com"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
context "when a comma-separated list of targets that include ranges is provided" do
|
52
|
-
let(:target_string) { "ssh://node[0:1],ssh://machine[0:1]" }
|
53
|
-
it "returns an array with correct TargetHost instances" do
|
54
|
-
actual_targets = subject.targets
|
55
|
-
|
56
|
-
expect(actual_targets[0].config[:host]).to eq "node0"
|
57
|
-
expect(actual_targets[1].config[:host]).to eq "node1"
|
58
|
-
expect(actual_targets[2].config[:host]).to eq "machine0"
|
59
|
-
expect(actual_targets[3].config[:host]).to eq "machine1"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
context "when a mixed list of targets containing user prefix and not are included" do
|
64
|
-
|
65
|
-
let(:target_string) { "test_user1@node1,node2,test_user2:password@node3" }
|
66
|
-
|
67
|
-
context "and the :user option is provided" do
|
68
|
-
|
69
|
-
let(:connection_options) { { user: "defaultuser" } }
|
70
|
-
it "should default to the given :user only for the host that does not include name" do
|
71
|
-
actual_targets = subject.targets
|
72
|
-
tc = actual_targets[0].config
|
73
|
-
expect(tc[:host]).to eq "node1"
|
74
|
-
expect(tc[:user]).to eq "test_user1"
|
75
|
-
|
76
|
-
tc = actual_targets[1].config
|
77
|
-
expect(tc[:host]).to eq "node2"
|
78
|
-
expect(tc[:user]).to eq "defaultuser"
|
79
|
-
|
80
|
-
tc = actual_targets[2].config
|
81
|
-
expect(tc[:host]).to eq "node3"
|
82
|
-
expect(tc[:user]).to eq "test_user2"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
context "and the :user option is not provided" do
|
86
|
-
let(:opts) { {} }
|
87
|
-
it "should default to no user when user is not included with host" do
|
88
|
-
actual_targets = subject.targets
|
89
|
-
tc = actual_targets[0].config
|
90
|
-
expect(tc[:host]).to eq "node1"
|
91
|
-
expect(tc[:user]).to eq "test_user1"
|
92
|
-
|
93
|
-
tc = actual_targets[1].config
|
94
|
-
expect(tc[:host]).to eq "node2"
|
95
|
-
expect(tc[:user]).to eq nil
|
96
|
-
|
97
|
-
tc = actual_targets[2].config
|
98
|
-
expect(tc[:host]).to eq "node3"
|
99
|
-
expect(tc[:user]).to eq "test_user2"
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
context "#expand_targets" do
|
107
|
-
it "returns a single item when no expansion is required" do
|
108
|
-
expect(subject.expand_targets("one")).to eq ["one"]
|
109
|
-
end
|
110
|
-
|
111
|
-
it "expands single alphabetic range" do
|
112
|
-
expect(subject.expand_targets("host[a:h]")).to eq %w{
|
113
|
-
hosta hostb hostc hostd hoste hostf hostg hosth
|
114
|
-
}
|
115
|
-
end
|
116
|
-
it "expands single alphabetic range even if reverse ordering is given" do
|
117
|
-
expect(subject.expand_targets("host[h:a]")).to eq %w{
|
118
|
-
hosta hostb hostc hostd hoste hostf hostg hosth
|
119
|
-
}
|
120
|
-
end
|
121
|
-
|
122
|
-
it "expands a range when the target name is qualified with credentials" do
|
123
|
-
expect(subject.expand_targets("ssh://user:password@host[a:b]")).to eq %w{
|
124
|
-
ssh://user:password@hosta
|
125
|
-
ssh://user:password@hostb
|
126
|
-
}
|
127
|
-
end
|
128
|
-
|
129
|
-
it "expands a numeric range correctly when start/stop string values ASCII-sort in reverse" do
|
130
|
-
# eg: ["4", "10"].sort => ["10", "4"]
|
131
|
-
expect(subject.expand_targets("[4:10]")).to eq %w{ 4 5 6 7 8 9 10 }
|
132
|
-
end
|
133
|
-
|
134
|
-
it "expands a numeric range correctly when stop is higher than start" do
|
135
|
-
expect(subject.expand_targets("[10:8]")).to eq %w{ 8 9 10 }
|
136
|
-
end
|
137
|
-
|
138
|
-
it "expands a string range correctly when stop is higher than start" do
|
139
|
-
expect(subject.expand_targets("[z:y]")).to eq %w{ y z }
|
140
|
-
end
|
141
|
-
|
142
|
-
it "expands single numeric range" do
|
143
|
-
expect(subject.expand_targets("host[10:20]")).to eq %w{
|
144
|
-
host10 host11 host12 host13 host14 host15 host16
|
145
|
-
host17 host18 host19 host20
|
146
|
-
}
|
147
|
-
end
|
148
|
-
|
149
|
-
it "expands two included ranges" do
|
150
|
-
expect(subject.expand_targets("host[1:4].domain[a:c]").sort).to eq [
|
151
|
-
"host1.domaina", "host1.domainb", "host1.domainc",
|
152
|
-
"host2.domaina", "host2.domainb", "host2.domainc",
|
153
|
-
"host3.domaina", "host3.domainb", "host3.domainc",
|
154
|
-
"host4.domaina", "host4.domainb", "host4.domainc"
|
155
|
-
].sort
|
156
|
-
end
|
157
|
-
|
158
|
-
it "raises InvalidRange if a range mixes alpha and numeric" do
|
159
|
-
expect { subject.expand_targets("host[a:9]") }.to raise_error(ChefApply::TargetResolver::InvalidRange)
|
160
|
-
end
|
161
|
-
|
162
|
-
it "raises TooManyRanges if more than two ranges are included" do
|
163
|
-
expect { subject.expand_targets("[0:1][5:10][10:11]") }.to raise_error(ChefApply::TargetResolver::TooManyRanges)
|
164
|
-
end
|
165
|
-
|
166
|
-
context "when the target resolves to more than #{ChefApply::TargetResolver::MAX_EXPANDED_TARGETS} names" do
|
167
|
-
it "raises TooManyTargets" do
|
168
|
-
expect { subject.expand_targets("[0:99999]") }.to raise_error(ChefApply::TargetResolver::TooManyTargets)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
context "#make_credentials" do
|
174
|
-
let(:default_user) { nil }
|
175
|
-
let(:default_password) { nil }
|
176
|
-
|
177
|
-
let(:inline_user) { nil }
|
178
|
-
let(:inline_password) { nil }
|
179
|
-
|
180
|
-
subject do
|
181
|
-
opts = {}
|
182
|
-
opts[:user] = default_user unless default_user.nil?
|
183
|
-
opts[:password] = default_password unless default_password.nil?
|
184
|
-
resolver = ChefApply::TargetResolver.new("", default_protocol, opts)
|
185
|
-
Proc.new { resolver.make_credentials(inline_user, inline_password) }
|
186
|
-
end
|
187
|
-
|
188
|
-
context "when no default user or password is given" do
|
189
|
-
let(:default_user) { nil }
|
190
|
-
let(:default_password) { nil }
|
191
|
-
|
192
|
-
context "and only an inline user is provided" do
|
193
|
-
let(:inline_user) { "aninlineuser" }
|
194
|
-
let(:inline_password) { nil }
|
195
|
-
it "returns the decorated inline user with nil password" do
|
196
|
-
expect(subject.call).to eq [inline_user, nil]
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
context "and only an inline password is provided" do
|
201
|
-
let(:inline_user) { nil }
|
202
|
-
let(:inline_password) { "inlinepassword4u" }
|
203
|
-
it "returns the decorated inline password" do
|
204
|
-
expect(subject.call).to eq [nil, inline_password]
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
context "and neither inline user nor inline password is given" do
|
209
|
-
let(:inline_user) { nil }
|
210
|
-
let(:inline_password) { nil }
|
211
|
-
it "returns an empty string" do
|
212
|
-
expect(subject.call).to eq [nil, nil]
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
context "and both inline user and inline password are given" do
|
217
|
-
let(:inline_user) { "adefaultuser" }
|
218
|
-
let(:inline_password) { "inlinepassword4u" }
|
219
|
-
it "returns the decorated inline_user and inline password" do
|
220
|
-
expect(subject.call).to eq [inline_user, inline_password]
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
context "when only a default user is given" do
|
226
|
-
let(:default_user) { "defaultusername" }
|
227
|
-
let(:default_password) { nil }
|
228
|
-
|
229
|
-
context "and only an inline user is provided" do
|
230
|
-
let(:inline_user) { "aninlineuser" }
|
231
|
-
let(:inline_password) { nil }
|
232
|
-
it "returns the inline user with no password" do
|
233
|
-
expect(subject.call).to eq [inline_user, nil]
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
context "and only an inline password is provided" do
|
238
|
-
let(:inline_user) { nil }
|
239
|
-
let(:inline_password) { "inlinepassword4u" }
|
240
|
-
it "returns the default user and inline password" do
|
241
|
-
expect(subject.call).to eq [default_user, inline_password]
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
context "and neither inline user nor inline password is given" do
|
246
|
-
let(:inline_user) { nil }
|
247
|
-
let(:inline_password) { nil }
|
248
|
-
it "returns the default user" do
|
249
|
-
expect(subject.call).to eq [default_user, nil]
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
context "and both inline user and inline password are given" do
|
254
|
-
let(:inline_user) { "adefaultuser" }
|
255
|
-
let(:inline_password) { "inlinepassword4u" }
|
256
|
-
it "returns the decorated inline_user and inline password" do
|
257
|
-
expect(subject.call).to eq [inline_user, inline_password]
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
context "when only a default password is given" do
|
263
|
-
let(:default_user) { nil }
|
264
|
-
let(:default_password) { "ihasdefaultpassword" }
|
265
|
-
|
266
|
-
context "and only an inline user is provided" do
|
267
|
-
let(:inline_user) { "aninlineuser" }
|
268
|
-
let(:inline_password) { nil }
|
269
|
-
it "returns the decorated inline user and default password" do
|
270
|
-
expect(subject.call).to eq [inline_user, default_password]
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
context "and only an inline password is provided" do
|
275
|
-
let(:inline_user) { nil }
|
276
|
-
let(:inline_password) { "inlinepassword4u" }
|
277
|
-
it "returns nil user and inline password" do
|
278
|
-
expect(subject.call).to eq [nil, inline_password]
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
context "and neither inline user nor inline password is given" do
|
283
|
-
let(:inline_user) { nil }
|
284
|
-
let(:inline_password) { nil }
|
285
|
-
it "returns the nil user and default password" do
|
286
|
-
expect(subject.call).to eq [nil, default_password]
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
context "and both inline user and inline password are given" do
|
291
|
-
let(:inline_user) { "adefaultuser" }
|
292
|
-
let(:inline_password) { "inlinepassword4u" }
|
293
|
-
it "returns the inline_user and inline password" do
|
294
|
-
expect(subject.call).to eq [inline_user, inline_password]
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
context "when defaults for both user and password are given" do
|
300
|
-
let(:default_user) { "adefaultuser" }
|
301
|
-
let(:default_password) { "ihasdefaultpassword" }
|
302
|
-
|
303
|
-
context "and only an inline user is provided" do
|
304
|
-
let(:inline_user) { "aninlineuser" }
|
305
|
-
let(:inline_password) { nil }
|
306
|
-
it "returns the decorated inline user and default password" do
|
307
|
-
expect(subject.call).to eq [inline_user, default_password]
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
context "and only an inline password is provided" do
|
312
|
-
let(:inline_user) { nil }
|
313
|
-
let(:inline_password) { "inlinepassword4u" }
|
314
|
-
it "returns the decorated default user and inline password" do
|
315
|
-
expect(subject.call).to eq [default_user, inline_password]
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
context "and neither inline user nor inline password is given" do
|
320
|
-
let(:inline_user) { nil }
|
321
|
-
let(:inline_password) { nil }
|
322
|
-
it "returns the decorated default user and default password" do
|
323
|
-
expect(subject.call).to eq [default_user, default_password]
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
context "and both inline user and inline password are given" do
|
328
|
-
let(:inline_user) { "adefaultuser" }
|
329
|
-
let(:inline_password) { "inlinepassword4u" }
|
330
|
-
it "returns the decorated inline_user and inline password" do
|
331
|
-
expect(subject.call).to eq [inline_user, inline_password]
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
context "#config_for_target" do
|
338
|
-
{ "example.com" => { password: nil, url: "ssh://example.com", user: nil },
|
339
|
-
"ssh://example.com" => { password: nil, url: "ssh://example.com", user: nil },
|
340
|
-
"ssh://user@example.com" => { password: nil, url: "ssh://example.com", user: "user" },
|
341
|
-
"ssh://user:password@example.com" => { password: "password", user: "user", url: "ssh://example.com" },
|
342
|
-
"ssh://user:pas:sw:ord@example.com" => { password: "pas:sw:ord", user: "user", url: "ssh://example.com" },
|
343
|
-
"ssh://user:!@#$%^&*()|\'\";:/?><.,{}[]+=`~@example.com" => { password: "!@#$%^&*()|\'\";:/?><.,{}[]+=`~", user: "user", url: "ssh://example.com" },
|
344
|
-
}.each do |values|
|
345
|
-
it "resolves #{values[0]} to #{values[1]}" do
|
346
|
-
expect(subject.config_for_target(values[0])).to eq values[1]
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
it "preserves range specifiers in the host portion while encoding in the password portion" do
|
351
|
-
input = "user:pas[1:2]!^@ho[a:b]s[t:z].com"
|
352
|
-
output = { password: "pas[1:2]!^", url: "ssh://ho[a:b]s[t:z].com", user: "user" }
|
353
|
-
expect(subject.config_for_target(input)).to eq output
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
context "#prefix_from_target" do
|
358
|
-
context "when no protocol is provided" do
|
359
|
-
let(:default_protocol) { "badproto" }
|
360
|
-
it "uses the default from configuration" do
|
361
|
-
expect(subject.prefix_from_target("host.com")).to eq %w{badproto:// host.com}
|
362
|
-
end
|
363
|
-
end
|
364
|
-
|
365
|
-
context "when protocol is provided" do
|
366
|
-
context "and it is valid" do
|
367
|
-
it "keeps the protocol" do
|
368
|
-
expect(subject.prefix_from_target("ssh://host.com")).to eq %w{ssh:// host.com}
|
369
|
-
end
|
370
|
-
end
|
371
|
-
context "and it is not valid" do
|
372
|
-
it "raises an error" do
|
373
|
-
expect { subject.prefix_from_target("bad://host.com") }
|
374
|
-
.to raise_error(ChefApply::TargetResolver::UnsupportedProtocol)
|
375
|
-
end
|
376
|
-
end
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
end
|