kitchen-vra 3.3.3 → 3.4.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.
@@ -22,16 +22,48 @@ require "kitchen"
22
22
  require "highline/import"
23
23
  require "openssl" unless defined?(OpenSSL)
24
24
  require "base64" unless defined?(Base64)
25
- require "digest/sha1" unless defined?(Digest::SHA1)
25
+ require "digest" unless defined?(Digest)
26
+ require "fileutils" unless defined?(FileUtils)
26
27
  require "vra"
28
+ require "time" unless defined?(Time.now.iso8601)
27
29
  require_relative "vra_version"
28
30
 
29
31
  module Kitchen
32
+ # Test Kitchen's driver plugins.
30
33
  module Driver
34
+ # Test Kitchen driver for VMware vRealize Automation (vRA) 8.x.
35
+ #
36
+ # Unlike a cloud driver that creates a machine directly, this driver
37
+ # submits a request against a vRA catalog item and waits for vRA's own
38
+ # automation to build a deployment. The blueprint behind that catalog item
39
+ # decides what gets built; this driver only supplies the image, flavor,
40
+ # and project, then waits for a single VM to come back.
41
+ #
42
+ # The blueprint must return exactly one VM. A blueprint returning several,
43
+ # or none, fails the +create+ rather than guessing which one to test.
44
+ #
45
+ # @see https://www.vmware.com/products/vrealize-automation.html vRealize Automation
31
46
  class Vra < Kitchen::Driver::Base # rubocop:disable Metrics/ClassLength
32
47
  kitchen_driver_api_version 2
33
48
  plugin_version Kitchen::Driver::VRA_VERSION
34
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
+
55
+ # Location of the credential cache, relative to the working directory.
56
+ CREDENTIALS_CACHE_FILE = ".kitchen/cached_vra"
57
+
58
+ # Cipher used for the credential cache. GCM is authenticated, so a cache
59
+ # written for a different +base_url+, or one that has been altered on
60
+ # disk, fails to decrypt rather than yielding garbage credentials.
61
+ CREDENTIALS_CIPHER = "aes-256-gcm"
62
+
63
+ # Marker for the cache file layout, so a later format change can reject
64
+ # old files instead of misreading them.
65
+ CREDENTIALS_CACHE_VERSION = "v1"
66
+
35
67
  default_config :username, nil
36
68
  default_config :password, nil
37
69
  required_config :base_url
@@ -68,10 +100,20 @@ module Kitchen
68
100
  In vRA 8.x, the 'tenant' configuration is no longer relevant for authentication.
69
101
  Please use the 'domain' configuration in its place.".dup)
70
102
 
103
+ # @return [String] the driver's display name in `kitchen list`
71
104
  def name
72
105
  "vRA"
73
106
  end
74
107
 
108
+ # Resolves the vRA username and password, prompting if necessary.
109
+ #
110
+ # Sources are tried in order: explicit config, then the +VRA_USER_NAME+
111
+ # and +VRA_USER_PASSWORD+ environment variables, then the credential
112
+ # cache, then an interactive prompt.
113
+ #
114
+ # @param force_change [Boolean] prompt even if credentials already
115
+ # resolved, used to re-ask after an authentication failure
116
+ # @return [void]
75
117
  def check_config(force_change = false)
76
118
  config[:username] = config[:username] || ENV["VRA_USER_NAME"]
77
119
  config[:password] = config[:password] || ENV["VRA_USER_PASSWORD"]
@@ -82,43 +124,104 @@ module Kitchen
82
124
  c_save if config[:cache_credentials]
83
125
  end
84
126
 
127
+ # Writes the resolved credentials to {CREDENTIALS_CACHE_FILE}.
128
+ #
129
+ # The file is obfuscated rather than secured: the key is derived from
130
+ # +base_url+, which is not a secret, so anyone holding both the file and
131
+ # the kitchen config can recover the credentials. It keeps passwords out
132
+ # of plain sight on disk; it is not a substitute for a secret store.
133
+ #
134
+ # @return [void]
85
135
  def c_save
