kitchen-rackspace 0.22.0 → 0.22.1
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/.release-please-manifest.json +1 -1
- data/.rspec +1 -0
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTING.md +33 -1
- data/README.md +28 -6
- data/lib/kitchen/driver/rackspace.rb +25 -3
- data/lib/kitchen/driver/rackspace_version.rb +1 -1
- data/spec/kitchen/driver/rackspace/compute_spec.rb +162 -0
- data/spec/kitchen/driver/rackspace/configuration_spec.rb +210 -0
- data/spec/kitchen/driver/rackspace/create_spec.rb +150 -0
- data/spec/kitchen/driver/rackspace/destroy_spec.rb +63 -0
- data/spec/kitchen/driver/rackspace/image_spec.rb +58 -0
- data/spec/kitchen/driver/rackspace/plugin_spec.rb +14 -0
- data/spec/kitchen/driver/rackspace/server_name_spec.rb +63 -0
- data/spec/kitchen/driver/rackspace/status_spec.rb +151 -0
- data/spec/kitchen/driver/rackspace/tcp_check_spec.rb +37 -0
- data/spec/spec_helper.rb +21 -5
- data/spec/support/driver_context.rb +51 -0
- data/spec/support/rackspace_environment.rb +27 -0
- metadata +14 -3
- data/spec/kitchen/driver/rackspace_spec.rb +0 -785
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
include_context "a built server"
|
|
4
|
+
|
|
5
|
+
let(:credentials) do
|
|
6
|
+
{ rackspace_username: "hello", rackspace_api_key: "world", wait_for: 1200 }
|
|
7
|
+
end
|
|
8
|
+
let(:config) { credentials }
|
|
9
|
+
|
|
10
|
+
describe "#create" do
|
|
11
|
+
it "generates a server name in the absence of one" do
|
|
12
|
+
driver.create(state)
|
|
13
|
+
|
|
14
|
+
expect(driver[:server_name]).to eq("a_monkey!")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "records the server ID" do
|
|
18
|
+
driver.create(state)
|
|
19
|
+
|
|
20
|
+
expect(state[:server_id]).to eq("test123")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "records the public IP as the hostname" do
|
|
24
|
+
driver.create(state)
|
|
25
|
+
|
|
26
|
+
expect(state[:hostname]).to eq("1.2.3.4")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "records the login user" do
|
|
30
|
+
driver.create(state)
|
|
31
|
+
|
|
32
|
+
expect(state[:username]).to eq("root")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "records the SSH port" do
|
|
36
|
+
driver.create(state)
|
|
37
|
+
|
|
38
|
+
expect(state[:port]).to eq("22")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "records an overridden login user and port" do
|
|
42
|
+
config.merge!(username: "admin", port: "2222")
|
|
43
|
+
driver.create(state)
|
|
44
|
+
|
|
45
|
+
expect(state).to include(username: "admin", port: "2222")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "waits for the server to report ready" do
|
|
49
|
+
expect(server).to receive(:wait_for)
|
|
50
|
+
|
|
51
|
+
driver.create(state)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "waits for the instance to accept a login" do
|
|
55
|
+
expect(driver).to receive(:tcp_check).with(state)
|
|
56
|
+
|
|
57
|
+
driver.create(state)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "does not wait for rackconnect by default" do
|
|
61
|
+
expect(driver).to_not receive(:rackconnect_check)
|
|
62
|
+
|
|
63
|
+
driver.create(state)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "does not wait for managed service level by default" do
|
|
67
|
+
expect(driver).to_not receive(:servicelevel_check)
|
|
68
|
+
|
|
69
|
+
driver.create(state)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context "a Fog error" do
|
|
73
|
+
before do
|
|
74
|
+
allow_any_instance_of(described_class).to receive(:create_server)
|
|
75
|
+
.and_raise(Fog::Errors::Error, "Uhoh")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "re-raises it as a Kitchen failure" do
|
|
79
|
+
expect { driver.create(state) }
|
|
80
|
+
.to raise_error(Kitchen::ActionFailed, "Uhoh")
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Anything the Rackspace API rejects surfaces from Excon rather than fog,
|
|
85
|
+
# and it has to be turned into a Kitchen failure too or the user gets a
|
|
86
|
+
# raw stack trace instead of a message.
|
|
87
|
+
context "an Excon error" do
|
|
88
|
+
before do
|
|
89
|
+
allow_any_instance_of(described_class).to receive(:create_server)
|
|
90
|
+
.and_raise(Excon::Errors::BadRequest, "Bad flavor")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "re-raises it as a Kitchen failure" do
|
|
94
|
+
expect { driver.create(state) }
|
|
95
|
+
.to raise_error(Kitchen::ActionFailed, /Bad flavor/)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe "#create with rackconnect_wait" do
|
|
101
|
+
let(:config) { credentials.merge(rackconnect_wait: true) }
|
|
102
|
+
|
|
103
|
+
it "waits for rackconnect" do
|
|
104
|
+
expect(driver).to receive(:rackconnect_check).with(server)
|
|
105
|
+
|
|
106
|
+
driver.create(state)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "waits on the rackconnect automation status" do
|
|
110
|
+
driver.send(:rackconnect_check, server)
|
|
111
|
+
|
|
112
|
+
expect(server).to have_received(:wait_for)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# RackConnect hands the server a new public address as part of its work.
|
|
116
|
+
# Without the refresh, the stale address goes into the state file and the
|
|
117
|
+
# transport connects to nothing.
|
|
118
|
+
it "refreshes the server so the new public IP is picked up" do
|
|
119
|
+
driver.send(:rackconnect_check, server)
|
|
120
|
+
|
|
121
|
+
expect(server).to have_received(:update)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
describe "#create with servicelevel_wait" do
|
|
126
|
+
let(:config) { credentials.merge(servicelevel_wait: true) }
|
|
127
|
+
|
|
128
|
+
it "waits for managed service level automation" do
|
|
129
|
+
expect(driver).to receive(:servicelevel_check).with(server)
|
|
130
|
+
|
|
131
|
+
driver.create(state)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "waits on the service level automation status" do
|
|
135
|
+
driver.send(:servicelevel_check, server)
|
|
136
|
+
|
|
137
|
+
expect(server).to have_received(:wait_for)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe "#create with servicenet" do
|
|
142
|
+
let(:config) { credentials.merge(servicenet: true) }
|
|
143
|
+
|
|
144
|
+
it "records the private IP as the hostname" do
|
|
145
|
+
driver.create(state)
|
|
146
|
+
|
|
147
|
+
expect(state[:hostname]).to eq("10.9.8.7")
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
|
|
4
|
+
describe "#destroy" do
|
|
5
|
+
let(:server) { double("server", nil?: false, destroy: true) }
|
|
6
|
+
let(:servers) { instance_double(Fog::Compute::RackspaceV2::Servers, get: server) }
|
|
7
|
+
let(:state) { { server_id: "12345", hostname: "example.com" } }
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
allow_any_instance_of(described_class).to receive(:compute)
|
|
11
|
+
.and_return(double("compute", servers:))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "a live server that needs to be destroyed" do
|
|
15
|
+
it "destroys the server" do
|
|
16
|
+
expect(server).to receive(:destroy)
|
|
17
|
+
|
|
18
|
+
driver.destroy(state)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "clears the server out of the state" do
|
|
22
|
+
driver.destroy(state)
|
|
23
|
+
|
|
24
|
+
expect(state).to_not include(:server_id)
|
|
25
|
+
expect(state).to_not include(:hostname)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "says which server it destroyed" do
|
|
29
|
+
driver.destroy(state)
|
|
30
|
+
|
|
31
|
+
expect(logged_output.string).to match(/instance <12345> destroyed/)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context "no server ID present" do
|
|
36
|
+
let(:state) { {} }
|
|
37
|
+
|
|
38
|
+
it "does nothing" do
|
|
39
|
+
expect(driver).to_not receive(:compute)
|
|
40
|
+
|
|
41
|
+
expect { driver.destroy(state) }.to_not raise_error
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "a server that was already destroyed" do
|
|
46
|
+
let(:servers) do
|
|
47
|
+
instance_double(Fog::Compute::RackspaceV2::Servers, get: nil)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "does not try to destroy the server again" do
|
|
51
|
+
expect(server).to_not receive(:destroy)
|
|
52
|
+
|
|
53
|
+
driver.destroy(state)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "still clears the server out of the state" do
|
|
57
|
+
driver.destroy(state)
|
|
58
|
+
|
|
59
|
+
expect(state).to_not include(:server_id)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
|
|
4
|
+
describe "#default_image" do
|
|
5
|
+
{
|
|
6
|
+
"ubuntu-12.04" => "f2d30a56-bc2b-4906-8027-92f8a45bbb10",
|
|
7
|
+
"ubuntu-12" => "f2d30a56-bc2b-4906-8027-92f8a45bbb10",
|
|
8
|
+
"ubuntu-14.04" => "e6baca58-c5f4-48d3-901a-abdeb0cfe907",
|
|
9
|
+
"ubuntu-14" => "e6baca58-c5f4-48d3-901a-abdeb0cfe907",
|
|
10
|
+
"ubuntu" => "9b3ae961-0ba0-4d5a-973f-2e79043f0ddd",
|
|
11
|
+
"centos-6" => "7d791876-4c8f-44a2-8d4b-e84bfb0b1c8c",
|
|
12
|
+
"centos" => "1a79f262-33d2-428c-924b-9852a6c15ea8",
|
|
13
|
+
}.each do |platform, id|
|
|
14
|
+
context "the platform is #{platform}" do
|
|
15
|
+
let(:platform_name) { platform }
|
|
16
|
+
|
|
17
|
+
it "resolves it to the right image ID" do
|
|
18
|
+
expect(driver[:image_id]).to eq(id)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The bundled table has not been regenerated since 2016, so every modern
|
|
24
|
+
# platform name lands here. `image_id` then has no value, `required_config`
|
|
25
|
+
# rejects it, and the user has to supply one -- which is exactly what the
|
|
26
|
+
# README tells them to do. If this ever starts returning something, the
|
|
27
|
+
# README is wrong.
|
|
28
|
+
context "a platform the bundled table does not know" do
|
|
29
|
+
let(:platform_name) { "ubuntu-24.04" }
|
|
30
|
+
|
|
31
|
+
it "has no image to offer" do
|
|
32
|
+
expect(driver.default_image).to be_nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "the bundled image table" do
|
|
38
|
+
subject(:images) { driver.send(:images) }
|
|
39
|
+
|
|
40
|
+
it "parses as JSON" do
|
|
41
|
+
expect { images }.to_not raise_error
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "is not empty" do
|
|
45
|
+
expect(images).to_not be_empty
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "maps every platform name to a UUID" do
|
|
49
|
+
expect(images.values).to all(match(/\A\h{8}(-\h{4}){3}-\h{12}\z/))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "is only read from disk once" do
|
|
53
|
+
expect(IO).to receive(:read).once.and_call_original
|
|
54
|
+
|
|
55
|
+
2.times { driver.send(:images) }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
|
|
4
|
+
describe "plugin metadata" do
|
|
5
|
+
it "declares the driver API version" do
|
|
6
|
+
expect(described_class.instance_variable_get(:@api_version)).to eq(2)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "reports its own gem version to kitchen diagnose" do
|
|
10
|
+
expect(driver.diagnose_plugin[:version])
|
|
11
|
+
.to eq(Kitchen::Driver::RACKSPACE_VERSION)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
|
|
4
|
+
describe "#default_name" do
|
|
5
|
+
subject(:name) { driver.default_name }
|
|
6
|
+
|
|
7
|
+
let(:login) { "user" }
|
|
8
|
+
let(:hostname) { "host" }
|
|
9
|
+
|
|
10
|
+
before do
|
|
11
|
+
allow(Etc).to receive(:getlogin).and_return(login)
|
|
12
|
+
allow(Socket).to receive(:gethostname).and_return(hostname)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "generates a name" do
|
|
16
|
+
expect(name).to match(/^potatoes-user-host-(\S*)/)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "a local node with a long hostname" do
|
|
20
|
+
let(:hostname) { "ab.c" * 20 }
|
|
21
|
+
|
|
22
|
+
it "limits the generated name to 63 characters" do
|
|
23
|
+
expect(name.length).to be <= 63
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "a long hostname, username, and base name" do
|
|
28
|
+
let(:login) { "abcd" * 20 }
|
|
29
|
+
let(:hostname) { "efgh" * 20 }
|
|
30
|
+
let(:instance_name) { "ijkl" * 20 }
|
|
31
|
+
|
|
32
|
+
it "limits the generated name to 63 characters" do
|
|
33
|
+
expect(name.length).to eq(63)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context "a login and hostname with punctuation in them" do
|
|
38
|
+
let(:login) { "some.u-se-r" }
|
|
39
|
+
let(:hostname) { "a.host-name" }
|
|
40
|
+
let(:instance_name) { "a.instance-name" }
|
|
41
|
+
|
|
42
|
+
it "strips out the dots to prevent bad server names" do
|
|
43
|
+
expect(name).to_not include(".")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "strips out all but the three hyphen separators" do
|
|
47
|
+
expect(name.count("-")).to eq(3)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context "a non-login shell" do
|
|
52
|
+
let(:login) { nil }
|
|
53
|
+
|
|
54
|
+
it "subs in a placeholder login string" do
|
|
55
|
+
expect(name).to match(/^potatoes-nologin-/)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "does not repeat itself" do
|
|
60
|
+
expect(driver.default_name).to_not eq(driver.default_name)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
|
|
4
|
+
describe "#status" do
|
|
5
|
+
let(:server) { double("server", state: "ACTIVE") }
|
|
6
|
+
let(:servers) { instance_double(Fog::Compute::RackspaceV2::Servers) }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
allow_any_instance_of(described_class).to receive(:compute)
|
|
10
|
+
.and_return(double("compute", servers:))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
context "with no server in state" do
|
|
14
|
+
it "reports an unknown status" do
|
|
15
|
+
expect(driver.status({})).to include(live: nil, state: "unknown")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "with a server Rackspace does not know" do
|
|
20
|
+
it "reports an unknown status" do
|
|
21
|
+
expect(servers).to receive(:get).with("gone").and_return(nil)
|
|
22
|
+
|
|
23
|
+
expect(driver.status(server_id: "gone")).to include(state: "unknown")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "with a live server" do
|
|
28
|
+
before do
|
|
29
|
+
allow(servers).to receive(:get).with("12345").and_return(server)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "reports the server as live" do
|
|
33
|
+
expect(driver.status(server_id: "12345")).to include(
|
|
34
|
+
live: true,
|
|
35
|
+
state: "ACTIVE",
|
|
36
|
+
source: "driver",
|
|
37
|
+
resource_id: "12345"
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "stamps when the check happened" do
|
|
42
|
+
expect(driver.status(server_id: "12345")[:checked_at])
|
|
43
|
+
.to match(/\A\d{4}-\d{2}-\d{2}T/)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context "with a server that is not active" do
|
|
48
|
+
it "reports the server as not live" do
|
|
49
|
+
allow(servers).to receive(:get)
|
|
50
|
+
.with("12345").and_return(double("server", state: "ERROR"))
|
|
51
|
+
|
|
52
|
+
expect(driver.status(server_id: "12345"))
|
|
53
|
+
.to include(live: false, state: "ERROR")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context "when Rackspace cannot be reached" do
|
|
58
|
+
it "reports an unknown status rather than raising" do
|
|
59
|
+
allow(servers).to receive(:get)
|
|
60
|
+
.and_raise(Fog::Errors::Error.new("boom"))
|
|
61
|
+
|
|
62
|
+
expect(driver.status(server_id: "12345")).to include(state: "unknown")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "survives an Excon error too" do
|
|
66
|
+
allow(servers).to receive(:get)
|
|
67
|
+
.and_raise(Excon::Errors::SocketError.new(StandardError.new("nope")))
|
|
68
|
+
|
|
69
|
+
expect(driver.status(server_id: "12345")).to include(state: "unknown")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe "#doctor" do
|
|
75
|
+
let(:config) do
|
|
76
|
+
{ image_id: "img-1", public_key_path: "/home/user/.ssh/id_rsa.pub" }
|
|
77
|
+
end
|
|
78
|
+
let(:servers) do
|
|
79
|
+
instance_double(Fog::Compute::RackspaceV2::Servers, all: [])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
before do
|
|
83
|
+
allow(driver).to receive(:compute)
|
|
84
|
+
.and_return(double("compute", servers:))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "reports no problem when the configuration is complete" do
|
|
88
|
+
expect(driver.doctor(state)).to eq(false)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# `summary` is not a method fog-rackspace has. Stubbing the collection with
|
|
92
|
+
# a verifying double is what keeps that mistake from coming back.
|
|
93
|
+
it "asks Rackspace for something fog actually implements" do
|
|
94
|
+
driver.doctor(state)
|
|
95
|
+
|
|
96
|
+
expect(servers).to have_received(:all)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context "with no resolvable image" do
|
|
100
|
+
let(:config) { { public_key_path: "/key.pub" } }
|
|
101
|
+
|
|
102
|
+
it "reports a problem" do
|
|
103
|
+
allow(driver).to receive(:default_image).and_return(nil)
|
|
104
|
+
|
|
105
|
+
expect(driver.doctor(state)).to eq(true)
|
|
106
|
+
expect(logged_output.string).to match(/No image_id is set/)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context "with no public key" do
|
|
111
|
+
let(:config) { { image_id: "img-1", public_key_path: nil } }
|
|
112
|
+
|
|
113
|
+
it "reports a problem" do
|
|
114
|
+
expect(driver.doctor(state)).to eq(true)
|
|
115
|
+
expect(logged_output.string).to match(/No public key was found/)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
context "with credentials Rackspace rejects" do
|
|
120
|
+
it "reports a problem" do
|
|
121
|
+
allow(driver).to receive(:compute)
|
|
122
|
+
.and_raise(Fog::Errors::Error.new("unauthorized"))
|
|
123
|
+
|
|
124
|
+
expect(driver.doctor(state)).to eq(true)
|
|
125
|
+
expect(logged_output.string)
|
|
126
|
+
.to match(/rejected the configured credentials/)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "names the region it tried" do
|
|
130
|
+
config[:rackspace_region] = "ord"
|
|
131
|
+
allow(driver).to receive(:compute)
|
|
132
|
+
.and_raise(Fog::Errors::Error.new("unauthorized"))
|
|
133
|
+
|
|
134
|
+
driver.doctor(state)
|
|
135
|
+
|
|
136
|
+
expect(logged_output.string).to match(/region ord/)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "reports every problem it finds, not just the first" do
|
|
141
|
+
config.delete(:image_id)
|
|
142
|
+
config[:public_key_path] = nil
|
|
143
|
+
allow(driver).to receive(:default_image).and_return(nil)
|
|
144
|
+
|
|
145
|
+
driver.doctor(state)
|
|
146
|
+
|
|
147
|
+
expect(logged_output.string).to match(/No image_id is set/)
|
|
148
|
+
expect(logged_output.string).to match(/No public key was found/)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
|
|
4
|
+
describe "#tcp_check" do
|
|
5
|
+
before do
|
|
6
|
+
allow_any_instance_of(described_class).to receive(:wait_for_sshd)
|
|
7
|
+
allow_any_instance_of(described_class).to receive(:sleep)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
context "the default non-skipping behavior" do
|
|
11
|
+
it "uses Kitchen's own SSH check" do
|
|
12
|
+
expect(driver).to receive(:wait_for_sshd).with(state)
|
|
13
|
+
expect(driver).to_not receive(:sleep)
|
|
14
|
+
|
|
15
|
+
driver.send(:tcp_check, state)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "a config set to wait instead of TCP check" do
|
|
20
|
+
let(:config) { { no_ssh_tcp_check: true } }
|
|
21
|
+
|
|
22
|
+
it "uses a sleep instead of a port check" do
|
|
23
|
+
expect(driver).to_not receive(:wait_for_sshd)
|
|
24
|
+
expect(driver).to receive(:sleep)
|
|
25
|
+
|
|
26
|
+
driver.send(:tcp_check, state)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "sleeps for the configured number of seconds" do
|
|
30
|
+
config[:no_ssh_tcp_check_sleep] = 42
|
|
31
|
+
expect(driver).to receive(:sleep).with(42)
|
|
32
|
+
|
|
33
|
+
driver.send(:tcp_check, state)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
-
require "rake"
|
|
2
|
-
require "rspec"
|
|
3
1
|
require "logger"
|
|
4
2
|
require "stringio" unless defined?(StringIO)
|
|
5
3
|
require "kitchen"
|
|
6
4
|
|
|
7
5
|
require_relative "../lib/kitchen/driver/rackspace"
|
|
6
|
+
|
|
7
|
+
# `require "fog/rackspace"` only registers the services; it does not load the
|
|
8
|
+
# model classes. Load the ones the specs verify doubles against, so that a
|
|
9
|
+
# stubbed method which fog does not actually implement fails the example
|
|
10
|
+
# instead of quietly passing.
|
|
11
|
+
require "fog/rackspace/models/compute_v2/servers"
|
|
12
|
+
|
|
13
|
+
Dir[File.expand_path("support/**/*.rb", __dir__)].sort.each { |f| require f }
|
|
14
|
+
|
|
8
15
|
RSpec.configure do |config|
|
|
9
|
-
config.
|
|
10
|
-
config.
|
|
11
|
-
config.order =
|
|
16
|
+
config.disable_monkey_patching!
|
|
17
|
+
config.filter_run_when_matching :focus
|
|
18
|
+
config.order = :random
|
|
19
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
20
|
+
|
|
21
|
+
config.expect_with :rspec do |expectations|
|
|
22
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
config.mock_with :rspec do |mocks|
|
|
26
|
+
mocks.verify_partial_doubles = true
|
|
27
|
+
end
|
|
12
28
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Everything a driver needs to exist outside a real Test Kitchen run: an
|
|
2
|
+
# instance to belong to, a logger to talk to, and a config hash to read.
|
|
3
|
+
#
|
|
4
|
+
# Individual example groups override `config`, `platform_name`, and
|
|
5
|
+
# `instance_name` with `let` to describe the situation under test.
|
|
6
|
+
RSpec.shared_context "a driver for an instance" do
|
|
7
|
+
let(:logged_output) { StringIO.new }
|
|
8
|
+
let(:logger) { Logger.new(logged_output) }
|
|
9
|
+
let(:config) { {} }
|
|
10
|
+
let(:state) { {} }
|
|
11
|
+
let(:platform_name) { "ubuntu" }
|
|
12
|
+
let(:instance_name) { "potatoes" }
|
|
13
|
+
|
|
14
|
+
let(:instance) do
|
|
15
|
+
double("instance",
|
|
16
|
+
name: instance_name,
|
|
17
|
+
logger:,
|
|
18
|
+
to_str: "instance",
|
|
19
|
+
platform: double("platform", name: platform_name))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
let(:driver) { described_class.new(config) }
|
|
23
|
+
|
|
24
|
+
before do
|
|
25
|
+
allow_any_instance_of(described_class).to receive(:instance)
|
|
26
|
+
.and_return(instance)
|
|
27
|
+
ENV["RACKSPACE_USERNAME"] = "user"
|
|
28
|
+
ENV["RACKSPACE_API_KEY"] = "key"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# A server that has finished building and is reachable, as `#create` sees it.
|
|
33
|
+
RSpec.shared_context "a built server" do
|
|
34
|
+
let(:server) do
|
|
35
|
+
double("server",
|
|
36
|
+
id: "test123",
|
|
37
|
+
wait_for: true,
|
|
38
|
+
update: nil,
|
|
39
|
+
public_ip_address: "1.2.3.4",
|
|
40
|
+
private_ip_address: "10.9.8.7")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
before do
|
|
44
|
+
allow_any_instance_of(described_class).to receive(:default_name)
|
|
45
|
+
.and_return("a_monkey!")
|
|
46
|
+
allow_any_instance_of(described_class).to receive(:create_server)
|
|
47
|
+
.and_return(server)
|
|
48
|
+
allow_any_instance_of(described_class).to receive(:tcp_check)
|
|
49
|
+
.and_return(true)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# The driver reads its credentials and its region straight out of the
|
|
2
|
+
# environment, and several examples set those variables to prove it. Without
|
|
3
|
+
# this the values leak from one example into the next, and out of the suite
|
|
4
|
+
# into whatever else the process goes on to do.
|
|
5
|
+
RACKSPACE_ENVIRONMENT_VARIABLES = %w{
|
|
6
|
+
RACKSPACE_USERNAME
|
|
7
|
+
RACKSPACE_API_KEY
|
|
8
|
+
RACKSPACE_REGION
|
|
9
|
+
OS_USERNAME
|
|
10
|
+
OS_PASSWORD
|
|
11
|
+
OS_REGION_NAME
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
RSpec.configure do |config|
|
|
15
|
+
config.around do |example|
|
|
16
|
+
saved = RACKSPACE_ENVIRONMENT_VARIABLES.to_h { |name| [name, ENV.fetch(name, nil)] }
|
|
17
|
+
RACKSPACE_ENVIRONMENT_VARIABLES.each { |name| ENV.delete(name) }
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
example.run
|
|
21
|
+
ensure
|
|
22
|
+
saved.each do |name, value|
|
|
23
|
+
value.nil? ? ENV.delete(name) : ENV[name] = value
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-rackspace
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.22.
|
|
4
|
+
version: 0.22.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jonathan Hartman
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: fog-rackspace
|
|
@@ -77,6 +77,7 @@ files:
|
|
|
77
77
|
- ".gitignore"
|
|
78
78
|
- ".markdownlint.yaml"
|
|
79
79
|
- ".release-please-manifest.json"
|
|
80
|
+
- ".rspec"
|
|
80
81
|
- ".rubocop.yml"
|
|
81
82
|
- ".yamllint"
|
|
82
83
|
- ".yardopts"
|
|
@@ -94,8 +95,18 @@ files:
|
|
|
94
95
|
- lib/kitchen/driver/rackspace_version.rb
|
|
95
96
|
- release-please-config.json
|
|
96
97
|
- renovate.json
|
|
97
|
-
- spec/kitchen/driver/
|
|
98
|
+
- spec/kitchen/driver/rackspace/compute_spec.rb
|
|
99
|
+
- spec/kitchen/driver/rackspace/configuration_spec.rb
|
|
100
|
+
- spec/kitchen/driver/rackspace/create_spec.rb
|
|
101
|
+
- spec/kitchen/driver/rackspace/destroy_spec.rb
|
|
102
|
+
- spec/kitchen/driver/rackspace/image_spec.rb
|
|
103
|
+
- spec/kitchen/driver/rackspace/plugin_spec.rb
|
|
104
|
+
- spec/kitchen/driver/rackspace/server_name_spec.rb
|
|
105
|
+
- spec/kitchen/driver/rackspace/status_spec.rb
|
|
106
|
+
- spec/kitchen/driver/rackspace/tcp_check_spec.rb
|
|
98
107
|
- spec/spec_helper.rb
|
|
108
|
+
- spec/support/driver_context.rb
|
|
109
|
+
- spec/support/rackspace_environment.rb
|
|
99
110
|
homepage: https://github.com/test-kitchen/kitchen-rackspace
|
|
100
111
|
licenses:
|
|
101
112
|
- Apache-2.0
|