chef-apply 0.4.6 → 0.4.9
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/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
|