86
- cipher = OpenSSL::Cipher.new("cip-her-aes")
87
- cipher.encrypt
88
- cipher.key = Digest::SHA1.hexdigest(config[:base_url])
89
- iv_user = cipher.random_iv
90
- cipher.iv = iv_user
91
- username = cipher.update(config[:username]) + cipher.final
92
- iv_pwd = cipher.random_iv
93
- cipher.iv = iv_pwd
94
- password = cipher.update(config[:password]) + cipher.final
95
- output = "#{Base64.encode64(iv_user).strip!}:#{Base64.encode64(username).strip!}:#{Base64.encode64(iv_pwd).strip!}:#{Base64.encode64(password).strip!}"
96
- file = File.open(".kitchen/cached_vra", "w")
97
- file.write(output)
98
- file.close
99
- rescue
100
- puts "Unable to save credentials"
136
+ FileUtils.mkdir_p(File.dirname(CREDENTIALS_CACHE_FILE))
137
+ fields = [config[:username], config[:password]].flat_map { |value| encrypt_credential(value) }
138
+
139
+ File.open(CREDENTIALS_CACHE_FILE, File::WRONLY | File::CREAT | File::TRUNC, 0o600) do |file|
140
+ file.write(([CREDENTIALS_CACHE_VERSION] + fields).join(":"))
141
+ end
142
+ # The mode above only applies when the file is created, so narrow the
143
+ # permissions of a cache left behind by an earlier run as well.
144
+ File.chmod(0o600, CREDENTIALS_CACHE_FILE)
145
+ rescue => e
146
+ warn("Unable to save credentials to #{CREDENTIALS_CACHE_FILE}: #{e.message}")
101
147
  end
102
148
 
149
+ # Reads credentials back from {CREDENTIALS_CACHE_FILE}.
150
+ #
151
+ # A cache that cannot be decrypted -- a different +base_url+, a truncated
152
+ # or edited file, an older layout -- is reported and ignored, leaving the
153
+ # credentials unset so the caller falls through to prompting.
154
+ #
155
+ # @return [void]
103
156
  def c_load
104
- if File.exist? ".kitchen/cached_vra"
105
- encrypted = File.read(".kitchen/cached_vra")
106
- iv_user = Base64.decode64(encrypted.split(":")[0] + '\n')
107
- username = Base64.decode64(encrypted.split(":")[1] + "\n")
108
- iv_pwd = Base64.decode64(encrypted.split(":")[2] + "\n")
109
- password = Base64.decode64(encrypted.split(":")[3] + "\n")
110
- cipher = OpenSSL::Cipher.new("cip-her-aes")
111
- cipher.decrypt
112
- cipher.key = Digest::SHA1.hexdigest(config[:base_url])
113
- cipher.iv = iv_user
114
- config[:username] = cipher.update(username) + cipher.final
115
- cipher.iv = iv_pwd
116
- config[:password] = cipher.update(password) + cipher.final
117
- end
118
- rescue
119
- puts "Failed to load cached credentials"
157
+ return unless File.exist?(CREDENTIALS_CACHE_FILE)
158
+
159
+ version, *fields = File.read(CREDENTIALS_CACHE_FILE).strip.split(":")
160
+ raise "unrecognized cache format" unless version == CREDENTIALS_CACHE_VERSION && fields.length == 6
161
+
162
+ # Decrypt both before assigning either, so a partially readable cache
163
+ # cannot leave half the credentials set.
164
+ username = decrypt_credential(*fields[0, 3])
165
+ password = decrypt_credential(*fields[3, 3])
166
+
167
+ config[:username] = username
168
+ config[:password] = password
169
+ rescue => e
170
+ warn("Failed to load cached credentials from #{CREDENTIALS_CACHE_FILE}: #{e.message}")
120
171
  end
121
172
 
