kitchen-vra 3.3.4 → 3.4.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/.gitignore +0 -1
- data/.release-please-manifest.json +1 -1
- data/.rspec +2 -0
- data/.rubocop.yml +0 -1
- data/.yardopts +1 -1
- data/CHANGELOG.md +20 -0
- data/Gemfile +0 -4
- data/kitchen-vra.gemspec +1 -2
- data/lib/kitchen/driver/vra.rb +79 -16
- data/lib/kitchen/driver/vra_version.rb +1 -1
- data/spec/spec_helper.rb +43 -5
- data/spec/support/vra_api.rb +120 -0
- data/spec/vra_spec.rb +504 -180
- metadata +13 -25
- /data/{LICENSE.txt → LICENSE} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3077884f3c8f5e434889905e6c4d47e06ecf308fe810948c79d644e5f13086bd
|
|
4
|
+
data.tar.gz: addc3a87013a8834a75392625e026b5efdf1cfaff8009e0eaa8e690236b0be84
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2d6521702e1cc486f91d1aa2c0d1c17fdd1feaece55a8b634aaea65950282bde3b327e4bb79e7c256a6683ba8128b36ef5b59852b2c1f0de763d64dc4aa359a2
|
|
7
|
+
data.tar.gz: 190fa3d7bbfc3fd0db0220934390545f9b28cd4565126648d0503459de10035962f795b69275599409671aa15d2f5d31a018074f7b59d4a092f83437b027d8cd
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.rubocop.yml
CHANGED
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,26 @@
|
|
|
10
10
|
* Docs: rewrite README for new users and split contributor docs ([#80](https://github.com/test-kitchen/kitchen-vra/pull/80)) ([d9ebc45](https://github.com/test-kitchen/kitchen-vra/commit/d9ebc45))
|
|
11
11
|
* Standardize renovate config and remove dependabot ([#81](https://github.com/test-kitchen/kitchen-vra/pull/81)) ([be552bd](https://github.com/test-kitchen/kitchen-vra/commit/be552bd))
|
|
12
12
|
|
|
13
|
+
## [3.4.1](https://github.com/test-kitchen/kitchen-vra/compare/v3.4.0...v3.4.1) (2026-08-30)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* stop prompting for credentials on every run with cache_credentials ([#91](https://github.com/test-kitchen/kitchen-vra/issues/91)) ([e1c185f](https://github.com/test-kitchen/kitchen-vra/commit/e1c185f8b38700abd98824cae67f01a33d36b710))
|
|
19
|
+
|
|
20
|
+
## [3.4.0](https://github.com/test-kitchen/kitchen-vra/compare/v3.3.4...v3.4.0) (2026-08-24)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* implement the driver status hook ([#88](https://github.com/test-kitchen/kitchen-vra/issues/88)) ([933e254](https://github.com/test-kitchen/kitchen-vra/commit/933e25445d1a231490ef977cbbfd9644aa16022d))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
* constrain the test-kitchen dependency ([#86](https://github.com/test-kitchen/kitchen-vra/issues/86)) ([ac5f32d](https://github.com/test-kitchen/kitchen-vra/commit/ac5f32d1dd5eb132c91a3fda58ee41f778148245))
|
|
31
|
+
* drop the unused rack runtime dependency ([#89](https://github.com/test-kitchen/kitchen-vra/issues/89)) ([b68f1fd](https://github.com/test-kitchen/kitchen-vra/commit/b68f1fdd4dfc6d1a0c37320f8b099b5d0ca19c8a))
|
|
32
|
+
|
|
13
33
|
## [3.3.4](https://github.com/test-kitchen/kitchen-vra/compare/v3.3.3...v3.3.4) (2026-08-23)
|
|
14
34
|
|
|
15
35
|
|
data/Gemfile
CHANGED
data/kitchen-vra.gemspec
CHANGED
|
@@ -20,9 +20,8 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
|
|
21
21
|
spec.required_ruby_version = ">= 3.1"
|
|
22
22
|
|
|
23
|
-
spec.add_dependency "test-kitchen"
|
|
23
|
+
spec.add_dependency "test-kitchen", ">= 3.0", "< 5"
|
|
24
24
|
spec.add_dependency "vmware-vra", "~> 3.0", ">= 3.2.0" # 3.0 required for vRA 8.x
|
|
25
25
|
spec.add_dependency "highline"
|
|
26
|
-
spec.add_dependency "rack", ">= 1.6", "< 4.0"
|
|
27
26
|
spec.add_dependency "ffi-yajl", ">= 2.2.3", "< 3.1.0"
|
|
28
27
|
end
|
data/lib/kitchen/driver/vra.rb
CHANGED
|
@@ -25,6 +25,7 @@ require "base64" unless defined?(Base64)
|
|
|
25
25
|
require "digest" unless defined?(Digest)
|
|
26
26
|
require "fileutils" unless defined?(FileUtils)
|
|
27
27
|
require "vra"
|
|
28
|
+
require "time" unless defined?(Time.now.iso8601)
|
|
28
29
|
require_relative "vra_version"
|
|
29
30
|
|
|
30
31
|
module Kitchen
|
|
@@ -46,6 +47,11 @@ module Kitchen
|
|
|
46
47
|
kitchen_driver_api_version 2
|
|
47
48
|
plugin_version Kitchen::Driver::VRA_VERSION
|
|
48
49
|
|
|
50
|
+
# Deployment statuses vRA reports for a deployment that is up.
|
|
51
|
+
#
|
|
52
|
+
# @return [Array<String>]
|
|
53
|
+
LIVE_STATUSES = %w{CREATE_SUCCESSFUL}.freeze
|
|
54
|
+
|
|
49
55
|
# Location of the credential cache, relative to the working directory.
|
|
50
56
|
CREDENTIALS_CACHE_FILE = ".kitchen/cached_vra"
|
|
51
57
|
|
|
@@ -314,6 +320,41 @@ module Kitchen
|
|
|
314
320
|
end
|
|
315
321
|
end
|
|
316
322
|
|
|
323
|
+
# Reports what vRA currently thinks of the deployment.
|
|
324
|
+
#
|
|
325
|
+
# @param state [Hash] instance state naming the deployment
|
|
326
|
+
# @return [Hash] a Test Kitchen status hash, or the base implementation's
|
|
327
|
+
# answer when there is no deployment or vRA does not know it
|
|
328
|
+
def status(state)
|
|
329
|
+
return super unless state[:deployment_id]
|
|
330
|
+
|
|
331
|
+
deployment = lookup_deployment(state[:deployment_id])
|
|
332
|
+
return super unless deployment
|
|
333
|
+
|
|
334
|
+
deployment_status = deployment.status.to_s
|
|
335
|
+
{
|
|
336
|
+
live: LIVE_STATUSES.include?(deployment_status),
|
|
337
|
+
state: deployment_status,
|
|
338
|
+
source: "driver",
|
|
339
|
+
resource_id: state[:deployment_id],
|
|
340
|
+
message: "vRA reports the deployment as #{deployment_status}",
|
|
341
|
+
checked_at: Time.now.utc.iso8601,
|
|
342
|
+
}
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Looks a deployment up without turning an unreachable vRA into a
|
|
346
|
+
# failure. A deployment that has already been destroyed answers with
|
|
347
|
+
# NotFound, which is a real answer rather than an error.
|
|
348
|
+
#
|
|
349
|
+
# @param deployment_id [String] the vRA deployment ID
|
|
350
|
+
# @return [Vra::Deployment, nil] the deployment, or nil when vRA does not
|
|
351
|
+
# know it or cannot be reached
|
|
352
|
+
def lookup_deployment(deployment_id)
|
|
353
|
+
vra_client.deployments.by_id(deployment_id)
|
|
354
|
+
rescue ::StandardError
|
|
355
|
+
nil
|
|
356
|
+
end
|
|
357
|
+
|
|
317
358
|
# Destroys the vRA deployment, if one exists.
|
|
318
359
|
#
|
|
319
360
|
# A deployment that vRA no longer knows about, or that offers no destroy
|
|
@@ -342,8 +383,10 @@ module Kitchen
|
|
|
342
383
|
wait_for_request(destroy_request)
|
|
343
384
|
info("Destroy request complete.")
|
|
344
385
|
|
|
345
|
-
|
|
346
|
-
|
|
386
|
+
return unless File.exist?(CREDENTIALS_CACHE_FILE)
|
|
387
|
+
|
|
388
|
+
File.delete(CREDENTIALS_CACHE_FILE)
|
|
389
|
+
info("Removed the cached credentials in #{CREDENTIALS_CACHE_FILE}.")
|
|
347
390
|
end
|
|
348
391
|
|
|
349
392
|
# Builds the vRA catalog request for the configured blueprint.
|
|
@@ -390,21 +433,43 @@ module Kitchen
|
|
|
390
433
|
|
|
391
434
|
# The vRA API client, built from the resolved credentials.
|
|
392
435
|
#
|
|
393
|
-
#
|
|
394
|
-
#
|
|
436
|
+
# Credentials are resolved once, when the client is first built. A
|
|
437
|
+
# failure to build it is assumed to be an authentication problem, so the
|
|
438
|
+
# credentials are asked for again and the client built a second time. A
|
|
439
|
+
# second failure is raised rather than swallowed.
|
|
395
440
|
#
|
|
396
441
|
# @return [Vra::Client]
|
|
442
|
+
# @raise [ArgumentError] if the credentials are still not usable after
|
|
443
|
+
# re-prompting
|
|
397
444
|
def vra_client
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
445
|
+
@client ||= build_client
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Resolves the credentials and builds the client, asking for the
|
|
449
|
+
# credentials a second time if the first attempt fails.
|
|
450
|
+
#
|
|
451
|
+
# @return [Vra::Client]
|
|
452
|
+
# @raise [ArgumentError] if the second attempt fails as well
|
|
453
|
+
# @api private
|
|
454
|
+
def build_client
|
|
455
|
+
retried = false
|
|
456
|
+
|
|
457
|
+
begin
|
|
458
|
+
check_config(retried)
|
|
459
|
+
::Vra::Client.new(
|
|
460
|
+
base_url: config[:base_url],
|
|
461
|
+
username: config[:username],
|
|
462
|
+
password: config[:password],
|
|
463
|
+
domain: config[:domain],
|
|
464
|
+
verify_ssl: config[:verify_ssl]
|
|
465
|
+
)
|
|
466
|
+
rescue => e
|
|
467
|
+
raise if retried
|
|
468
|
+
|
|
469
|
+
warn("Unable to build a vRA client: #{e.message}")
|
|
470
|
+
retried = true
|
|
471
|
+
retry
|
|
472
|
+
end
|
|
408
473
|
end
|
|
409
474
|
|
|
410
475
|
# Polls a vRA request until it completes or times out.
|
|
@@ -416,8 +481,6 @@ module Kitchen
|
|
|
416
481
|
# @return [void]
|
|
417
482
|
# @raise [Timeout::Error] if the request does not complete in time
|
|
418
483
|
def wait_for_request(request)
|
|
419
|
-
# config = check_config config
|
|
420
|
-
|
|
421
484
|
last_status = ""
|
|
422
485
|
wait_time = config[:request_timeout]
|
|
423
486
|
sleep_time = config[:request_refresh_rate]
|
data/spec/spec_helper.rb
CHANGED
|
@@ -19,13 +19,51 @@
|
|
|
19
19
|
#
|
|
20
20
|
|
|
21
21
|
require "webmock/rspec"
|
|
22
|
+
require "tmpdir"
|
|
23
|
+
require "fileutils"
|
|
22
24
|
|
|
25
|
+
require "kitchen/driver/vra"
|
|
26
|
+
require "kitchen/provisioner/dummy"
|
|
27
|
+
require "kitchen/transport/dummy"
|
|
28
|
+
require "kitchen/verifier/dummy"
|
|
29
|
+
|
|
30
|
+
Dir[File.join(__dir__, "support", "**", "*.rb")].sort.each { |file| require file }
|
|
31
|
+
|
|
32
|
+
# These specs never reach a vRA appliance. Everything the driver does is turn
|
|
33
|
+
# kitchen.yml configuration into a vRA catalog request, and turn vRA's answers
|
|
34
|
+
# back into Test Kitchen state, so an outbound request from a spec is a mistake
|
|
35
|
+
# rather than a slow test.
|
|
23
36
|
WebMock.disable_net_connect!(allow_localhost: true)
|
|
24
37
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
command_name "Specs"
|
|
38
|
+
RSpec.configure do |config|
|
|
39
|
+
config.expect_with :rspec do |expectations|
|
|
40
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
29
41
|
end
|
|
30
|
-
|
|
42
|
+
|
|
43
|
+
config.mock_with :rspec do |mocks|
|
|
44
|
+
# Fail if an example stubs a method the real object does not have. Almost
|
|
45
|
+
# every driver method is a call into the vmware-vra gem, so without this a
|
|
46
|
+
# rename there leaves the specs stubbing a method that no longer exists,
|
|
47
|
+
# passing while the driver is broken.
|
|
48
|
+
mocks.verify_partial_doubles = true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
52
|
+
|
|
53
|
+
# No `describe` on bare objects and no `should` -- the RSpec 2 DSL, kept
|
|
54
|
+
# alive only by monkey patches on Object and BasicObject.
|
|
55
|
+
config.disable_monkey_patching!
|
|
56
|
+
|
|
57
|
+
# Surface deprecations as failures rather than warnings that scroll past.
|
|
58
|
+
config.raise_errors_for_deprecations!
|
|
59
|
+
|
|
60
|
+
config.include VraApi
|
|
61
|
+
|
|
62
|
+
config.filter_run_when_matching :focus
|
|
63
|
+
|
|
64
|
+
# Run specs in random order to surface order dependencies. To debug one, fix
|
|
65
|
+
# the order by passing the seed printed after each run:
|
|
66
|
+
# --seed 1234
|
|
67
|
+
config.order = :random
|
|
68
|
+
Kernel.srand config.seed
|
|
31
69
|
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
# Stand-ins for the vmware-vra objects the driver talks to.
|
|
18
|
+
#
|
|
19
|
+
# The driver is a thin layer over the vmware-vra gem: nearly every method
|
|
20
|
+
# either asks a Vra object something or hands it a request. A bare `double`
|
|
21
|
+
# answers whatever it is told to answer, so a suite built on them keeps passing
|
|
22
|
+
# after vmware-vra renames a method -- which is exactly the change most likely
|
|
23
|
+
# to break the driver. Everything here is a verifying double checked against
|
|
24
|
+
# the real class instead, so the specs fail when the gem moves underneath them.
|
|
25
|
+
#
|
|
26
|
+
# Each helper takes a hash of overrides so an example can say only what it
|
|
27
|
+
# cares about:
|
|
28
|
+
#
|
|
29
|
+
# vra_deployment(status: "CREATE_FAILED", failed?: true)
|
|
30
|
+
module VraApi
|
|
31
|
+
# A VM inside a deployment, as returned by Vra::Deployment#resources.
|
|
32
|
+
#
|
|
33
|
+
# @param overrides [Hash] stubs to replace the defaults with
|
|
34
|
+
# @return [InstanceDouble<Vra::Resource>]
|
|
35
|
+
def vra_resource(overrides = {})
|
|
36
|
+
instance_double(
|
|
37
|
+
Vra::Resource,
|
|
38
|
+
{
|
|
39
|
+
id: "res-1",
|
|
40
|
+
deployment_id: "dep-1",
|
|
41
|
+
name: "test-server",
|
|
42
|
+
ip_address: "1.2.3.4",
|
|
43
|
+
vm?: true,
|
|
44
|
+
}.merge(overrides)
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# A deployment. This is also what Vra::DeploymentRequest#submit hands back,
|
|
49
|
+
# which is why the driver waits on it and then asks it for its resources.
|
|
50
|
+
#
|
|
51
|
+
# @param overrides [Hash] stubs to replace the defaults with
|
|
52
|
+
# @return [InstanceDouble<Vra::Deployment>]
|
|
53
|
+
def vra_deployment(overrides = {})
|
|
54
|
+
instance_double(
|
|
55
|
+
Vra::Deployment,
|
|
56
|
+
{
|
|
57
|
+
id: "dep-1",
|
|
58
|
+
name: "test-instance",
|
|
59
|
+
status: "CREATE_SUCCESSFUL",
|
|
60
|
+
failed?: false,
|
|
61
|
+
completed?: true,
|
|
62
|
+
completion_details: nil,
|
|
63
|
+
refresh: nil,
|
|
64
|
+
resources: [vra_resource],
|
|
65
|
+
}.merge(overrides)
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# A request against a deployment, such as the destroy action.
|
|
70
|
+
#
|
|
71
|
+
# @param overrides [Hash] stubs to replace the defaults with
|
|
72
|
+
# @return [InstanceDouble<Vra::Request>]
|
|
73
|
+
def vra_request(overrides = {})
|
|
74
|
+
instance_double(
|
|
75
|
+
Vra::Request,
|
|
76
|
+
{
|
|
77
|
+
id: "req-1",
|
|
78
|
+
status: "SUCCESSFUL",
|
|
79
|
+
completed?: true,
|
|
80
|
+
refresh: nil,
|
|
81
|
+
details: nil,
|
|
82
|
+
}.merge(overrides)
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# A catalog item, as returned by Vra::Catalog#fetch_catalog_items.
|
|
87
|
+
#
|
|
88
|
+
# @param overrides [Hash] stubs to replace the defaults with
|
|
89
|
+
# @return [InstanceDouble<Vra::CatalogItem>]
|
|
90
|
+
def vra_catalog_item(overrides = {})
|
|
91
|
+
instance_double(
|
|
92
|
+
Vra::CatalogItem,
|
|
93
|
+
{ id: "cat-1", name: "Ubuntu Server" }.merge(overrides)
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# An unsubmitted catalog request.
|
|
98
|
+
#
|
|
99
|
+
# @param overrides [Hash] stubs to replace the defaults with
|
|
100
|
+
# @return [InstanceDouble<Vra::DeploymentRequest>]
|
|
101
|
+
def vra_deployment_request(overrides = {})
|
|
102
|
+
instance_double(
|
|
103
|
+
Vra::DeploymentRequest,
|
|
104
|
+
{ submit: vra_deployment, set_parameters: nil }.merge(overrides)
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# A vRA API client, wired to a catalog and a deployments collection.
|
|
109
|
+
#
|
|
110
|
+
# @param catalog [InstanceDouble<Vra::Catalog>, nil]
|
|
111
|
+
# @param deployments [InstanceDouble<Vra::Deployments>, nil]
|
|
112
|
+
# @return [InstanceDouble<Vra::Client>]
|
|
113
|
+
def vra_client(catalog: nil, deployments: nil)
|
|
114
|
+
instance_double(
|
|
115
|
+
Vra::Client,
|
|
116
|
+
catalog: catalog || instance_double(Vra::Catalog),
|
|
117
|
+
deployments: deployments || instance_double(Vra::Deployments)
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
end
|