kitchen-vcair 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,57 @@
1
+ # kitchen-vcair demonstration videos
2
+
3
+ Below are videos created by the original plugin author that demonstrate
4
+ kitchen-vcair and how to use it. Some configuration parameters have changed
5
+ since the initial development of this plugin to keep is consistent with other
6
+ VMware-based plugins, so be sure to check the README.md in this repo for the
7
+ latest instructions.
8
+
9
+ ## Linux
10
+
11
+ * [github.com/vulk/kitchen-vcair](https://www.youtube.com/watch?v=5srDko69XJ0&t=03)
12
+ * [vchs.vmware.com](https://www.youtube.com/watch?v=5srDko69XJ0&t=15)
13
+ * [Walkthrough steps for cloning, building gem](https://www.youtube.com/watch?v=5srDko69XJ0&t=30)
14
+ * [git clone git@github.com:/vulk/kitchen-vcair.git](https://www.youtube.com/watch?v=5srDko69XJ0&t=68)
15
+ * [cd kitchen-vcair](https://www.youtube.com/watch?v=5srDko69XJ0&t=94)
16
+ * [gem build kitchen-vcair.gemspec](https://www.youtube.com/watch?v=5srDko69XJ0&t=100)
17
+ * [gem install ./kitchen-vcair-0.1.0.gem](https://www.youtube.com/watch?v=5srDko69XJ0&t=120)
18
+ * [quick look through code ](https://www.youtube.com/watch?v=5srDko69XJ0&t=126)
19
+ * [git clone git@github.com:chef-cookbooks/httpd.git ](https://www.youtube.com/watch?v=5srDko69XJ0&t=173)
20
+ * [walkthrough of .kitchen.vcair.yml](https://www.youtube.com/watch?v=5srDko69XJ0&t=199)
21
+ * [walkthrough of environment variables](https://www.youtube.com/watch?v=5srDko69XJ0&t=247)
22
+ * [kitchen test](https://www.youtube.com/watch?v=5srDko69XJ0&t=282)
23
+ * [vchs.vmware.com virtualmachine list, showing creation of helloworldtest VM](https://www.youtube.com/watch?v=5srDko69XJ0&t=296)
24
+ * [knife vcair server list showing creation of helloworld test VM](https://www.youtube.com/watch?v=5srDko69XJ0&t=326)
25
+ * [instance provisionied, waiting for ssh](https://www.youtube.com/watch?v=5srDko69XJ0&t=355)
26
+ * [ssh available, installing chef-client](https://www.youtube.com/watch?v=5srDko69XJ0&t=400)
27
+ * [chef-client starting](https://www.youtube.com/watch?v=5srDko69XJ0&t=499)
28
+ * [chef-client finished, apache install completed](https://www.youtube.com/watch?v=5srDko69XJ0&t=515)
29
+ * [Kitchen Setup and Verify](https://www.youtube.com/watch?v=5srDko69XJ0&t=516)
30
+ * [Kitichen Destroy](https://www.youtube.com/watch?v=5srDko69XJ0&t=517)
31
+ * [Kitchen is finished](https://www.youtube.com/watch?v=5srDko69XJ0&t=525)
32
+ * [vchs.vmware.com and knife vcair shows vm destroyed](https://www.youtube.com/watch?v=5srDko69XJ0&t=530)
33
+
34
+ ## Windows
35
+
36
+ * [vmwair-vcair.env.example](https://www.youtube.com/watch?v=k8OZII4UGZs&t=09)
37
+ * [.kitchen.vcair.yml](https://www.youtube.com/watch?v=k8OZII4UGZs&t=20)
38
+ * [.yml / platforms:customization_script note](https://www.youtube.com/watch?v=k8OZII4UGZs&t=30)
39
+ * [customization_script install-winrm-vcair.bat](https://www.youtube.com/watch?v=k8OZII4UGZs&t=37)
40
+ * [git clone opscode-cookbooks/iis](https://www.youtube.com/watch?v=k8OZII4UGZs&t=54)
41
+ * [start coping files into iis cookbooks](https://www.youtube.com/watch?v=k8OZII4UGZs&t=60)
42
+ * [Add kitchen-vcair and kitchen-pester to the Gemfile](https://www.youtube.com/watch?v=k8OZII4UGZs&t=98)
43
+ * [bundle install kitchen vcair and pester](https://www.youtube.com/watch?v=k8OZII4UGZs&t=120)
44
+ * [KITCHEN_YAML=.kitchen.vcair.yml bundle exec kitchen verify](https://www.youtube.com/watch?v=k8OZII4UGZs&t=150)
45
+ * [Server is allocated.](https://www.youtube.com/watch?v=k8OZII4UGZs&t=270)
46
+ * ['pre'/'post' customization script ](https://www.youtube.com/watch?v=k8OZII4UGZs&t=300)
47
+ * ['pre' customization reboot ](https://www.youtube.com/watch?v=k8OZII4UGZs&t=412)
48
+ * ['post' customization boot ](https://www.youtube.com/watch?v=k8OZII4UGZs&t=440)
49
+ * [winrm is online](https://www.youtube.com/watch?v=k8OZII4UGZs&t=555)
50
+ * [installing chef omnibus](https://www.youtube.com/watch?v=k8OZII4UGZs&t=560)
51
+ * [chef-client starts](https://www.youtube.com/watch?v=k8OZII4UGZs&t=600)
52
+ * [iis:default recipe runs](https://www.youtube.com/watch?v=k8OZII4UGZs&t=630)
53
+ * [verification via kitche-pester](https://www.youtube.com/watch?v=k8OZII4UGZs&t=647)
54
+ * [kitchen verify complete!](https://www.youtube.com/watch?v=k8OZII4UGZs&t=660)
55
+ * [iis default web page via links](https://www.youtube.com/watch?v=k8OZII4UGZs&t=695)
56
+ * [kitchen verify again](https://www.youtube.com/watch?v=k8OZII4UGZs&t=710)
57
+ * [kitchen destroy](https://www.youtube.com/watch?v=k8OZII4UGZs&t=735)
@@ -0,0 +1,53 @@
1
+ @echo off
2
+
3
+ @rem First Boot...
4
+ if “%1%” == “precustomization” (
5
+
6
+ echo Do precustomization tasks
7
+ @rem during this boot the hostname is set, which requires a reboot
8
+
9
+ @rem we also enable winrm over http, plaintext, long timeout, more memory etc
10
+
11
+ cmd.exe /c winrm quickconfig -q
12
+ cmd.exe /c winrm quickconfig -transport:http
13
+ cmd.exe /c winrm set winrm/config @{MaxTimeoutms="1800000"}
14
+ cmd.exe /c winrm set winrm/config/winrs @{MaxMemoryPerShellMB="300"}
15
+ cmd.exe /c winrm set winrm/config/service @{AllowUnencrypted="true"}
16
+ cmd.exe /c winrm set winrm/config/service/auth @{Basic="true"}
17
+ cmd.exe /c winrm set winrm/config/client/auth @{Basic="true"}
18
+ cmd.exe /c winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"}
19
+
20
+ @rem Make sure winrm is off for this boot, but enabled on next
21
+ @rem as we don't want a tcp connection available until we are
22
+ @rem past postcustomization
23
+
24
+ cmd.exe /c net stop winrm
25
+ cmd.exe /c sc config winrm start= auto
26
+
27
+ @rem make sure the default on password age is unlimited
28
+ @rem this ensures we don't have a password change forced on us
29
+ cmd.exe /c net accounts /maxpwage:unlimited
30
+
31
+ @rem write out a timestamp for this first boot / customization completes
32
+ echo %DATE% %TIME% > C:\vm-is-customized
33
+
34
+ ) else if “%1%” == “postcustomization” (
35
+
36
+ @rem Second Boot / start winrm, just incase, and fix firewall
37
+
38
+ cmd.exe /c net start winrm
39
+ cmd.exe /c netsh advfirewall firewall set rule group="remote administration" new enable=yes
40
+ cmd.exe /c netsh firewall add portopening TCP 5985 "Port 5985 for WinRM"
41
+
42
+ @rem Password Setting and Autologin currently seem broken
43
+ @rem when done via the API, so we MUST set it in the postcustomization phase
44
+ cmd.exe /c net user administrator Password1
45
+
46
+ @rem in some environments we found the need to specify a DNS address
47
+ @rem cmd.exe /c netsh interface ipv4 add dnsserver "Ethernet" address=8.8.8.8
48
+ @rem cmd.exe /c netsh interface ipv4 add dnsserver "Ethernet0" address=8.8.8.8
49
+
50
+ @rem this is our 'ready' boot, password and winrm should be up
51
+ echo %DATE% %TIME% > C:\vm-is-ready
52
+
53
+ )
@@ -0,0 +1,32 @@
1
+ # Encoding: UTF-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'kitchen/driver/vcair_version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'kitchen-vcair'
9
+ spec.version = Kitchen::Driver::VCAIR_VERSION
10
+ spec.authors = ['Chef Partner Engineering', 'Taylor Carpenter', 'Chris McClimans']
11
+ spec.email = %w(partnereng@chef.io wolfpack+c+t@vulk.co)
12
+ spec.description = 'A Test Kitchen vCloud Air driver'
13
+ spec.summary = 'A Test Kitchen vCloud Air driver built on Fog'
14
+ spec.homepage = 'https://github.com/chef-partners/kitchen-vcair'
15
+ spec.license = 'Apache 2.0'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = %w(lib)
21
+
22
+ spec.required_ruby_version = '>= 2.0.0'
23
+
24
+ spec.add_dependency 'test-kitchen', '~> 1.4', '>= 1.4.1'
25
+ spec.add_dependency 'fog', '~> 1.33'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.7'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.2'
30
+ spec.add_development_dependency 'rubocop', '~> 0.33'
31
+ spec.add_development_dependency 'pry', '~> 0.10'
32
+ end
@@ -0,0 +1,359 @@
1
+ # Encoding: UTF-8
2
+ #
3
+ # Authors:: Chris McClimans (<c@vulk.co>)
4
+ # Authors:: Taylor Carpenter (<t@vulk.co>)
5
+ # Authors:: Chef Partner Engineering (<partnereng@chef.io>)
6
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
7
+ # License:: Apache License, Version 2.0
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+
21
+ require 'fog'
22
+ require 'kitchen'
23
+ require 'securerandom'
24
+
25
+ module Kitchen
26
+ module Driver
27
+ class Vcair < Kitchen::Driver::Base
28
+ attr_accessor :vapp_id
29
+
30
+ default_config :wait_for, 600
31
+ default_config :vcair_api_path, '/api'
32
+ default_config :catalog_id, nil
33
+ default_config :catalog_name, nil
34
+ default_config :image_id, nil
35
+ default_config :image_name, nil
36
+ default_config :vdc_id, nil
37
+ default_config :vdc_name, nil
38
+ default_config :network_id, nil
39
+ default_config :network_name, nil
40
+ default_config :cpus, 1
41
+ default_config :memory, 1024
42
+ default_config :vm_password
43
+
44
+ required_config :vcair_username
45
+ required_config :vcair_password
46
+ required_config :vcair_api_host
47
+ required_config :vcair_org
48
+
49
+ def initialize(config)
50
+ super
51
+ Fog.timeout = config[:wait_for].to_i
52
+ end
53
+
54
+ def name
55
+ 'vCloudAir'
56
+ end
57
+
58
+ def create(state)
59
+ return unless state[:vapp_id].nil?
60
+
61
+ validate!
62
+
63
+ create_server(state)
64
+ vm.wait_for { ready? }
65
+ state[:hostname] = vm.ip_address
66
+
67
+ info("Server #{state[:hostname]} is powered on. Waiting for it to be ready...")
68
+ wait_for_server(state)
69
+ end
70
+
71
+ def destroy(state)
72
+ return if state[:vapp_id].nil?
73
+
74
+ validate!
75
+
76
+ self.vapp_id = state[:vapp_id]
77
+
78
+ info("Destroying vApp #{vapp_id}...")
79
+ begin
80
+ vapp
81
+ rescue Fog::Compute::VcloudDirector::Forbidden
82
+ warn("Unable to locate vApp <#{state[:vapp_id]}> - assuming it is already destroyed.")
83
+ return
84
+ end
85
+
86
+ info('Powering off the vApp...')
87
+ vapp.power_off
88
+
89
+ info('Undeploying the vApp...')
90
+ vapp.undeploy
91
+
92
+ info('Deleting the vApp...')
93
+ vapp.destroy
94
+
95
+ info("vApp <#{state[:vapp_id]}> destroyed.")
96
+ end
97
+
98
+ def vcloud_client
99
+ @vcloud_client ||= Fog::Compute.new(fog_server_def)
100
+ rescue Excon::Errors::Unauthorized => e
101
+ raise "Connection failure, please check your username and password. -- #{e.message}"
102
+ end
103
+
104
+ def org
105
+ @org ||= vcloud_client.organizations.get_by_name(config[:vcair_org])
106
+ end
107
+
108
+ def create_server(state)
109
+ self.vapp_id = instantiate
110
+ state[:vapp_id] = vapp_id
111
+
112
+ info("vApp ID #{vapp_id} created.")
113
+
114
+ info('Validating the vApp...')
115
+ unless validate_vapp
116
+ destroy(state)
117
+ return
118
+ end
119
+
120
+ info('Updating the VM customization...')
121
+ update_customization
122
+
123
+ info('Adjusting VM hardware...')
124
+ adjust_hardware
125
+
126
+ info('Attaching it to the network...')
127
+ attach_network
128
+
129
+ info('Tagging the VM...')
130
+ tag_vm
131
+
132
+ info('Powering on the VM...')
133
+ power_on
134
+ end
135
+
136
+ def adjust_hardware
137
+ vm.cpu = config[:cpus] if config[:cpus]
138
+ vm.memory = config[:memory] if config[:memory]
139
+ end
140
+
141
+ def attach_network_payload
142
+ {
143
+ PrimaryNetworkConnectionIndex: 0,
144
+ NetworkConnection: [
145
+ {
146
+ network: network.name,
147
+ needsCustomization: true,
148
+ NetworkConnectionIndex: 0,
149
+ IsConnected: true,
150
+ IpAddressAllocationMode: 'POOL'
151
+ }
152
+ ]
153
+ }
154
+ end
155
+
156
+ def attach_network
157
+ task = vcloud_client.put_network_connection_system_section_vapp(vm.id, attach_network_payload)
158
+ vcloud_client.process_task(task.body)
159
+ end
160
+
161
+ def tag_vm
162
+ vm.tags.create('created-by', 'test-kitchen')
163
+ end
164
+
165
+ def power_on
166
+ vapp.power_on
167
+ end
168
+
169
+ def wait_for_server(state)
170
+ instance.transport.connection(state).wait_until_ready
171
+ rescue
172
+ error("Server #{vapp.id} (#{vm.name}) not reachable. Destroying server...")
173
+ destroy(state)
174
+ raise
175
+ end
176
+
177
+ def vcloud_username
178
+ [ config[:vcair_username], config[:vcair_org] ].join('@')
179
+ end
180
+
181
+ def fog_server_def
182
+ {
183
+ provider: 'vclouddirector',
184
+ vcloud_director_username: vcloud_username,
185
+ vcloud_director_password: config[:vcair_password],
186
+ vcloud_director_host: config[:vcair_api_host],
187
+ vcloud_director_api_version: config[:vcair_api_version],
188
+ vcloud_director_show_progress: false,
189
+ path: config[:vcair_api_path]
190
+ }
191
+ end
192
+
193
+ def image
194
+ if config[:image_id]
195
+ catalog.catalog_items.get(config[:image_id])
196
+ else
197
+ catalog.catalog_items.get_by_name(config[:image_name])
198
+ end
199
+ end
200
+
201
+ def catalog
202
+ if config[:catalog_id]
203
+ org.catalogs.get(config[:catalog_id])
204
+ else
205
+ org.catalogs.get_by_name(config[:catalog_name])
206
+ end
207
+ end
208
+
209
+ def vdc
210
+ if config[:vdc_id]
211
+ org.vdcs.get(config[:vdc_id])
212
+ else
213
+ org.vdcs.get_by_name(config[:vdc_name])
214
+ end
215
+ end
216
+
217
+ def network
218
+ if config[:network_id]
219
+ org.networks.get(config[:network_id])
220
+ else
221
+ org.networks.get_by_name(config[:network_name])
222
+ end
223
+ end
224
+
225
+ def node_description
226
+ config[:node_description] || "Test Kitchen: #{node_name}"
227
+ end
228
+
229
+ def node_name
230
+ config[:node_name] || generate_node_name
231
+ end
232
+
233
+ def generate_node_name
234
+ # SecureRandom.hex generates a string 2x the argument.
235
+ # We need the name to be 15 chars or less to play nicely
236
+ # with windows, so we're generating a 12-char random
237
+ # string prefixed with "tk-"
238
+ 'tk-' + SecureRandom.hex(6)
239
+ end
240
+
241
+ def instantiate_config
242
+ {
243
+ vdc_id: vdc.id,
244
+ network_id: network.id,
245
+ description: node_description
246
+ }
247
+ end
248
+
249
+ def print_error_and_exit(message)
250
+ error(message)
251
+ fail message
252
+ end
253
+
254
+ def validate!
255
+ %w(vdc catalog image network).each do |param|
256
+ validate_parameter_pair!(param)
257
+ end
258
+
259
+ [ :org, :vdc, :catalog, :image, :network].each do |method|
260
+ validate_method!(method)
261
+ end
262
+
263
+ validate_customization_script!
264
+ validate_computer_name!
265
+ end
266
+
267
+ def validate_parameter_pair!(param)
268
+ id_key = param + '_id'
269
+ name_key = param + '_name'
270
+
271
+ print_error_and_exit("No #{param} found. You must specify #{id_key} or #{name_key}.") if
272
+ config[id_key.to_sym].nil? && config[name_key.to_sym].nil?
273
+ end
274
+
275
+ def validate_method!(method)
276
+ send(method)
277
+ rescue => e
278
+ raise "Unable to validate #{method} - check your configuration and try again. #{e.class} -- #{e.message}"
279
+ end
280
+
281
+ def validate_computer_name!
282
+ # regex proudly modified after stealing from:
283
+ # http://stackoverflow.com/questions/2063213/regular-expression-for-validating-dns-label-host-name
284
+ print_error_and_exit('Node name is not valid - must be 15 characters or less, and be a valid Windows node name') unless
285
+ node_name =~ /^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{,15}(?<!-)$/
286
+ end
287
+
288
+ def validate_customization_script!
289
+ return unless config[:customization_script]
290
+
291
+ print_error_and_exit("Customization script #{config[:customization_script]} is not found or not readable.") unless
292
+ ::File.readable?(config[:customization_script])
293
+ end
294
+
295
+ def instantiate
296
+ image.instantiate(node_name, instantiate_config)
297
+ end
298
+
299
+ def vapp
300
+ @vapp ||= vdc.vapps.get(vapp_id)
301
+ end
302
+
303
+ def vm
304
+ @vm ||= vapp.vms.first
305
+ end
306
+
307
+ def validate_vapp
308
+ vms = vapp.vms
309
+ if vms.empty?
310
+ error('vApp created, but did not contain any VMs')
311
+ return false
312
+ end
313
+
314
+ if vms.size > 1
315
+ error('vApp created, but contained more than one VM')
316
+ return false
317
+ end
318
+
319
+ true
320
+ end
321
+
322
+ def customization
323
+ @customization ||= vm.customization
324
+ end
325
+
326
+ def update_customization
327
+ set_customization_script if config[:customization_script]
328
+ set_customization_password
329
+ set_customization_computer_name
330
+ save_customization
331
+ end
332
+
333
+ def set_customization_script
334
+ customization.script = ::File.read(config[:customization_script])
335
+ end
336
+
337
+ def set_customization_password
338
+ if config[:vm_password]
339
+ customization.admin_password = config[:vm_password]
340
+ customization.admin_password_auto = false
341
+ customization.reset_password_required = false
342
+ else
343
+ customization.admin_password = nil
344
+ customization.admin_password_auto = true
345
+ customization.reset_password_required = false
346
+ end
347
+ end
348
+
349
+ def set_customization_computer_name
350
+ customization.computer_name = node_name
351
+ end
352
+
353
+ def save_customization
354
+ customization.enabled = true
355
+ customization.save
356
+ end
357
+ end
358
+ end
359
+ end