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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e15a887394d3ed26e459527e63a809e9cc8e0ac881c831eab8b71a9a585d5833
|
|
4
|
+
data.tar.gz: 23b98c7da4db1e2361bf0734069a6303ed1d976334977e296bb6e2935420d2b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '059d13472f0a85ce909b4ca0ad5b14873f19c88e7b8709d4181e9980d2e0663735d3512e9d61ca1e366a6bce1b0d96599b8e4975f242275e7b6779ca5d035a3f'
|
|
7
|
+
data.tar.gz: 365fead87993ea3f203f48de1512e747eb6cbe8d5de7746db9a025fce7ee570065d3eba539535978823391115b84f199f07ab809e2e0427db267f01b4819caa9
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,13 @@
|
|
|
9
9
|
* Docs: rewrite README for new users and split contributor docs ([#121](https://github.com/test-kitchen/kitchen-rackspace/pull/121)) ([638440a](https://github.com/test-kitchen/kitchen-rackspace/commit/638440a))
|
|
10
10
|
* Remove dependabot config in favor of renovate ([#122](https://github.com/test-kitchen/kitchen-rackspace/pull/122)) ([92dcec9](https://github.com/test-kitchen/kitchen-rackspace/commit/92dcec9))
|
|
11
11
|
|
|
12
|
+
## [0.22.1](https://github.com/test-kitchen/kitchen-rackspace/compare/v0.22.0...v0.22.1) (2026-08-30)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* check credentials with a call fog-rackspace actually implements ([#133](https://github.com/test-kitchen/kitchen-rackspace/issues/133)) ([a3b35fb](https://github.com/test-kitchen/kitchen-rackspace/commit/a3b35fb72290a2372d32f9b2e1156d49742ebaef))
|
|
18
|
+
|
|
12
19
|
## [0.22.0](https://github.com/test-kitchen/kitchen-rackspace/compare/v0.21.2...v0.22.0) (2026-08-24)
|
|
13
20
|
|
|
14
21
|
|
data/CONTRIBUTING.md
CHANGED
|
@@ -49,6 +49,38 @@ bundle exec cookstyle --chefstyle
|
|
|
49
49
|
back to stock RuboCop defaults, disagrees with this codebase about string
|
|
50
50
|
quoting, and reports over a hundred offenses that are not real.
|
|
51
51
|
|
|
52
|
+
### How the specs are laid out
|
|
53
|
+
|
|
54
|
+
There is one spec file per area of the driver, so the file you need to open is
|
|
55
|
+
the one named after the thing you changed:
|
|
56
|
+
|
|
57
|
+
| File | Covers |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `spec/kitchen/driver/rackspace/plugin_spec.rb` | API version and plugin version |
|
|
60
|
+
| `spec/kitchen/driver/rackspace/configuration_spec.rb` | `default_config` values, overrides, credentials from the environment |
|
|
61
|
+
| `spec/kitchen/driver/rackspace/image_spec.rb` | platform-name to image-ID resolution and the bundled `data/images.json` |
|
|
62
|
+
| `spec/kitchen/driver/rackspace/server_name_spec.rb` | the generated server name |
|
|
63
|
+
| `spec/kitchen/driver/rackspace/create_spec.rb` | `#create`, the RackConnect and service level waits, ServiceNet |
|
|
64
|
+
| `spec/kitchen/driver/rackspace/tcp_check_spec.rb` | waiting for the instance to accept a login |
|
|
65
|
+
| `spec/kitchen/driver/rackspace/destroy_spec.rb` | `#destroy` |
|
|
66
|
+
| `spec/kitchen/driver/rackspace/status_spec.rb` | `#status` and `#doctor` |
|
|
67
|
+
| `spec/kitchen/driver/rackspace/compute_spec.rb` | the fog connection, `#create_server`, and the network list |
|
|
68
|
+
|
|
69
|
+
`spec/support/` holds the shared setup:
|
|
70
|
+
|
|
71
|
+
- `driver_context.rb` — the `"a driver for an instance"` shared context, which
|
|
72
|
+
gives an example group a logger, a fake Test Kitchen instance, and a driver
|
|
73
|
+
built from `config`. Nearly every group includes it. `"a built server"` adds
|
|
74
|
+
a server double that has finished building.
|
|
75
|
+
- `rackspace_environment.rb` — clears `RACKSPACE_*` and `OS_*` out of the
|
|
76
|
+
environment around every example and puts your own back afterwards, so
|
|
77
|
+
credentials in your shell cannot make a spec pass or fail.
|
|
78
|
+
|
|
79
|
+
When you stub a fog object, reach for `instance_double` rather than `double`.
|
|
80
|
+
A plain double will happily answer a method fog does not have, which is how a
|
|
81
|
+
`kitchen doctor` that called a nonexistent `servers.summary` shipped with a
|
|
82
|
+
green test suite.
|
|
83
|
+
|
|
52
84
|
Many style offenses can be corrected automatically:
|
|
53
85
|
|
|
54
86
|
```sh
|
|
@@ -79,7 +111,7 @@ leave one running.
|
|
|
79
111
|
1. Fork the repository.
|
|
80
112
|
2. Create a feature branch off `main`.
|
|
81
113
|
3. Make your change, adding or updating tests to cover it.
|
|
82
|
-
4. Make sure `bundle exec
|
|
114
|
+
4. Make sure `bundle exec rake test` and `bundle exec cookstyle --chefstyle` pass.
|
|
83
115
|
5. Push the branch to your fork and open a pull request.
|
|
84
116
|
|
|
85
117
|
Please keep pull requests focused on a single change — it makes review much
|
data/README.md
CHANGED
|
@@ -223,16 +223,28 @@ All options below are set under the `driver:` key in `kitchen.yml`.
|
|
|
223
223
|
| `server_name` | *generated* | Name for the server. If unset, a unique name of at most 63 characters is generated from the base name, your username, the hostname, and a random string. |
|
|
224
224
|
| `user_data` | `nil` | Extra configuration data passed to the server at build time. |
|
|
225
225
|
| `config_drive` | `true` | Attach the read-only metadata config drive. |
|
|
226
|
-
| `no_passwd_lock` | `false` | Do not let the underlying fog library lock the root account. |
|
|
226
|
+
| `no_passwd_lock` | `false` | Do not let the underlying fog library lock the root account. Forced on when `rackconnect_wait` or `servicelevel_wait` is set — see [Networking](#networking). |
|
|
227
227
|
|
|
228
228
|
### Networking
|
|
229
229
|
|
|
230
230
|
| Option | Default | Description |
|
|
231
231
|
| --- | --- | --- |
|
|
232
|
-
| `networks` | PublicNet and ServiceNet |
|
|
232
|
+
| `networks` | *PublicNet and ServiceNet* | **Additional** Rackspace network UUIDs to attach. PublicNet and ServiceNet are always attached too — see below. |
|
|
233
233
|
| `servicenet` | `false` | Connect over the ServiceNet address rather than the public one. |
|
|
234
|
-
| `rackconnect_wait` | `false` | Wait for RackConnect to finish before continuing. Enable this if the account uses RackConnect. |
|
|
235
|
-
| `servicelevel_wait` | `false` | Wait for Managed Service Level automation to finish before continuing. |
|
|
234
|
+
| `rackconnect_wait` | `false` | Wait for RackConnect to finish before continuing. Enable this if the account uses RackConnect. Forces `no_passwd_lock` on. |
|
|
235
|
+
| `servicelevel_wait` | `false` | Wait for Managed Service Level automation to finish before continuing. Forces `no_passwd_lock` on. |
|
|
236
|
+
|
|
237
|
+
`networks` adds to the standard networks rather than replacing them. Whatever
|
|
238
|
+
you list, the driver puts Rackspace's PublicNet
|
|
239
|
+
(`00000000-0000-0000-0000-000000000000`) and ServiceNet
|
|
240
|
+
(`11111111-1111-1111-1111-111111111111`) at the front of the list first, so
|
|
241
|
+
there is no way to build a server without a public interface. Do not list
|
|
242
|
+
either of those UUIDs yourself — you will just send Rackspace a duplicate.
|
|
243
|
+
|
|
244
|
+
`rackconnect_wait` and `servicelevel_wait` both turn `no_passwd_lock` on
|
|
245
|
+
regardless of how you set it, because RackConnect and Managed Service Level
|
|
246
|
+
each log in as root to do their work and a locked root account leaves them
|
|
247
|
+
stuck.
|
|
236
248
|
|
|
237
249
|
### SSH
|
|
238
250
|
|
|
@@ -246,7 +258,7 @@ All options below are set under the `driver:` key in `kitchen.yml`.
|
|
|
246
258
|
|
|
247
259
|
| Option | Default | Description |
|
|
248
260
|
| --- | --- | --- |
|
|
249
|
-
| `wait_for` | `600` | Seconds to wait
|
|
261
|
+
| `wait_for` | `600` | Seconds any single wait may take before timing out. This is fog's global timeout, so it applies to the build wait and to the `rackconnect_wait` and `servicelevel_wait` polls individually, not to `kitchen create` as a whole. |
|
|
250
262
|
| `no_ssh_tcp_check` | `false` | Skip the TCP check on the SSH port. Use when a firewall makes the check unreliable. |
|
|
251
263
|
| `no_ssh_tcp_check_sleep` | `120` | Seconds to sleep instead of checking, when `no_ssh_tcp_check` is enabled. |
|
|
252
264
|
|
|
@@ -266,12 +278,22 @@ Image IDs are per-region, so `image_id` and `rackspace_region` travel together.
|
|
|
266
278
|
|
|
267
279
|
### Connecting over ServiceNet
|
|
268
280
|
|
|
281
|
+
```yaml
|
|
282
|
+
driver:
|
|
283
|
+
name: rackspace
|
|
284
|
+
servicenet: true
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`servicenet` is all you need — ServiceNet is attached either way, and this
|
|
288
|
+
tells the driver to hand the transport the private address instead of the
|
|
289
|
+
public one. Use `networks` only to attach an *extra* network of your own:
|
|
290
|
+
|
|
269
291
|
```yaml
|
|
270
292
|
driver:
|
|
271
293
|
name: rackspace
|
|
272
294
|
servicenet: true
|
|
273
295
|
networks:
|
|
274
|
-
-
|
|
296
|
+
- 4b1c1b3a-8f0f-4a1e-9d33-6f3c7ab2e5d9 # your isolated cloud network
|
|
275
297
|
```
|
|
276
298
|
|
|
277
299
|
### RackConnect accounts
|
|
@@ -74,6 +74,11 @@ module Kitchen
|
|
|
74
74
|
required_config :image_id
|
|
75
75
|
required_config :public_key_path
|
|
76
76
|
|
|
77
|
+
# Sets up the driver and applies +wait_for+ as fog's global timeout.
|
|
78
|
+
#
|
|
79
|
+
# +Fog.timeout+ is process-wide, so the last driver constructed wins if
|
|
80
|
+
# several are in play.
|
|
81
|
+
#
|
|
77
82
|
# @param config [Hash] the driver configuration
|
|
78
83
|
def initialize(config)
|
|
79
84
|
super
|
|
@@ -172,7 +177,8 @@ module Kitchen
|
|
|
172
177
|
# Checks the configuration for the mistakes that only show up as a
|
|
173
178
|
# confusing failure part way through +create+.
|
|
174
179
|
#
|
|
175
|
-
# @param state [Hash]
|
|
180
|
+
# @param state [Hash] instance state; accepted for the Test Kitchen hook
|
|
181
|
+
# signature and not read
|
|
176
182
|
# @return [Boolean] true when a problem was reported
|
|
177
183
|
def doctor(state) # rubocop:disable Lint/UnusedMethodArgument
|
|
178
184
|
problems = []
|
|
@@ -198,9 +204,14 @@ module Kitchen
|
|
|
198
204
|
# Confirms the configured credentials can actually talk to Rackspace,
|
|
199
205
|
# which is cheaper to learn here than half way through a converge.
|
|
200
206
|
#
|
|
207
|
+
# Listing servers is the cheapest authenticated call fog-rackspace
|
|
208
|
+
# offers: +GET /servers+ returns only an ID and a name per server, and
|
|
209
|
+
# it exercises the compute endpoint for the configured region rather
|
|
210
|
+
# than just the identity endpoint.
|
|
211
|
+
#
|
|
201
212
|
# @return [Array<String>] a problem description, or an empty array
|
|
202
213
|
def credential_problems
|
|
203
|
-
compute.servers.
|
|
214
|
+
compute.servers.all
|
|
204
215
|
[]
|
|
205
216
|
rescue Fog::Errors::Error, Excon::Errors::Error => e
|
|
206
217
|
["Rackspace rejected the configured credentials for region " \
|
|
@@ -237,13 +248,19 @@ module Kitchen
|
|
|
237
248
|
# unlocked to do their work, so +no_passwd_lock+ is forced on whenever
|
|
238
249
|
# either wait is requested, regardless of how it was configured.
|
|
239
250
|
#
|
|
251
|
+
# @note fog's +bootstrap+ calls +Server#setup+, which rescues
|
|
252
|
+
# +Errno::ECONNREFUSED+ and retries forever, one second apart. A server
|
|
253
|
+
# that never opens port 22 hangs here rather than failing, and
|
|
254
|
+
# +wait_for+ does not bound it.
|
|
255
|
+
#
|
|
240
256
|
# @return [Fog::Compute::RackspaceV2::Server] the newly built server
|
|
241
257
|
def create_server
|
|
242
258
|
server_def = { name: config[:server_name], networks: }
|
|
243
259
|
%i{image_id flavor_id public_key_path no_passwd_lock user_data config_drive}.each do |opt|
|
|
244
260
|
server_def[opt] = config[opt]
|
|
245
261
|
end
|
|
246
|
-
#
|
|
262
|
+
# RackConnect and Managed Service Level need the root password left
|
|
263
|
+
# unlocked; see the note above.
|
|
247
264
|
no_passwd_lock = config[:rackconnect_wait] || config[:servicelevel_wait]
|
|
248
265
|
server_def[:no_passwd_lock] = no_passwd_lock if no_passwd_lock
|
|
249
266
|
compute.servers.bootstrap(server_def)
|
|
@@ -275,8 +292,13 @@ module Kitchen
|
|
|
275
292
|
puts "(ssh ready)"
|
|
276
293
|
end
|
|
277
294
|
|
|
295
|
+
# Blocks until the configured transport can connect to the instance.
|
|
296
|
+
#
|
|
278
297
|
# Kitchen::Driver::SSHBase used to supply this; the configured transport
|
|
279
298
|
# knows how to wait for the instance to accept connections.
|
|
299
|
+
#
|
|
300
|
+
# @param state [Hash] instance state describing how to connect
|
|
301
|
+
# @return [void]
|
|
280
302
|
def wait_for_sshd(state)
|
|
281
303
|
instance.transport.connection(state, &:wait_until_ready)
|
|
282
304
|
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
|
|
4
|
+
describe "#compute" do
|
|
5
|
+
let(:config) do
|
|
6
|
+
{
|
|
7
|
+
rackspace_username: "monkey",
|
|
8
|
+
rackspace_api_key: "potato",
|
|
9
|
+
rackspace_region: "ord",
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
context "all requirements provided" do
|
|
14
|
+
it "creates a new compute connection" do
|
|
15
|
+
allow(Fog::Compute).to receive(:new) { |arg| arg }
|
|
16
|
+
|
|
17
|
+
expect(driver.send(:compute)).to eq(
|
|
18
|
+
provider: "Rackspace",
|
|
19
|
+
version: "v2",
|
|
20
|
+
rackspace_username: "monkey",
|
|
21
|
+
rackspace_api_key: "potato",
|
|
22
|
+
rackspace_region: "ord"
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "no username provided" do
|
|
28
|
+
let(:config) { { rackspace_username: nil, rackspace_api_key: "1234" } }
|
|
29
|
+
|
|
30
|
+
it "raises an error" do
|
|
31
|
+
expect { driver.send(:compute) }.to raise_error(ArgumentError)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context "no API key provided" do
|
|
36
|
+
let(:config) { { rackspace_username: "monkey", rackspace_api_key: nil } }
|
|
37
|
+
|
|
38
|
+
it "raises an error" do
|
|
39
|
+
expect { driver.send(:compute) }.to raise_error(ArgumentError)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "#create_server" do
|
|
45
|
+
subject(:server_def) { driver.send(:create_server) }
|
|
46
|
+
|
|
47
|
+
let(:config) do
|
|
48
|
+
{
|
|
49
|
+
server_name: "hello",
|
|
50
|
+
image_id: "there",
|
|
51
|
+
flavor_id: "captain",
|
|
52
|
+
public_key_path: "tarpals",
|
|
53
|
+
no_passwd_lock: false,
|
|
54
|
+
networks: nil,
|
|
55
|
+
user_data: nil,
|
|
56
|
+
config_drive: false,
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
let(:servers) do
|
|
60
|
+
collection = instance_double(Fog::Compute::RackspaceV2::Servers)
|
|
61
|
+
allow(collection).to receive(:bootstrap) { |arg| arg }
|
|
62
|
+
collection
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
before do
|
|
66
|
+
allow_any_instance_of(described_class).to receive(:compute)
|
|
67
|
+
.and_return(double("compute", servers:))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "creates the server using a compute connection" do
|
|
71
|
+
expect(server_def).to eq(
|
|
72
|
+
name: "hello",
|
|
73
|
+
image_id: "there",
|
|
74
|
+
flavor_id: "captain",
|
|
75
|
+
public_key_path: "tarpals",
|
|
76
|
+
no_passwd_lock: false,
|
|
77
|
+
networks: nil,
|
|
78
|
+
user_data: nil,
|
|
79
|
+
config_drive: false
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "passes user data through" do
|
|
84
|
+
config[:user_data] = "#cloud-config\n"
|
|
85
|
+
|
|
86
|
+
expect(server_def[:user_data]).to eq("#cloud-config\n")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "passes the config drive setting through" do
|
|
90
|
+
config[:config_drive] = true
|
|
91
|
+
|
|
92
|
+
expect(server_def[:config_drive]).to eq(true)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# RackConnect and Managed Service Level both need to log in as root to do
|
|
96
|
+
# their work, so the driver overrides no_passwd_lock whenever either wait
|
|
97
|
+
# is requested. Getting this wrong leaves the automation stuck forever.
|
|
98
|
+
context "rackconnect_wait is set" do
|
|
99
|
+
before { config[:rackconnect_wait] = true }
|
|
100
|
+
|
|
101
|
+
it "unlocks the root password even though the option says otherwise" do
|
|
102
|
+
expect(server_def[:no_passwd_lock]).to eq(true)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
context "servicelevel_wait is set" do
|
|
107
|
+
before { config[:servicelevel_wait] = true }
|
|
108
|
+
|
|
109
|
+
it "unlocks the root password even though the option says otherwise" do
|
|
110
|
+
expect(server_def[:no_passwd_lock]).to eq(true)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context "neither wait is set" do
|
|
115
|
+
it "leaves the configured no_passwd_lock alone" do
|
|
116
|
+
config[:no_passwd_lock] = true
|
|
117
|
+
|
|
118
|
+
expect(server_def[:no_passwd_lock]).to eq(true)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "leaves a false no_passwd_lock alone" do
|
|
122
|
+
expect(server_def[:no_passwd_lock]).to eq(false)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context "additional networks specified" do
|
|
127
|
+
before { config[:networks] = %w{bob_dole} }
|
|
128
|
+
|
|
129
|
+
it "keeps PublicNet and ServiceNet ahead of the custom network" do
|
|
130
|
+
expect(server_def[:networks]).to eq(
|
|
131
|
+
%w{
|
|
132
|
+
00000000-0000-0000-0000-000000000000
|
|
133
|
+
11111111-1111-1111-1111-111111111111
|
|
134
|
+
bob_dole
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe "#networks" do
|
|
142
|
+
context "the default Rackspace networks" do
|
|
143
|
+
it "returns nil so Fog will use the defaults" do
|
|
144
|
+
expect(driver.send(:networks)).to be_nil
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
context "a custom Rackspace network" do
|
|
149
|
+
let(:config) { { networks: %w{abcdefg} } }
|
|
150
|
+
|
|
151
|
+
it "returns the base networks plus the custom one" do
|
|
152
|
+
expect(driver.send(:networks)).to eq(
|
|
153
|
+
%w{
|
|
154
|
+
00000000-0000-0000-0000-000000000000
|
|
155
|
+
11111111-1111-1111-1111-111111111111
|
|
156
|
+
abcdefg
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
RSpec.describe Kitchen::Driver::Rackspace do
|
|
2
|
+
include_context "a driver for an instance"
|
|
3
|
+
|
|
4
|
+
before do
|
|
5
|
+
allow(Fog).to receive(:timeout=)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "default options" do
|
|
9
|
+
it "defaults to v2 cloud" do
|
|
10
|
+
expect(driver[:version]).to eq("v2")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "defaults to a 2 GB general purpose flavor" do
|
|
14
|
+
expect(driver[:flavor_id]).to eq("general1-2")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "defaults to local user's SSH public key" do
|
|
18
|
+
expect(File).to receive(:exist?).with(%r{/.ssh/id_rsa.pub$})
|
|
19
|
+
.and_return(true)
|
|
20
|
+
|
|
21
|
+
expect(driver[:public_key_path]).to match(%r{/.ssh/id_rsa.pub$})
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "falls through the other key types when there is no RSA key" do
|
|
25
|
+
allow(File).to receive(:exist?).and_return(false)
|
|
26
|
+
allow(File).to receive(:exist?).with(%r{/.ssh/id_ecdsa.pub$})
|
|
27
|
+
.and_return(true)
|
|
28
|
+
|
|
29
|
+
expect(driver[:public_key_path]).to match(%r{/.ssh/id_ecdsa.pub$})
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "leaves public_key_path unset when ~/.ssh holds no public key" do
|
|
33
|
+
allow(File).to receive(:exist?).and_return(false)
|
|
34
|
+
|
|
35
|
+
expect(driver[:public_key_path]).to be_nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "defaults to SSH with root user on port 22" do
|
|
39
|
+
expect(driver[:username]).to eq("root")
|
|
40
|
+
expect(driver[:port]).to eq("22")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "defaults to a random server name" do
|
|
44
|
+
expect(driver[:server_name]).to match(/^potatoes-/)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "defaults to the DFW region" do
|
|
48
|
+
expect(driver[:rackspace_region]).to eq("dfw")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "defaults to username from $RACKSPACE_USERNAME" do
|
|
52
|
+
expect(driver[:rackspace_username]).to eq("user")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "defaults to API key from $RACKSPACE_API_KEY" do
|
|
56
|
+
expect(driver[:rackspace_api_key]).to eq("key")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "defaults to wait_for timeout of 600 seconds" do
|
|
60
|
+
expect(driver[:wait_for]).to eq(600)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "defaults the SSH TCP bypassing to false" do
|
|
64
|
+
expect(driver[:no_ssh_tcp_check]).to eq(false)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "defaults the TCP bypass sleep time to 120 seconds" do
|
|
68
|
+
expect(driver[:no_ssh_tcp_check_sleep]).to eq(120)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "defaults to the standard Rackspace networks" do
|
|
72
|
+
expect(driver[:networks]).to eq(nil)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "defaults to not waiting for rackconnect" do
|
|
76
|
+
expect(driver[:rackconnect_wait]).to eq(false)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "defaults to not waiting for managed service level" do
|
|
80
|
+
expect(driver[:servicelevel_wait]).to eq(false)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "defaults to the public ip address" do
|
|
84
|
+
expect(driver[:servicenet]).to eq(false)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "defaults to no_passwd_lock as false" do
|
|
88
|
+
expect(driver[:no_passwd_lock]).to eq(false)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "defaults to attaching the config drive" do
|
|
92
|
+
expect(driver[:config_drive]).to eq(true)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "defaults to sending no user data" do
|
|
96
|
+
expect(driver[:user_data]).to eq(nil)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe "overridden options" do
|
|
101
|
+
overrides = {
|
|
102
|
+
version: "v3",
|
|
103
|
+
image_id: "22",
|
|
104
|
+
flavor_id: "33",
|
|
105
|
+
public_key_path: "/tmp",
|
|
106
|
+
username: "admin",
|
|
107
|
+
port: "2222",
|
|
108
|
+
server_name: "puppy",
|
|
109
|
+
rackspace_region: "ord",
|
|
110
|
+
wait_for: 1200,
|
|
111
|
+
rackconnect_wait: true,
|
|
112
|
+
servicelevel_wait: true,
|
|
113
|
+
servicenet: true,
|
|
114
|
+
no_passwd_lock: true,
|
|
115
|
+
no_ssh_tcp_check: true,
|
|
116
|
+
no_ssh_tcp_check_sleep: 180,
|
|
117
|
+
config_drive: false,
|
|
118
|
+
user_data: "#cloud-config\n",
|
|
119
|
+
networks: %w{abcdefg},
|
|
120
|
+
}
|
|
121
|
+
let(:config) { overrides.dup }
|
|
122
|
+
|
|
123
|
+
# Every key here is a real `default_config` on the driver. Asserting that
|
|
124
|
+
# first keeps this list from drifting into options that do not exist --
|
|
125
|
+
# `driver[:whatever]` happily echoes back anything you put in the config
|
|
126
|
+
# hash, so a test for a made-up option passes without proving anything.
|
|
127
|
+
it "only names options the driver actually declares" do
|
|
128
|
+
expect(described_class.defaults.keys).to include(*overrides.keys)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
overrides.each do |key, value|
|
|
132
|
+
it "uses the overridden #{key} option" do
|
|
133
|
+
expect(driver[key]).to eq(value)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe "credentials from the environment" do
|
|
139
|
+
it "reads the username from $RACKSPACE_USERNAME" do
|
|
140
|
+
ENV["RACKSPACE_USERNAME"] = "rax_user"
|
|
141
|
+
|
|
142
|
+
expect(driver[:rackspace_username]).to eq("rax_user")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "falls back to $OS_USERNAME" do
|
|
146
|
+
ENV.delete("RACKSPACE_USERNAME")
|
|
147
|
+
ENV["OS_USERNAME"] = "os_user"
|
|
148
|
+
|
|
149
|
+
expect(driver[:rackspace_username]).to eq("os_user")
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "prefers $RACKSPACE_USERNAME over $OS_USERNAME" do
|
|
153
|
+
ENV["RACKSPACE_USERNAME"] = "rax_user"
|
|
154
|
+
ENV["OS_USERNAME"] = "os_user"
|
|
155
|
+
|
|
156
|
+
expect(driver[:rackspace_username]).to eq("rax_user")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "falls back to $OS_PASSWORD for the API key" do
|
|
160
|
+
ENV.delete("RACKSPACE_API_KEY")
|
|
161
|
+
ENV["OS_PASSWORD"] = "os_pass"
|
|
162
|
+
|
|
163
|
+
expect(driver[:rackspace_api_key]).to eq("os_pass")
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "prefers $RACKSPACE_API_KEY over $OS_PASSWORD" do
|
|
167
|
+
ENV["RACKSPACE_API_KEY"] = "rax_key"
|
|
168
|
+
ENV["OS_PASSWORD"] = "os_pass"
|
|
169
|
+
|
|
170
|
+
expect(driver[:rackspace_api_key]).to eq("rax_key")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "falls back to $OS_REGION_NAME for the region" do
|
|
174
|
+
ENV["OS_REGION_NAME"] = "os_region"
|
|
175
|
+
|
|
176
|
+
expect(driver[:rackspace_region]).to eq("os_region")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it "prefers $RACKSPACE_REGION over $OS_REGION_NAME" do
|
|
180
|
+
ENV["RACKSPACE_REGION"] = "ord"
|
|
181
|
+
ENV["OS_REGION_NAME"] = "os_region"
|
|
182
|
+
|
|
183
|
+
expect(driver[:rackspace_region]).to eq("ord")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it "falls back to dfw when neither region variable is set" do
|
|
187
|
+
expect(driver[:rackspace_region]).to eq("dfw")
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe "the fog timeout" do
|
|
192
|
+
it "hands wait_for to fog as its global timeout" do
|
|
193
|
+
described_class.new(wait_for: 1200)
|
|
194
|
+
|
|
195
|
+
expect(Fog).to have_received(:timeout=).with(1200)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "uses the wait_for default when the option is not set" do
|
|
199
|
+
described_class.new({})
|
|
200
|
+
|
|
201
|
+
expect(Fog).to have_received(:timeout=).with(600)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "coerces a wait_for given as a string" do
|
|
205
|
+
described_class.new(wait_for: "1200")
|
|
206
|
+
|
|
207
|
+
expect(Fog).to have_received(:timeout=).with(1200)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|