chef-apply 0.2.8 → 0.2.13
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.lock +84 -63
- data/README.md +6 -0
- data/chef-apply.gemspec +1 -0
- data/i18n/errors/en.yml +11 -10
- data/lib/chef_apply/action/base.rb +0 -84
- data/lib/chef_apply/action/converge_target.rb +60 -24
- data/lib/chef_apply/action/install_chef.rb +99 -10
- data/lib/chef_apply/cli.rb +19 -1
- data/lib/chef_apply/config.rb +1 -0
- data/lib/chef_apply/error.rb +1 -0
- data/lib/chef_apply/errors/ccr_failure_mapper.rb +2 -2
- data/lib/chef_apply/target_host.rb +113 -73
- data/lib/chef_apply/target_host/linux.rb +63 -0
- data/lib/chef_apply/target_host/windows.rb +62 -0
- data/lib/chef_apply/version.rb +1 -1
- data/spec/integration/fixtures/chef_help.out +1 -0
- data/spec/unit/action/base_spec.rb +0 -30
- data/spec/unit/action/converge_target_spec.rb +130 -73
- data/spec/unit/action/install_chef_spec.rb +118 -23
- data/spec/unit/cli_spec.rb +32 -4
- data/spec/unit/target_host/linux_spec.rb +57 -0
- data/spec/unit/target_host/windows_spec.rb +43 -0
- data/spec/unit/target_host_spec.rb +130 -135
- data/spec/unit/ui/error_printer_spec.rb +1 -1
- metadata +27 -8
- data/lib/chef_apply/action/install_chef/base.rb +0 -117
- data/lib/chef_apply/action/install_chef/linux.rb +0 -37
- data/lib/chef_apply/action/install_chef/windows.rb +0 -54
- data/spec/unit/action/install_chef/base_spec.rb +0 -168
@@ -19,8 +19,8 @@ require "spec_helper"
|
|
19
19
|
require "chef_apply/action/install_chef"
|
20
20
|
|
21
21
|
RSpec.describe ChefApply::Action::InstallChef do
|
22
|
-
let(:mock_os_name) { "
|
23
|
-
let(:mock_os_family) { "
|
22
|
+
let(:mock_os_name) { "linux" }
|
23
|
+
let(:mock_os_family) { "linux" }
|
24
24
|
let(:mock_os_release ) { "unknown" }
|
25
25
|
let(:mock_opts) do
|
26
26
|
{
|
@@ -31,38 +31,133 @@ RSpec.describe ChefApply::Action::InstallChef do
|
|
31
31
|
}
|
32
32
|
end
|
33
33
|
let(:target_host) do
|
34
|
-
ChefApply::TargetHost.
|
34
|
+
ChefApply::TargetHost.mock_instance("mock://user1:password1@localhost", mock_opts)
|
35
35
|
end
|
36
36
|
|
37
|
-
|
38
|
-
ChefApply::
|
37
|
+
let(:reporter) do
|
38
|
+
ChefApply::MockReporter.new
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
target_host
|
43
|
-
|
41
|
+
subject(:install) do
|
42
|
+
ChefApply::Action::InstallChef.new(target_host: target_host,
|
43
|
+
reporter: reporter,
|
44
|
+
check_only: false)
|
44
45
|
end
|
45
46
|
|
46
|
-
context "
|
47
|
-
context "
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
context "#perform_action" do
|
48
|
+
context "when chef is already installed on target" do
|
49
|
+
it "notifies of success and takes no further action" do
|
50
|
+
expect(ChefApply::MinimumChefVersion).to receive(:check!).with(install.target_host, false)
|
51
|
+
.and_return(:minimum_version_met)
|
52
|
+
expect(install).not_to receive(:perform_local_install)
|
53
|
+
install.perform_action
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when chef is not already installed on target" do
|
58
|
+
it "should invoke perform_local_install" do
|
59
|
+
expect(ChefApply::MinimumChefVersion).to receive(:check!).with(install.target_host, false)
|
60
|
+
.and_return(:client_not_installed)
|
61
|
+
expect(install).to receive(:perform_local_install)
|
62
|
+
install.perform_action
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "#perform_local_install" do
|
68
|
+
let(:artifact) { double("artifact") }
|
69
|
+
let(:package_url) { "https://chef.io/download/package/here" }
|
70
|
+
before do
|
71
|
+
allow(artifact).to receive(:url).and_return package_url
|
72
|
+
end
|
73
|
+
|
74
|
+
it "performs the steps necessary to perform an installation" do
|
75
|
+
expect(install).to receive(:lookup_artifact).and_return artifact
|
76
|
+
expect(install).to receive(:download_to_workstation).with(package_url) .and_return "/local/path"
|
77
|
+
expect(install).to receive(:upload_to_target).with("/local/path").and_return("/remote/path")
|
78
|
+
expect(target_host).to receive(:install_package).with("/remote/path")
|
79
|
+
|
80
|
+
install.perform_local_install
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "#train_to_mixlib" do
|
85
|
+
let(:platform) { double }
|
86
|
+
before do
|
87
|
+
allow(platform).to receive(:release).and_return "1234"
|
88
|
+
allow(platform).to receive(:name).and_return "beos"
|
89
|
+
allow(platform).to receive(:arch).and_return "ppc"
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when any flavor of windows" do
|
93
|
+
before do
|
94
|
+
allow(platform).to receive(:name).and_return "windows_10_pro_n"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "sets platform name to 'windows'" do
|
98
|
+
mixlib_info = install.train_to_mixlib(platform)
|
99
|
+
expect(mixlib_info[:platform]).to eq "windows"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when redhat" do
|
104
|
+
before do
|
105
|
+
allow(platform).to receive(:name).and_return "redhat"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "sets platform name to 'el'" do
|
109
|
+
mixlib_info = install.train_to_mixlib(platform)
|
110
|
+
expect(mixlib_info[:platform]).to eq "el"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "when centos" do
|
115
|
+
before do
|
116
|
+
allow(platform).to receive(:name).and_return "centos"
|
117
|
+
end
|
51
118
|
|
52
|
-
it "
|
53
|
-
|
54
|
-
expect(
|
119
|
+
it "sets platform name to 'el'" do
|
120
|
+
mixlib_info = install.train_to_mixlib(platform)
|
121
|
+
expect(mixlib_info[:platform]).to eq "el"
|
55
122
|
end
|
56
123
|
end
|
57
124
|
|
58
|
-
context "
|
59
|
-
|
60
|
-
|
61
|
-
|
125
|
+
context "when suse" do
|
126
|
+
before do
|
127
|
+
allow(platform).to receive(:name).and_return "suse"
|
128
|
+
end
|
129
|
+
|
130
|
+
it "sets platform name to 'sles'" do
|
131
|
+
mixlib_info = install.train_to_mixlib(platform)
|
132
|
+
expect(mixlib_info[:platform]).to eq "sles"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
context "when amazon" do
|
136
|
+
before do
|
137
|
+
allow(platform).to receive(:name).and_return "amazon"
|
138
|
+
end
|
139
|
+
|
140
|
+
context "when amazon linux 1.x" do
|
141
|
+
before do
|
142
|
+
allow(platform).to receive(:release).and_return "2017.09"
|
143
|
+
end
|
144
|
+
|
145
|
+
it "sets platform name to 'amazon' and plaform_version to '6'" do
|
146
|
+
mixlib_info = install.train_to_mixlib(platform)
|
147
|
+
expect(mixlib_info[:platform]).to eq "el"
|
148
|
+
expect(mixlib_info[:platform_version]).to eq "6"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
context "when amazon linux 2.x" do
|
152
|
+
before do
|
153
|
+
allow(platform).to receive(:release).and_return "2"
|
154
|
+
end
|
62
155
|
|
63
|
-
|
64
|
-
|
65
|
-
|
156
|
+
it "sets platform name to 'amazon' and plaform_version to '7'" do
|
157
|
+
mixlib_info = install.train_to_mixlib(platform)
|
158
|
+
expect(mixlib_info[:platform]).to eq "el"
|
159
|
+
expect(mixlib_info[:platform_version]).to eq "7"
|
160
|
+
end
|
66
161
|
end
|
67
162
|
end
|
68
163
|
end
|
data/spec/unit/cli_spec.rb
CHANGED
@@ -146,6 +146,7 @@ RSpec.describe ChefApply::CLI do
|
|
146
146
|
context "and they are valid" do
|
147
147
|
it "creates the cookbook locally and converges it" do
|
148
148
|
expect(subject).to receive(:parse_options)
|
149
|
+
expect(subject).to receive(:check_license_acceptance)
|
149
150
|
expect(subject).to receive(:validate_params)
|
150
151
|
expect(subject).to receive(:resolve_targets).and_return target_hosts
|
151
152
|
expect(subject).to receive(:render_cookbook_setup)
|
@@ -156,6 +157,33 @@ RSpec.describe ChefApply::CLI do
|
|
156
157
|
end
|
157
158
|
end
|
158
159
|
|
160
|
+
describe "#check_license_acceptance" do
|
161
|
+
let(:acceptance_value) { "accept-no-persist" }
|
162
|
+
let(:acceptor) { instance_double(LicenseAcceptance::Acceptor) }
|
163
|
+
|
164
|
+
before do
|
165
|
+
ChefApply::Config.reset
|
166
|
+
expect(LicenseAcceptance::Acceptor).to receive(:new).with(provided: ChefApply::Config.chef.chef_license).and_return(acceptor)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "sets the config value to the acceptance value" do
|
170
|
+
expect(ChefApply::Config.chef.chef_license).to eq(nil)
|
171
|
+
expect(acceptor).to receive(:check_and_persist).with("infra-client", "latest")
|
172
|
+
expect(acceptor).to receive(:acceptance_value).and_return(acceptance_value)
|
173
|
+
subject.check_license_acceptance
|
174
|
+
expect(ChefApply::Config.chef.chef_license).to eq(acceptance_value)
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "when the user does not accept the license" do
|
178
|
+
it "raises a LicenseCheckFailed error" do
|
179
|
+
expect(ChefApply::Config.chef.chef_license).to eq(nil)
|
180
|
+
expect(acceptor).to receive(:check_and_persist).with("infra-client", "latest").and_raise(LicenseAcceptance::LicenseNotAcceptedError.new(nil, []))
|
181
|
+
expect { subject.check_license_acceptance }.to raise_error(ChefApply::LicenseCheckFailed)
|
182
|
+
expect(ChefApply::Config.chef.chef_license).to eq(nil)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
159
187
|
describe "#connect_target" do
|
160
188
|
let(:host) { double("TargetHost", config: {}, user: "root" ) }
|
161
189
|
let(:reporter) { double("reporter", update: :ok, success: :ok) }
|
@@ -354,12 +382,12 @@ RSpec.describe ChefApply::CLI do
|
|
354
382
|
upgrading?: upgrading,
|
355
383
|
version_to_install: "14.0") end
|
356
384
|
|
357
|
-
it "updates status,
|
385
|
+
it "updates status, creates an InstallChef action and executes it" do
|
358
386
|
expect(reporter)
|
359
387
|
.to receive(:update)
|
360
388
|
.with(ChefApply::CLI::TS.install_chef.verifying)
|
361
|
-
expect(ChefApply::Action::InstallChef).to receive(:
|
362
|
-
.with(target_host, check_only: false)
|
389
|
+
expect(ChefApply::Action::InstallChef).to receive(:new)
|
390
|
+
.with(target_host: target_host, check_only: false)
|
363
391
|
.and_return action
|
364
392
|
expect(action).to receive(:run)
|
365
393
|
subject.install(target_host, reporter)
|
@@ -371,7 +399,7 @@ RSpec.describe ChefApply::CLI do
|
|
371
399
|
|
372
400
|
before do
|
373
401
|
allow(ChefApply::Action::InstallChef)
|
374
|
-
.to receive(:
|
402
|
+
.to receive(:new).and_return action
|
375
403
|
allow(action)
|
376
404
|
.to receive(:run) { |&block| block.call(event, event_args) }
|
377
405
|
allow(reporter)
|
@@ -0,0 +1,57 @@
|
|
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
|
@@ -0,0 +1,43 @@
|
|
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,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright:: Copyright (c) 2018 Chef Software Inc.
|
2
|
+
# Copyright:: Copyright (c) 2018-2019 Chef Software Inc.
|
3
3
|
# License:: Apache License, Version 2.0
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -21,19 +21,17 @@ require "chef_apply/target_host"
|
|
21
21
|
|
22
22
|
RSpec.describe ChefApply::TargetHost do
|
23
23
|
let(:host) { "mock://user@example.com" }
|
24
|
-
let(:
|
25
|
-
let(:
|
26
|
-
|
27
|
-
let(:is_linux) { false }
|
28
|
-
let(:platform_mock) { double("platform", linux?: is_linux, family: family, name: "an os") }
|
24
|
+
let(:family) { "debian" }
|
25
|
+
let(:name) { "ubuntu" }
|
26
|
+
|
29
27
|
subject do
|
30
|
-
|
31
|
-
allow(s).to receive(:platform).and_return(platform_mock)
|
32
|
-
s
|
28
|
+
ChefApply::TargetHost.mock_instance(host, family: family, name: name)
|
33
29
|
end
|
34
30
|
|
35
31
|
context "#base_os" do
|
36
32
|
context "for a windows os" do
|
33
|
+
let(:family) { "windows" }
|
34
|
+
let(:name) { "windows" }
|
37
35
|
it "reports :windows" do
|
38
36
|
expect(subject.base_os).to eq :windows
|
39
37
|
end
|
@@ -41,29 +39,25 @@ RSpec.describe ChefApply::TargetHost do
|
|
41
39
|
|
42
40
|
context "for a linux os" do
|
43
41
|
let(:family) { "debian" }
|
44
|
-
let(:
|
42
|
+
let(:name) { "ubuntu" }
|
45
43
|
it "reports :linux" do
|
46
44
|
expect(subject.base_os).to eq :linux
|
47
45
|
end
|
48
46
|
end
|
49
47
|
|
50
48
|
context "for an unsupported OS" do
|
51
|
-
let(:family) { "
|
52
|
-
let(:
|
53
|
-
it "
|
54
|
-
expect
|
49
|
+
let(:family) { "unknown" }
|
50
|
+
let(:name) { "unknown" }
|
51
|
+
it "reports :other" do
|
52
|
+
expect(subject.base_os).to eq :other
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
58
56
|
|
59
57
|
context "#installed_chef_version" do
|
60
|
-
let(:manifest) { :not_found }
|
61
|
-
before do
|
62
|
-
allow(subject).to receive(:get_chef_version_manifest).and_return manifest
|
63
|
-
end
|
64
|
-
|
65
58
|
context "when no version manifest is present" do
|
66
59
|
it "raises ChefNotInstalled" do
|
60
|
+
expect(subject).to receive(:read_chef_version_manifest).and_raise(ChefApply::TargetHost::ChefNotInstalled.new)
|
67
61
|
expect { subject.installed_chef_version }.to raise_error(ChefApply::TargetHost::ChefNotInstalled)
|
68
62
|
end
|
69
63
|
end
|
@@ -71,30 +65,79 @@ RSpec.describe ChefApply::TargetHost do
|
|
71
65
|
context "when version manifest is present" do
|
72
66
|
let(:manifest) { { "build_version" => "14.0.1" } }
|
73
67
|
it "reports version based on the build_version field" do
|
68
|
+
expect(subject).to receive(:read_chef_version_manifest).and_return manifest
|
74
69
|
expect(subject.installed_chef_version).to eq Gem::Version.new("14.0.1")
|
75
70
|
end
|
76
71
|
end
|
77
72
|
end
|
78
73
|
|
79
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) }
|
80
78
|
let(:train_connection_mock) { double("train connection") }
|
81
79
|
before do
|
82
|
-
allow(
|
80
|
+
allow(target_host).to receive(:train_connection).and_return(train_connection_mock)
|
83
81
|
end
|
84
82
|
context "when an Train::UserError occurs" do
|
85
83
|
it "raises a ConnectionFailure" do
|
86
84
|
allow(train_connection_mock).to receive(:connection).and_raise Train::UserError
|
87
|
-
expect {
|
85
|
+
expect { target_host.connect! }.to raise_error(ChefApply::TargetHost::ConnectionFailure)
|
88
86
|
end
|
89
87
|
end
|
90
88
|
context "when a Train::Error occurs" do
|
91
89
|
it "raises a ConnectionFailure" do
|
92
90
|
allow(train_connection_mock).to receive(:connection).and_raise Train::Error
|
93
|
-
expect {
|
91
|
+
expect { target_host.connect! }.to raise_error(ChefApply::TargetHost::ConnectionFailure)
|
94
92
|
end
|
95
93
|
end
|
96
94
|
end
|
97
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
|
+
|
98
141
|
context "#user" do
|
99
142
|
before do
|
100
143
|
allow(subject).to receive(:config).and_return(user: user)
|
@@ -140,58 +183,73 @@ RSpec.describe ChefApply::TargetHost do
|
|
140
183
|
end
|
141
184
|
end
|
142
185
|
|
143
|
-
context "#
|
186
|
+
context "#read_chef_version_manifest" do
|
144
187
|
let(:manifest_content) { '{"build_version" : "1.2.3"}' }
|
145
|
-
let(:expected_manifest_path) do
|
146
|
-
{
|
147
|
-
windows: "c:\\opscode\\chef\\version-manifest.json",
|
148
|
-
linux: "/opt/chef/version-manifest.json",
|
149
|
-
}
|
150
|
-
end
|
151
|
-
let(:base_os) { :unknown }
|
152
188
|
before do
|
153
|
-
|
154
|
-
|
155
|
-
expect(backend_mock).to receive(:file)
|
156
|
-
.with(expected_manifest_path[base_os])
|
157
|
-
.and_return(remote_file_mock)
|
158
|
-
allow(subject).to receive(:backend).and_return backend_mock
|
159
|
-
allow(subject).to receive(:base_os).and_return base_os
|
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")
|
160
191
|
end
|
161
192
|
|
162
193
|
context "when manifest is missing" do
|
163
|
-
let(:
|
164
|
-
|
165
|
-
|
166
|
-
it "returns :not_found" do
|
167
|
-
expect(subject.get_chef_version_manifest).to eq :not_found
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
171
|
-
context "on linux" do
|
172
|
-
let(:base_os) { :linux }
|
173
|
-
it "returns :not_found" do
|
174
|
-
expect(subject.get_chef_version_manifest).to eq :not_found
|
175
|
-
end
|
194
|
+
let(:manifest_content) { nil }
|
195
|
+
it "raises ChefNotInstalled" do
|
196
|
+
expect { subject.read_chef_version_manifest }.to raise_error(ChefApply::TargetHost::ChefNotInstalled)
|
176
197
|
end
|
177
198
|
end
|
178
199
|
|
179
200
|
context "when manifest is present" do
|
180
|
-
let(:
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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) end
|
225
|
+
before do
|
226
|
+
expect(subject).to receive(:backend).and_return backend_mock
|
227
|
+
expect(backend_mock).to receive(:file).with(path).and_return remote_file_mock
|
228
|
+
end
|
229
|
+
|
230
|
+
context "when path exists" do
|
231
|
+
let(:path_exists) { true }
|
232
|
+
before do
|
186
233
|
end
|
187
234
|
|
188
|
-
context "
|
189
|
-
let(:
|
190
|
-
it "
|
191
|
-
expect(subject.
|
235
|
+
context "but is not a file" do
|
236
|
+
let(:path_is_file) { false }
|
237
|
+
it "returns nil" do
|
238
|
+
expect(subject.fetch_file_contents(path)).to be_nil
|
239
|
+
end
|
240
|
+
end
|
241
|
+
context "and is a file" do
|
242
|
+
it "returns the expected file contents" do
|
243
|
+
expect(subject.fetch_file_contents(path)).to eq sample_content
|
192
244
|
end
|
193
245
|
end
|
194
246
|
end
|
247
|
+
context "when path does not exist" do
|
248
|
+
let(:path_exists) { false }
|
249
|
+
it "returns nil" do
|
250
|
+
expect(subject.fetch_file_contents(path)).to be_nil
|
251
|
+
end
|
252
|
+
end
|
195
253
|
end
|
196
254
|
|
197
255
|
context "#apply_ssh_config" do
|
@@ -219,83 +277,20 @@ RSpec.describe ChefApply::TargetHost do
|
|
219
277
|
end
|
220
278
|
end
|
221
279
|
|
222
|
-
context "
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
allow(subject).to receive(:user).and_return user
|
280
|
+
context "#temp_dir" do
|
281
|
+
it "creates the temp directory and changes ownership" do
|
282
|
+
expect(subject).to receive(:make_temp_dir).and_return("/tmp/dir")
|
283
|
+
expect(subject).to receive(:chown).with("/tmp/dir", subject.user)
|
284
|
+
subject.temp_dir()
|
228
285
|
end
|
229
|
-
|
230
|
-
context "when the target is Windows" do
|
231
|
-
let(:base_os) { :windows }
|
232
|
-
it "creates the directory using the correct command PowerShell command" do
|
233
|
-
# TODO - testing command strings always feels a bit like an antipattern. Do we have alternatives?
|
234
|
-
expect(subject).to receive(:run_command!).with("New-Item -ItemType Directory -Force -Path C:\\temp\\dir")
|
235
|
-
subject.mkdir("C:\\temp\\dir")
|
236
|
-
end
|
237
|
-
|
238
|
-
end
|
239
|
-
context "when the target is Linux" do
|
240
|
-
let(:base_os) { :linux }
|
241
|
-
it "uses a properly formed mkdir to create the directory and changes ownership to connected user" do
|
242
|
-
expect(subject).to receive(:run_command!).with("mkdir -p /tmp/dir")
|
243
|
-
expect(subject).to receive(:run_command!).with("chown testuser '/tmp/dir'")
|
244
|
-
subject.mkdir("/tmp/dir")
|
245
|
-
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
context "#chown" do
|
251
|
-
context "when the target is Windows" do
|
252
|
-
let(:base_os) { :windows }
|
253
|
-
xit "does nothing - this is not implemented until we need it"
|
254
|
-
end
|
255
|
-
|
256
|
-
context "when the target is Linux" do
|
257
|
-
let(:base_os) { :linux }
|
258
|
-
let(:path) { "/tmp/blah" }
|
259
|
-
|
260
|
-
context "and an owner is provided" do
|
261
|
-
it "uses a properly formed chown to change owning user to the connected user" do
|
262
|
-
expect(subject).to receive(:run_command!).with("chown newowner '/tmp/dir'")
|
263
|
-
subject.chown("/tmp/dir", "newowner")
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
context "and an owner is not provided" do
|
268
|
-
it "uses a properly formed chown to change owning user to the connected user" do
|
269
|
-
expect(subject).to receive(:run_command!).with("chown #{user} '/tmp/dir'")
|
270
|
-
subject.chown("/tmp/dir")
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
context "#mktemp" do
|
277
|
-
context "when the target is Windows" do
|
278
|
-
let(:base_os) { :windows }
|
279
|
-
let(:path) { "C:\\temp\\blah" }
|
280
|
-
it "creates the temporary directory using the correct PowerShell command and returns the path" do
|
281
|
-
expect(subject).to receive(:run_command!)
|
282
|
-
.with(ChefApply::TargetHost::MKTMP_WIN_CMD)
|
283
|
-
.and_return(instance_double("result", stdout: path))
|
284
|
-
expect(subject.mktemp()).to eq(path)
|
285
|
-
end
|
286
|
-
end
|
286
|
+
end
|
287
287
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
.with("bash -c '#{ChefApply::TargetHost::MKTMP_LINUX_CMD}'")
|
294
|
-
.and_return(instance_double("result", stdout: "/tmp/blah"))
|
295
|
-
expect(subject).to receive(:chown).with(path)
|
296
|
-
expect(subject.mktemp()).to eq path
|
297
|
-
end
|
298
|
-
end
|
288
|
+
context "#make_directory" do
|
289
|
+
it "creates the directory and sets ownership to connecting user" do
|
290
|
+
expect(subject).to receive(:mkdir).with("/tmp/mkdir")
|
291
|
+
expect(subject).to receive(:chown).with("/tmp/mkdir", subject.user)
|
292
|
+
subject.make_directory("/tmp/mkdir")
|
299
293
|
end
|
300
294
|
end
|
295
|
+
|
301
296
|
end
|