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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 341c3ad7208f8a4747c8f1ea02a0729e0902de1aa9c953015b85dd597138809e
4
- data.tar.gz: aefd1c800711e40f056620f7f77f597a98cd7773f81e4c2a7f224554a338f205
3
+ metadata.gz: e15a887394d3ed26e459527e63a809e9cc8e0ac881c831eab8b71a9a585d5833
4
+ data.tar.gz: 23b98c7da4db1e2361bf0734069a6303ed1d976334977e296bb6e2935420d2b5
5
5
  SHA512:
6
- metadata.gz: 1c8b613a15c6cfa635246d9fa0b20f53910ba99015c8550c532067205b3fd7da0ac39070b158eb01d018660303dfec891a07c949394896e856f8b619d094f170
7
- data.tar.gz: e1d15ce47f981dcd4e187fb93b2fb53e1064ce246efd993222fa60988f952ac7bcaa8c446b9c3e501526d5d9db0b8940ebe4bf20fb56eb82c603ce47b69066d2
6
+ metadata.gz: '059d13472f0a85ce909b4ca0ad5b14873f19c88e7b8709d4181e9980d2e0663735d3512e9d61ca1e366a6bce1b0d96599b8e4975f242275e7b6779ca5d035a3f'
7
+ data.tar.gz: 365fead87993ea3f203f48de1512e747eb6cbe8d5de7746db9a025fce7ee570065d3eba539535978823391115b84f199f07ab809e2e0427db267f01b4819caa9
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.22.0"
2
+ ".": "0.22.1"
3
3
  }
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 rspec` and `bundle exec cookstyle` pass.
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 | Array of Rackspace network UUIDs to attach. |
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 for the server to become available before timing out. |
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
- - 11111111-1111-1111-1111-111111111111
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] mutable instance and driver state
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.summary
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
- # see @note on bootstrap def about rackconnect
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
@@ -4,6 +4,6 @@ module Kitchen
4
4
  # Test Kitchen's driver plugins.
5
5
  module Driver
6
6
  # The version of the kitchen-rackspace gem.
7
- RACKSPACE_VERSION = "0.22.0"
7
+ RACKSPACE_VERSION = "0.22.1"
8
8
  end
9
9
  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