173
+ # Encrypts one credential for the cache file.
174
+ #
175
+ # @param value [String] the credential to encrypt
176
+ # @return [Array<String>] Base64-encoded IV, authentication tag, and
177
+ # ciphertext, in that order
178
+ def encrypt_credential(value)
179
+ cipher = OpenSSL::Cipher.new(CREDENTIALS_CIPHER)
180
+ cipher.encrypt
181
+ cipher.key = credentials_key
182
+ iv = cipher.random_iv
183
+ encrypted = cipher.update(value.to_s) + cipher.final
184
+
185
+ [iv, cipher.auth_tag, encrypted].map { |part| Base64.strict_encode64(part) }
186
+ end
187
+
188
+ # Decrypts one credential from the cache file.
189
+ #
190
+ # @param iv [String] Base64-encoded initialization vector
191
+ # @param auth_tag [String] Base64-encoded GCM authentication tag
192
+ # @param encrypted [String] Base64-encoded ciphertext
193
+ # @return [String] the decrypted credential
194
+ # @raise [OpenSSL::Cipher::CipherError] if the key or tag does not match
195
+ def decrypt_credential(iv, auth_tag, encrypted)
196
+ cipher = OpenSSL::Cipher.new(CREDENTIALS_CIPHER)
197
+ cipher.decrypt
198
+ cipher.key = credentials_key
199
+ cipher.iv = Base64.strict_decode64(iv)
200
+ cipher.auth_tag = Base64.strict_decode64(auth_tag)
201
+
202
+ cipher.update(Base64.strict_decode64(encrypted)) + cipher.final
203
+ end
204
+
205
+ # Derives the cache key from +base_url+.
206
+ #
207
+ # SHA-256 is used for its digest length: {CREDENTIALS_CIPHER} requires a
208
+ # 32-byte key, which +Digest::SHA256.digest+ returns exactly.
209
+ #
210
+ # @return [String] a 32-byte key
211
+ def credentials_key
212
+ Digest::SHA256.digest(config[:base_url].to_s)
213
+ end
214
+
215
+ # Requests a deployment from vRA and waits until it can be logged into.
216
+ #
217
+ # Returns immediately if the state already names a deployment, so a
218
+ # re-run does not build a second one.
219
+ #
220
+ # @param state [Hash] mutable instance state; gains +deployment_id+,
221
+ # +hostname+, and +ssh_key+
222
+ # @return [void]
223
+ # @raise [RuntimeError] if the vRA request fails or does not yield
224
+ # exactly one VM
122
225
  def create(state)
123
226
  return if state[:deployment_id]
124
227
 
@@ -131,6 +234,15 @@ module Kitchen
131
234
  info("Server #{server.deployment_id} (#{server.name}) ready.")
132
235
  end
133
236
 
237
+ # Works out the address Test Kitchen should connect to.
238
+ #
239
+ # With +use_dns+ set, the server's name is used, optionally suffixed with
240
+ # +dns_suffix+. Otherwise the IP address is preferred, falling back to
241
+ # the name with a warning when vRA reports no address.
242
+ #
243
+ # @param server [Vra::Resource] the VM vRA built
244
+ # @return [String] a hostname or IP address
245
+ # @raise [RuntimeError] if +use_dns+ is set but vRA returned no name
134
246
  def hostname_for(server)
135
247
  if config[:use_dns]
136
248
  raise "No server name returned for the vRA request" if server.name.nil?
@@ -147,6 +259,11 @@ module Kitchen
147
259
  end
148
260
  end
149
261
 
262
+ # Submits the catalog request and waits for vRA to finish building it.
263
+ #
264
+ # @return [Vra::Resource] the single VM the blueprint produced
265
+ # @raise [RuntimeError] if the request failed, or produced zero or more
266
+ # than one VM
150
267
  def request_server
151
268
  info("Building vRA catalog request...")
152
269
 
@@ -167,6 +284,16 @@ module Kitchen
167
284
  servers.first
168
285
  end
169
286
 
287
+ # Waits for the transport to accept a connection, retrying on failure.
288
+ #
289
+ # Backs off in five-second steps up to thirty seconds between attempts.
290
+ # Once +server_ready_retries+ is exceeded the deployment is destroyed
291
+ # before the error is re-raised, so a machine that never comes up is not
292
+ # left running and billable.
293
+ #
294
+ # @param state [Hash] instance state describing how to connect
295
+ # @param server [Vra::Resource] the VM being waited on
296
+ # @return [void]
170
297
  def wait_for_server(state, server)
171
298
  info("Server #{server.id} (#{server.name}) created. Waiting until ready...")
172
299
 
@@ -193,6 +320,49 @@ module Kitchen
193
320
  end
194
321
  end
195
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
+
358
+ # Destroys the vRA deployment, if one exists.
359
+ #
360
+ # A deployment that vRA no longer knows about, or that offers no destroy
361
+ # action, is treated as already gone rather than an error. The cached
362
+ # credentials file is removed afterwards.
363
+ #
364
+ # @param state [Hash] instance state naming the deployment
365
+ # @return [void]
196
366
  def destroy(state)
197
367
  return if state[:deployment_id].nil?
