chef-apply 0.2.8 → 0.2.13
Sign up to get free protection for your applications and to get access to all the features.
- 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
|