kitchen-vra 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 809c8cf566e76098fd34ca3428983d631d4e4170
4
- data.tar.gz: f4863346038f89134bd1d9de0c60184abfeebed5
3
+ metadata.gz: 5c7f119529c9a3562d45db721bf394fd9742d3a5
4
+ data.tar.gz: 7b2578c10fe9bfe7ed5d736c5b0ff0bd4de5cf7b
5
5
  SHA512:
6
- metadata.gz: dd0290ce381d0adfda2ea38de3d34afcf346e4a28c6692a7ad0f1c29e05ea84323e454ad55d26669f4378284c19d59e4c8f83e2266596f6bba5fd9f0691a8245
7
- data.tar.gz: 97e02008a31a9a84e0e109b2bc873e18abf9f4bfe791d43f9beec8f563d74ded8c263f6e46e1ff11a645adc11dd986944a84432ed3617d5589b9ec2c44f23b2e
6
+ metadata.gz: 073bbc3ed64031fb0e99fed859c4211362c354db494d0d21dec266f5039146c911d7fd72d33fb95dab34afaf0860ba9bd8b570ef55046077981e463fa0759dff
7
+ data.tar.gz: f8d23dbce4ad8a4c92b9ae42314063237c7149fce5f27c59db918a8d7046aa0baa8a0b470c956f1f87d30fe911fecd6d1b934946625ef7fd4c6b0a8516a3e235
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # kitchen-vra Changelog
2
2
 