198
368
 
@@ -217,6 +387,14 @@ module Kitchen
217
387
  info("Removed cached file")
218
388
  end
219
389
 
390
+ # Builds the vRA catalog request for the configured blueprint.
391
+ #
392
+ # When +catalog_name+ is given it is resolved to a catalog ID first.
393
+ # A deployment name is only sent when +unique_name+ is false; otherwise
394
+ # vRA is left to name the deployment after its request ID.
395
+ #
396
+ # @return [Vra::CatalogRequest] a request ready to submit
397
+ # @raise [Kitchen::InstanceFailure] if no catalog could be resolved
220
398
  def catalog_request # rubocop:disable Metrics/MethodLength
221
399
  unless config[:catalog_name].nil?
222
400
  info("Fetching Catalog ID by Catalog Name")
@@ -251,6 +429,12 @@ module Kitchen
251
429
  catalog_request
252
430
  end
253
431
 
432
+ # The vRA API client, built from the resolved credentials.
433
+ #
434
+ # On any failure the credentials are re-prompted, on the assumption that
435
+ # what failed was authentication.
436
+ #
437
+ # @return [Vra::Client]
254
438
  def vra_client
255
439
  check_config config[:cache_credentials]
256
440
  @client ||= ::Vra::Client.new(
@@ -264,6 +448,14 @@ module Kitchen
264
448
  check_config true
265
449
  end
266
450
 
451
+ # Polls a vRA request until it completes or times out.
452
+ #
453
+ # Polls every +request_refresh_rate+ seconds, logging each change of
454
+ # status, and gives up after +request_timeout+ seconds.
455
+ #
456
+ # @param request [Vra::Request] the request to poll
457
+ # @return [void]
458
+ # @raise [Timeout::Error] if the request does not complete in time
267
459
  def wait_for_request(request)
268
460
  # config = check_config config
269
461
 
@@ -18,8 +18,11 @@
18
18
  # limitations under the License.
19
19
  #
20
20
 
21
+ # Test Kitchen's top-level namespace.
21
22
  module Kitchen
23
+ # Test Kitchen's driver plugins.
22
24
  module Driver
23
- VRA_VERSION = "3.3.3"
25
+ # The version of the kitchen-vra gem.
26
+ VRA_VERSION = "3.4.0"
24
27
  end
25
28
  end
data/renovate.json CHANGED
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
3
  "extends": [
4
- "config:recommended"
4
+ "config:recommended",
5
+ ":disableDependencyDashboard",
6
+ "schedule:automergeEarlyMondays"
5
7
  ]
6
8
  }
data/spec/vra_spec.rb CHANGED
@@ -19,6 +19,8 @@
19
19
  #
20
20
 
21
21
  require "spec_helper"
22
+ require "tmpdir"
23
+ require "fileutils"
22
24
  require "kitchen/driver/vra"
23
25
  require "kitchen/provisioner/dummy"
24
26
  require "kitchen/transport/dummy"
@@ -328,6 +330,64 @@ describe Kitchen::Driver::Vra do
328
330
  end
329
331
  end
330
332
 
