kitchen-openstack 7.0.1 → 8.0.0

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.
@@ -34,8 +34,17 @@ require_relative "openstack/volume"
34
34
 
35
35
  module Kitchen
36
36
  module Driver
37
- # This takes from the Base Class and creates the OpenStack driver.
37
+ # Test Kitchen driver for OpenStack Nova.
38
+ #
39
+ # Creates and destroys Nova instances, optionally attaching floating IPs,
40
+ # Cinder volumes and specific Neutron networks. Credentials come from
41
+ # kitchen.yml, `OS_*` environment variables, or a standard
42
+ # `clouds.yaml` -- see {Clouds} for the precedence rules.
38
43
  class Openstack < Kitchen::Driver::Base
44
+ # Settings Fog requires as Strings. Fog re-coerces anything that looks
45
+ # numeric back to an Integer, so these are stringified on the way in.
46
+ #
47
+ # @return [Array<Symbol>]
39
48
  FOG_STRING_SETTINGS = %i{
40
49
  openstack_username
41
50
  openstack_api_key
@@ -70,13 +79,6 @@ module Kitchen
70
79
  default_config :clouds_yaml_path, nil
71
80
  default_config :server_name, nil
72
81
 
73
- # Merge clouds.yaml values into config so they are visible in
74
- # `kitchen diagnose` and available to all driver methods.
75
- def finalize_config!(instance)
76
- super
77
- apply_clouds_config
78
- self
79
- end
80
82
  default_config :server_name_prefix, nil
81
83
  default_config :key_name, nil
82
84
  default_config :port, "22"
@@ -103,6 +105,28 @@ module Kitchen
103
105
  default_config :write_timeout, 60
104
106
  default_config :metadata, nil
105
107
 
108
+ # Merges clouds.yaml and `OS_*` values into the config hash.
109
+ #
110
+ # Done at finalize time rather than lazily so the resolved values show up
111
+ # in `kitchen diagnose` and are available to every driver method.
112
+ #
113
+ # @param instance [Kitchen::Instance] the instance this driver serves
114
+ # @return [self]
115
+ def finalize_config!(instance)
116
+ super
117
+ apply_clouds_config
118
+ self
119
+ end
120
+
121
+ # Creates a Nova instance and waits until it is reachable.
122
+ #
123
+ # Idempotent: returns immediately if `state` already names a server.
124
+ #
125
+ # @param state [Hash] mutable instance state; gains `:server_id` and
126
+ # `:hostname`
127
+ # @return [void]
128
+ # @raise [Kitchen::ActionFailed] on any Fog or Excon failure
129
+ # @raise [Kitchen::InstanceFailure] if the server builds to ERROR state
106
130
  def create(state)
107
131
  config_server_name
108
132
  if state[:server_id]
@@ -138,6 +162,14 @@ module Kitchen
138
162
  raise ActionFailed, e.message
139
163
  end
140
164
 
165
+ # Destroys the Nova instance named by `state`, releasing its floating IP
166
+ # first when this driver allocated one.
167
+ #
168
+ # Safe to call when the server is already gone.
169
+ #
170
+ # @param state [Hash] mutable instance state; loses `:server_id` and
171
+ # `:hostname`
172
+ # @return [void]
141
173
  def destroy(state)
142
174
  return if state[:server_id].nil?
143
175
 
@@ -150,12 +182,7 @@ module Kitchen
150
182
  pub, priv = get_public_private_ips(server)
151
183
  pub, = parse_ips(pub, priv)
152
184
  pub_ip = pub[config[:public_ip_order].to_i] || nil
153
- if pub_ip
154
- info "Retrieve the ID of floating IP <#{pub_ip}>"
155
- floating_ip_id = network.list_floating_ips(floating_ip_address: pub_ip).body["floatingips"][0]["id"]
156
- network.delete_floating_ip(floating_ip_id)
157
- info "OpenStack Floating IP <#{pub_ip}> released."
158
- end
185
+ release_floating_ip(pub_ip) if pub_ip
159
186
  end
160
187
  server.destroy
161
188
  end
@@ -166,6 +193,29 @@ module Kitchen
166
193
 
167
194
  private
168
195
 
196
+ # Releases a floating IP back to its pool.
197
+ #
198
+ # A floating IP that Neutron no longer knows about is not an error worth
199
+ # failing a destroy over, so an unknown address is logged and skipped.
200
+ #
201
+ # @param pub_ip [String] the floating IP to release
202
+ # @return [void]
203
+ def release_floating_ip(pub_ip)
204
+ info "Retrieve the ID of floating IP <#{pub_ip}>"
205
+ net = network
206
+ floating_ips = net.list_floating_ips(floating_ip_address: pub_ip).body["floatingips"]
207
+ if floating_ips.nil? || floating_ips.empty?
208
+ warn "No floating IP found matching <#{pub_ip}>; nothing to release."
209
+ return
210
+ end
211
+
212
+ net.delete_floating_ip(floating_ips[0]["id"])
213
+ info "OpenStack Floating IP <#{pub_ip}> released."
214
+ end
215
+
216
+ # Builds the settings hash handed to every Fog service constructor.
217
+ #
218
+ # @return [Hash] Fog connection settings
169
219
  def openstack_server
170
220
  server_def = {
171
221
  connection_options: {},
@@ -176,36 +226,61 @@ module Kitchen
176
226
  server_def
177
227
  end
178
228
 
229
+ # Settings always sent to Fog, even when nil.
230
+ #
231
+ # @return [Array<Symbol>]
179
232
  def required_server_settings
180
233
  %i{openstack_username openstack_api_key openstack_auth_url openstack_domain_id}
181
234
  end
182
235
 
236
+ # Every other `openstack_*` setting Fog recognizes, sent only when set.
237
+ #
238
+ # @return [Array<Symbol>]
183
239
  def optional_server_settings
184
240
  Fog::OpenStack::Compute.recognized.select do |k|
185
241
  k.to_s.start_with?("openstack")
186
242
  end - required_server_settings
187
243
  end
188
244
 
245
+ # Settings passed through to Excon rather than to Fog itself.
246
+ #
247
+ # `ssl_ca_file` belongs here, not in the Fog settings: Fog does not
248
+ # recognize it, so a CA bundle from `OS_CACERT` or a clouds.yaml
249
+ # `cacert` entry would otherwise be parsed and then silently dropped.
250
+ #
251
+ # @return [Array<Symbol>]
189
252
  def connection_options
190
- %i{read_timeout write_timeout connect_timeout}
253
+ %i{read_timeout write_timeout connect_timeout ssl_ca_file}
191
254
  end
192
255
 
256
+ # @return [Fog::OpenStack::Network] a Neutron connection
193
257
  def network
194
258
  Fog::OpenStack::Network.new(openstack_server)
195
259
  end
196
260
 
261
+ # @return [Fog::OpenStack::Compute] a Nova connection
197
262
  def compute
198
263
  Fog::OpenStack::Compute.new(openstack_server)
199
264
  end
200
265
 
266
+ # @return [Kitchen::Driver::Openstack::Volume] a Cinder helper
201
267
  def volume
202
268
  Volume.new(logger)
203
269
  end
204
270
 
271
+ # Resolves the block device mapping to send to Nova.
272
+ #
273
+ # @param config [Hash] the driver config
274
+ # @return [Hash] a Nova block device mapping
205
275
  def get_bdm(config)
206
276
  volume.get_bdm(config, openstack_server)
207
277
  end
208
278
 
279
+ # Coerces one setting to the type Fog expects.
280
+ #
281
+ # @param setting [Symbol] the Fog config key
282
+ # @param value [Object] the configured value
283
+ # @return [Object] the coerced value
209
284
  def normalize_fog_setting(setting, value)
210
285
  return value if value.nil?
211
286
  return normalize_identity_api_version(value) if setting == :openstack_identity_api_version
@@ -214,11 +289,20 @@ module Kitchen
214
289
  value.to_s
215
290
  end
216
291
 
292
+ # Normalizes the identity API version into a form Fog will not mangle.
293
+ #
217
294
  # Fog::Service#coerce_options re-coerces any value where
218
295
  # `value.to_s.to_i.to_s == value.to_s` back to an Integer, which
219
296
  # then breaks Fog::OpenStack::Auth::Token.build (it calls `=~`
220
297
  # on the value). Prefixing with "v" keeps Fog from coercing and
221
298
  # still satisfies Token.build's `/(v)*2(\.0)*/i` regex check.
299
+ #
300
+ # @example
301
+ # normalize_identity_api_version(3) #=> "v3"
302
+ # normalize_identity_api_version("2.0") #=> "v2.0"
303
+ #
304
+ # @param value [String, Integer] the configured identity API version
305
+ # @return [String] a version string prefixed with "v"
222
306
  def normalize_identity_api_version(value)
223
307
  str = value.to_s.strip
224
308
  return str if str.empty?
@@ -23,6 +23,12 @@ module Kitchen
23
23
  #
24
24
  # @author Jonathan Hartman <j@p4nt5.com>
25
25
  module Driver
26
- OPENSTACK_VERSION = "7.0.1"
26
+ # The kitchen-openstack gem version.
27
+ #
28
+ # Read by the gemspec and bumped by Release Please, so it must stay a
29
+ # plain string literal on a single line.
30
+ #
31
+ # @return [String]
32
+ OPENSTACK_VERSION = "8.0.0"
27
33
  end
28
34
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-openstack
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.1
4
+ version: 8.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Hartman
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2026-04-24 00:00:00.000000000 Z
12
+ date: 2026-08-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: test-kitchen