3
+ ## v1.2.0 (2015-11-25)
4
+ * [pr#7](https://github.com/chef-partners/kitchen-vra/pull/7) Added retry logic for wait_until_ready in cases where Test Kitchen would unwind (such as DNS issues). Added fallback logic for when a host has no IP address, complimenting the `use_dns` parameter.
5
+
3
6
  ## v1.1.0 (2015-10-13)
4
7
  * New `use_dns` option (defaults to false) for the driver to use the server name instead of the IP address - thanks to @stevehedrick in PR [#3](https://github.com/chef-partners/kitchen-vra/pull/3)
5
8
 
data/README.md CHANGED
@@ -52,12 +52,14 @@ Other options that you can set include:
52
52
 
53
53
  * **lease_days**: number of days to request for a lease, if your catalog item / blueprint requires it
54
54
  * **request_timeout**: amount of time, in seconds, to wait for a vRA request to complete. Default is 600 seconds.
55
+ * **server_ready_retries**: Number of times to retry the "waiting for server to be ready" check. In some cases, this will error out immediately due to DNS propagation issues, etc. Setting this to a number greater than 0 will retry the `wait_until_ready` method with a growing sleep in between each attempt. Defaults to 1. Set to 0 to disable any retrying of the `wait_until_ready` method.
55
56
  * **cpus**: number of CPUs the host should have
56
57
  * **memory**: amount of RAM, in MB, the host should have
57
58
  * **requested_for**: the vRA login ID to list as the owner of this resource. Defaults to the vRA username configured in the `driver` section.
58
59
  * **subtenant_id**: the Business Group ID to list as the owner. This is required if the catalog item is a shared/global item; we are unable to determine the subtenant_id from the catalog, and vRA requires it to be set on every request.
59
- * **private_key_path**: path to the SSH private key to use when logging in. Defaults to '~/.ssh/id_rsa' or '~/.ssh/id_dsa', preferring the RSA key. Only applies to instances where SSH transport is used (i.e. does not apply to Windows hosts with the WinRM transport configured).
60
+ * **private_key_path**: path to the SSH private key to use when logging in. Defaults to '~/.ssh/id_rsa' or '~/.ssh/id_dsa', preferring the RSA key. Only applies to instances where SSH transport is used; i.e., does not apply to Windows hosts with the WinRM transport configured.
60
61
  * **use_dns**: Defaults to `false`. Set to `true` if vRA doesn't manage vm ip addresses. This will cause kitchen to attempt to connect via hostname.
62
+ * **extra_parameters**: a hash of other data to set on a catalog request, most notably custom properties. Allows updates to existing properties on the blueprint as well as the addition of new properties. The vRA REST API expects 'provider-' appended to the front of a property name; each key in the hash is the property name, and the value is a another hash containing the value data type and the value itself.
61
63
 
62
64
  These settings can be set globally under the top-level `driver` section, or they can be set on each platform, which allows you to set globals and then override them. For example, this configuration would set the CPU count to 1 except on the "large" platform:
63
65
 
@@ -70,10 +72,24 @@ platforms:
70
72
  - name: small
71
73
  driver:
72
74
  catalog_id: 8a189191-fea6-43eb-981e-ee0fa40f8f57
75
+ extra_parameters:
76
+ provider-mycustompropname:
77
+ type: string
78
+ value: smallvalue
79
+ provider-Vrm.DataCenter.Location:
80
+ type: string
81
+ value: Non-Prod
73
82
  - name: large
74
83
  driver:
75
84
  catalog_id: 1d7c6122-18fa-4ed6-bd13-8a33b6c6ed50
76
85
  cpus: 2
86
+ extra_parameters:
87
+ provider-mycustompropname:
88
+ type: string
89
+ value: largevalue
90
+ provider-Vrm.DataCenter.Location:
91
+ type: string
92
+ value: Prod
77
93
  ```
78
94
 
79
95
  ## License and Authors
@@ -22,7 +22,7 @@ require_relative 'vra_version'
22
22
 
23
23
  module Kitchen
24
24
  module Driver
25
- class Vra < Kitchen::Driver::Base
25
+ class Vra < Kitchen::Driver::Base # rubocop:disable Metrics/ClassLength
26
26
  kitchen_driver_api_version 2
27
27
  plugin_version Kitchen::Driver::VRA_VERSION
28
28
 
@@ -36,6 +36,7 @@ module Kitchen
36
36
  default_config :verify_ssl, true
37
37
  default_config :request_timeout, 600
38
38
  default_config :request_refresh_rate, 2
39
+ default_config :server_ready_retries, 1
39
40
  default_config :cpus, 1
40
41
  default_config :memory, 1024
41
42
  default_config :requested_for do |driver|
@@ -61,17 +62,26 @@ module Kitchen
61
62
 
62
63
  server = request_server
63
64
  state[:resource_id] = server.id
65
+ state[:hostname] = hostname_for(server)
66
+ state[:ssh_key] = config[:private_key_path] unless config[:private_key_path].nil?
67
+
68
+ wait_for_server(state, server)
69
+ info("Server #{server.id} (#{server.name}) ready.")
70
+ end
71
+
72
+ def hostname_for(server)
64
73
  if config[:use_dns]
65
74
  raise 'No server name returned for the vRA request' if server.name.nil?
66
- state[:hostname] = server.name
67
- else
68
- raise 'No IP address returned for the vRA request' if server.ip_addresses.first.nil?
69
- state[:hostname] = server.ip_addresses.first
75
+ return server.name
70
76
  end
71
- state[:ssh_key] = config[:private_key_path] unless config[:private_key_path].nil?
72
77
 
73
- wait_for_server(state, server)
74
- info("Server #{server.id} (#{server.name}) ready.")
78
+ ip_address = server.ip_addresses.first
79
+ if ip_address.nil?
80
+ warn("Server #{server.id} has no IP address. Falling back to server name (#{server.name})...")
81
+ server.name
82
+ else
83
+ ip_address
84
+ end
75
85
  end
76
86
 
77
87
  def request_server
@@ -91,12 +101,27 @@ module Kitchen
91
101
 
92
102
  def wait_for_server(state, server)
93
103
  info("Server #{server.id} (#{server.name}) created. Waiting until ready...")
104
+
105
+ try = 0
106
+ sleep_time = 1
107
+
94
108
  begin
95
109
  instance.transport.connection(state).wait_until_ready
96
- rescue
97
- error("Server #{server.id} (#{server.name}) not reachable. Destroying server...")
98
- destroy(state)
99
- raise
110
+ rescue => e
111
+ warn("Server #{server.id} (#{server.name}) not reachable: #{e.class} -- #{e.message}")
112
+
113
+ try += 1
114
+ sleep_time *= 2
115
+
116
+ if try > config[:server_ready_retries]
117
+ error('Retries exceeded. Destroying server...')
118
+ destroy(state)
119
+ raise
120
+ else
121
+ warn("Sleeping #{sleep_time} seconds and retrying...")
122
+ sleep sleep_time
123
+ retry
124
+ end
100
125
  end
101
126
  end
102
127
 
@@ -18,6 +18,6 @@
18
18
 
19
19
  module Kitchen
20
20
  module Driver
21
- VRA_VERSION = '1.1.0'
21
+ VRA_VERSION = '1.2.0'
22
22
  end
23
23
  end
data/spec/vra_spec.rb CHANGED
@@ -103,28 +103,10 @@ describe Kitchen::Driver::Vra do
103
103
  expect(state[:resource_id]).to eq('e8706351-cf4c-4c12-acb7-c90cc683b22c')
104
104
  end
105
105
 
106
- describe 'setting the hostname in the state hash' do
107
- context 'when use_dns is true' do
108
- let(:config) { { use_dns: true } }
109
- it 'raises an exception if the server name is nil' do
110
- allow(resource).to receive(:name).and_return(nil)
111
- expect { driver.create(state) }.to raise_error(RuntimeError)
112
- end
113
- it 'uses the server name as the hostname' do
114
- driver.create(state)
115
- expect(state[:hostname]).to eq('server1')
116
- end
117
- end
118
- context 'when use_dns is false' do
119
- it 'raises an exception if no IP address is available' do
120
- allow(resource).to receive(:ip_addresses).and_return([])
121
- expect { driver.create(state) }.to raise_error(RuntimeError)
122
- end
123
- it 'uses the IP address as the hostname' do
124
- driver.create(state)
125
- expect(state[:hostname]).to eq('1.2.3.4')
126
- end
127
- end
106
+ it 'sets the hostname in the state hash' do
107
+ allow(driver).to receive(:hostname_for).and_return('test_hostname')
108
+ driver.create(state)
109
+ expect(state[:hostname]).to eq('test_hostname')
128
110
  end
129
111
 
130
112
  it 'waits for the server to be ready' do
@@ -133,6 +115,41 @@ describe Kitchen::Driver::Vra do
133
115
  end
134
116
  end
135
117
 
118
+ describe '#hostname_for' do
119
+ let(:server) do
120
+ double('server',
121
+ id: 'test_id',
122
+ name: 'test_hostname',
123
+ ip_addresses: [ '1.2.3.4' ],
124
+ vm?: true)
125
+ end
126
+
127
+ context 'when use_dns is true' do
128
+ let(:config) { { use_dns: true } }
129
+
130
+ it 'raises an exception if the server name is nil' do
131
+ allow(server).to receive(:name).and_return(nil)
132
+ expect { driver.hostname_for(server) }.to raise_error(RuntimeError)
133
+ end
134
+
135
+ it 'returns the server name' do
136
+ expect(driver.hostname_for(server)).to eq('test_hostname')
137
+ end
138
+ end
139
+
140
+ context 'when use_dns is false' do
141
+ it 'falls back to the server name if no IP address exists' do
142
+ allow(server).to receive(:ip_addresses).and_return([])
143
+ expect(driver).to receive(:warn)
144
+ expect(driver.hostname_for(server)).to eq('test_hostname')
145
+ end
146
+
147
+ it 'returns the IP address if it exists' do
148
+ expect(driver.hostname_for(server)).to eq('1.2.3.4')
149
+ end
150
+ end
151
+ end
152
+
136
153
  describe '#request_server' do
137
154
  let(:submitted_request) { double('submitted_request') }
138
155
  let(:catalog_request) { double('catalog_request') }
@@ -204,12 +221,12 @@ describe Kitchen::Driver::Vra do
204
221
  end
205
222
  end
206
223
 
207
- describe '#wait_for_server_to_be_ready' do
224
+ describe '#wait_for_server' do
208
225
  let(:connection) { instance.transport.connection(state) }
209
226
  let(:state) { {} }
210
227
  let(:resource1) do
211
228
  double('server1',
212
- id: 'e8706351-cf4c-4c12-acb7-c90cc683b22c',
229
+ id: 'test_id',
213
230
  name: 'server1',
214
231
  ip_addresses: [ '1.2.3.4' ],
215
232
  vm?: true)
@@ -217,6 +234,9 @@ describe Kitchen::Driver::Vra do
217
234
 
218
235
  before do
219
236
  allow(transport).to receive(:connection).and_return(connection)
237
+ allow(driver).to receive(:sleep)
238
+ allow(driver).to receive(:warn)
239
+ allow(driver).to receive(:error)
220
240
  end
221
241
 
222
242
  it 'waits for the server to be ready' do
@@ -224,10 +244,59 @@ describe Kitchen::Driver::Vra do
224
244
  driver.wait_for_server(state, resource1)
225
245
  end
226
246
 
227
- it 'destroys the server and raises an exception if it fails to become ready' do
228
- allow(connection).to receive(:wait_until_ready).and_raise(Timeout::Error)
229
- expect(driver).to receive(:destroy).with(state)
230
- expect { driver.wait_for_server(state, resource1) }.to raise_error(Timeout::Error)
247
+ context 'when an exception is caught and retries is 0' do
248
+ let(:config) { { server_ready_retries: 0 } }
249
+
250
+ it 'does not sleep and raises an exception' do
251
+ allow(connection).to receive(:wait_until_ready).and_raise(Timeout::Error)
252
+ expect(driver).not_to receive(:sleep)
253
+ expect(driver).to receive(:error).with('Retries exceeded. Destroying server...')
254
+ expect { driver.wait_for_server(state, resource1) }.to raise_error(Timeout::Error)
255
+ end
256
+ end
257
+
258
+ context 'when retries is 1 and it errors out twice' do
259
+ let(:config) { { server_ready_retries: 1 } }
260
+
261
+ it 'displays a warning, sleeps once, retries, errors, destroys, and raises' do
262
+ expect(connection).to receive(:wait_until_ready).twice.and_raise(Timeout::Error)
263
+ expect(driver).to receive(:warn).once.with('Sleeping 2 seconds and retrying...')
264
+ expect(driver).to receive(:sleep).once.with(2)
265
+ expect(driver).to receive(:error).with('Retries exceeded. Destroying server...')
266
+ expect(driver).to receive(:destroy).with(state)
267
+ expect { driver.wait_for_server(state, resource1) }.to raise_error(Timeout::Error)
268
+ end
269
+ end
270
+
271
+ context 'when retries is 2 and it errors out all 3 times' do
272
+ let(:config) { { server_ready_retries: 2 } }
273
+
274
+ it 'displays 2 warnings, sleeps twice, retries, errors, destroys, and raises' do
275
+ expect(connection).to receive(:wait_until_ready).exactly(3).times.and_raise(Timeout::Error)
276
+ expect(driver).to receive(:warn).once.with('Sleeping 2 seconds and retrying...')
277
+ expect(driver).to receive(:warn).once.with('Sleeping 4 seconds and retrying...')
278
+ expect(driver).to receive(:sleep).once.with(2)
279
+ expect(driver).to receive(:sleep).once.with(4)
280
+ expect(driver).to receive(:error).with('Retries exceeded. Destroying server...')
281
+ expect(driver).to receive(:destroy).with(state)
282
+ expect { driver.wait_for_server(state, resource1) }.to raise_error(Timeout::Error)
283
+ end
284
+ end
285
+
286
+ context 'when retries is 5, it errors out the first 2 tries, but works on the 3rd' do
287
+ let(:config) { { server_ready_retries: 5 } }
288
+
289
+ it 'displays 2 warnings, sleeps twice, retries, but does not destroy or raise' do
290
+ expect(connection).to receive(:wait_until_ready).twice.and_raise(Timeout::Error)
291
+ expect(connection).to receive(:wait_until_ready).once.and_return(true)
292
+ expect(driver).to receive(:warn).once.with('Sleeping 2 seconds and retrying...')
293
+ expect(driver).to receive(:warn).once.with('Sleeping 4 seconds and retrying...')
294
+ expect(driver).to receive(:sleep).once.with(2)
295
+ expect(driver).to receive(:sleep).once.with(4)
296
+ expect(driver).not_to receive(:error)
297
+ expect(driver).not_to receive(:destroy)
298
+ expect { driver.wait_for_server(state, resource1) }.not_to raise_error
299
+ end
231
300
  end
232
301
  end
233
302
 
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: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Partner Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-13 00:00:00.000000000 Z
11
+ date: 2015-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: test-kitchen