333
+ describe "#status" do
334
+ let(:deployments) { double("deployments") }
335
+ let(:vra_client) { double("vra_client", deployments: deployments) }
336
+
337
+ before { allow(driver).to receive(:vra_client).and_return(vra_client) }
338
+
339
+ it "reports an unknown status when state names no deployment" do
340
+ expect(driver.status({})).to include(live: nil, state: "unknown")
341
+ end
342
+
343
+ it "reports an unknown status when vRA has never heard of the deployment" do
344
+ allow(deployments).to receive(:by_id).with("gone")
345
+ .and_raise(Vra::Exception::NotFound.new("nope"))
346
+
347
+ expect(driver.status(deployment_id: "gone")).to include(state: "unknown")
348
+ end
349
+
350
+ it "reports a successfully created deployment as live" do
351
+ allow(deployments).to receive(:by_id).with("dep-1")
352
+ .and_return(double(status: "CREATE_SUCCESSFUL"))
353
+
354
+ expect(driver.status(deployment_id: "dep-1")).to include(
355
+ live: true, state: "CREATE_SUCCESSFUL", source: "driver",
356
+ resource_id: "dep-1"
357
+ )
358
+ end
359
+
360
+ it "stamps when the check happened" do
361
+ allow(deployments).to receive(:by_id)
362
+ .and_return(double(status: "CREATE_SUCCESSFUL"))
363
+
364
+ expect(driver.status(deployment_id: "dep-1")[:checked_at])
365
+ .to match(/\A\d{4}-\d{2}-\d{2}T/)
366
+ end
367
+
368
+ it "reports a deployment still building as not live" do
369
+ allow(deployments).to receive(:by_id)
370
+ .and_return(double(status: "CREATE_INPROGRESS"))
371
+
372
+ expect(driver.status(deployment_id: "dep-1"))
373
+ .to include(live: false, state: "CREATE_INPROGRESS")
374
+ end
375
+
376
+ it "names a failed deployment rather than reporting a bare false" do
377
+ allow(deployments).to receive(:by_id)
378
+ .and_return(double(status: "CREATE_FAILED"))
379
+
380
+ expect(driver.status(deployment_id: "dep-1"))
381
+ .to include(live: false, state: "CREATE_FAILED")
382
+ end
383
+
384
+ it "reports an unknown status when vRA cannot be reached" do
385
+ allow(deployments).to receive(:by_id).and_raise(StandardError.new("boom"))
386
+
387
+ expect(driver.status(deployment_id: "dep-1")).to include(state: "unknown")
388
+ end
389
+ end
390
+
331
391
  describe "#destroy" do
332
392
  let(:deployment_id) { "8c1a833a-5844-4100-b58c-9cab3543c958" }
333
393
  let(:state) { { deployment_id: deployment_id } }
@@ -447,7 +507,7 @@ describe Kitchen::Driver::Vra do
447
507
  }
448
508
  end
449
509
 
450
- it "sets extra parmeters" do
510
+ it "sets extra parameters" do
451
511
  expect(catalog_request).to receive(:set_parameters).with("key1", { type: "string", value: "value1" })
452
512
  expect(catalog_request).to receive(:set_parameters).with("key2", { type: "integer", value: 123 })
453
513
  driver.catalog_request
@@ -510,4 +570,99 @@ describe Kitchen::Driver::Vra do
510
570
  end
511
571
  end
512
572
  end
573
+
574
+ describe "credential caching" do
575
+ let(:cache_dir) { Dir.mktmpdir("kitchen-vra-spec") }
576
+ let(:cache_file) { File.join(cache_dir, ".kitchen", "cached_vra") }
577
+
578
+ # c_save/c_load address the cache by a path relative to the working
579
+ # directory, so each example runs inside a throwaway directory.
580
+ around do |example|
581
+ Dir.chdir(cache_dir) { example.run }
582
+ end
583
+
584
+ after { FileUtils.remove_entry(cache_dir) }
585
+
586
+ describe "#c_save" do
587
+ it "creates the cache file" do
588
+ driver.c_save
589
+ expect(File.exist?(cache_file)).to be true
590
+ end
591
+
592
+ it "creates the .kitchen directory when it does not exist" do
593
+ expect { driver.c_save }.to change { Dir.exist?(File.join(cache_dir, ".kitchen")) }.from(false).to(true)
594
+ end
595
+
596
+ it "writes the file readable only by its owner" do
597
+ driver.c_save
598
+ expect(File.stat(cache_file).mode & 0o777).to eq(0o600)
599
+ end
600
+
601
+ it "does not write the password in the clear" do
602
+ driver.c_save
603
+ expect(File.read(cache_file)).not_to include("mypassword")
604
+ end
605
+ end
606
+
607
+ describe "#c_load" do
608
+ it "restores credentials written by #c_save" do
609
+ driver.c_save
610
+
611
+ reader = Kitchen::Driver::Vra.new(config.reject { |k, _v| %i{username password}.include?(k) })
612
+ allow(reader).to receive(:instance).and_return(instance)
613
+ reader.c_load
614
+
615
+ expect(reader[:username]).to eq("myuser")
616
+ expect(reader[:password]).to eq("mypassword")
617
+ end
618
+
619
+ it "does nothing when no cache file exists" do
620
+ driver.c_load
621
+ expect(driver[:username]).to eq("myuser")
622
+ end
623
+
624
+ it "refuses a cache written against a different base_url" do
625
+ driver.c_save
626
+
627
+ other = Kitchen::Driver::Vra.new(config.merge(base_url: "https://other.corp.local")
628
+ .reject { |k, _v| %i{username password}.include?(k) })
629
+ allow(other).to receive(:instance).and_return(instance)
630
+ expect(other).to receive(:warn).with(/Failed to load cached credentials/)
631
+ other.c_load
632
+
633
+ expect(other[:username]).to be_nil
634
+ expect(other[:password]).to be_nil
635
+ end
636
+
637
+ it "refuses a corrupted cache rather than returning garbage" do
638
+ driver.c_save
639
+ File.write(cache_file, File.read(cache_file).succ)
640
+
641
+ loader = Kitchen::Driver::Vra.new(config.reject { |k, _v| %i{username password}.include?(k) })
642
+ allow(loader).to receive(:instance).and_return(instance)
643
+ expect(loader).to receive(:warn).with(/Failed to load cached credentials/)
644
+ loader.c_load
645
+
646
+ expect(loader[:username]).to be_nil
647
+ end
648
+ end
649
+
650
+ describe "#check_config" do
651
+ it "uses the cache instead of prompting" do
652
+ driver.c_save
653
+
654
+ reader = Kitchen::Driver::Vra.new(config.reject { |k, _v| %i{username password}.include?(k) })
655
+ allow(reader).to receive(:instance).and_return(instance)
656
+ allow(ENV).to receive(:[]).and_call_original
657
+ allow(ENV).to receive(:[]).with("VRA_USER_NAME").and_return(nil)
658
+ allow(ENV).to receive(:[]).with("VRA_USER_PASSWORD").and_return(nil)
659
+ expect(reader).not_to receive(:ask)
660
+
661
+ reader.check_config
662
+
663
+ expect(reader[:username]).to eq("myuser")
664
+ expect(reader[:password]).to eq("mypassword")
665
+ end
666
+ end
667
+ end
513
668
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-vra
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.3
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
- - Chef Commmunity Tools Team
7
+ - Chef Community Tools Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-27 00:00:00.000000000 Z
11
+ date: 2026-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: test-kitchen
@@ -16,14 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '3.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: '3.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: vmware-vra
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -58,26 +64,6 @@ dependencies:
58
64
  - - ">="
59
65
  - !ruby/object:Gem::Version
60
66
  version: '0'
61
- - !ruby/object:Gem::Dependency
62
- name: rack
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: '1.6'
68
- - - "<"
69
- - !ruby/object:Gem::Version
70
- version: '4.0'
71
- type: :runtime
72
- prerelease: false
73
- version_requirements: !ruby/object:Gem::Requirement
74
- requirements:
75
- - - ">="
76
- - !ruby/object:Gem::Version
77
- version: '1.6'
78
- - - "<"
79
- - !ruby/object:Gem::Version
80
- version: '4.0'
81
67
  - !ruby/object:Gem::Dependency
82
68
  name: ffi-yajl
83
69
  requirement: !ruby/object:Gem::Requirement
@@ -106,14 +92,16 @@ extensions: []
106
92
  extra_rdoc_files: []
107
93
  files:
108
94
  - ".github/CODEOWNERS"
109
- - ".github/dependabot.yml"
110
95
  - ".github/workflows/linters.yml"
111
96
  - ".github/workflows/publish.yml"
112
97
  - ".gitignore"
113
98
  - ".markdownlint.yaml"
114
99
  - ".release-please-manifest.json"
115
100
  - ".rubocop.yml"
101
+ - ".yamllint"
102
+ - ".yardopts"
116
103
  - CHANGELOG.md
104
+ - CONTRIBUTING.md
117
105
  - Gemfile
118
106
  - LICENSE.txt
119
107
  - README.md
@@ -144,10 +132,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
132
  - !ruby/object:Gem::Version
145
133
  version: '0'
146
134
  requirements: []
147
- rubygems_version: 3.4.10
135
+ rubygems_version: 3.5.9
148
136
  signing_key:
149
137
  specification_version: 4
150
138
  summary: A Test Kitchen driver for VMware vRealize Automation (vRA)
151
- test_files:
152
- - spec/spec_helper.rb
153
- - spec/vra_spec.rb
139
+ test_files: []
@@ -1,8 +0,0 @@
1
- ---
2
- version: 2
3
- updates:
4
- - package-ecosystem: bundler
5
- directory: "/"
6
- schedule:
7
- interval: daily
8
- open-pull-requests-limit: 10