kitchen-hyperv 0.5.3 → 0.7.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
  SHA256:
3
- metadata.gz: f95a36b67d309f5558ce09e652f1b2e1dae920b3e4f7dd87ef42e874d0d70b89
4
- data.tar.gz: d226329f6c724cf74adce0422276f0b4346e57cd7f1964e42f24a7c2a59c88aa
3
+ metadata.gz: 21905b2f2c54db8a9414e98231c4eaa08c66cfa8aab926bf41248a8159663b29
4
+ data.tar.gz: 2ae8c16b86ee2933635c59f5e8403c42507d9ea2461c4c581c3468e409967c89
5
5
  SHA512:
6
- metadata.gz: 3a71c5e7ae19eadf9bdacd4fa616365e162f944c36bc14b3091cdabc71ca845062d900790d79455a1b07fd68b159aacc85b5ab861b798e14f82ad8d230129273
7
- data.tar.gz: eac21386957db5983da0411ad14d11c5b7f2bbf38406b57fd78c56ca05c32e137b62be9e7e8ba0638eaeaab633b52a1b323d339d4cad743735a4291172384d4d
6
+ metadata.gz: 171d1d798fad06869c129086e5b72a093b3457ed4149cfb0ddc971756e70590f5359149a042519bc0d208381b61c9ebffc6b2c5c16f28c63f128ee14edf8815c
7
+ data.tar.gz: c7ff58b26dda26b67289a8acf66ae3bbdd152862b85e6bb9b2ed592833b0c2e44e980e3c4b8e17d24d1a108fe734820ed1da241778713c6d74819c9cd04ad8e0
data/Gemfile ADDED
@@ -0,0 +1,25 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in kitchen-pester.gemspec
4
+ gemspec
5
+
6
+ group :integration do
7
+ gem "berkshelf"
8
+ gem "kitchen-inspec"
9
+ gem "kitchen-dokken"
10
+ gem "kitchen-vagrant"
11
+ end
12
+
13
+ group :debug do
14
+ gem "pry"
15
+ gem "pry-byebug"
16
+ gem "pry-stack_explorer"
17
+ end
18
+
19
+ group :chefstyle do
20
+ gem "chefstyle"
21
+ end
22
+
23
+ group :docs do
24
+ gem "yard"
25
+ end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 Steven Murawski
1
+ Copyright (c) 2020 Steven Murawski
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require "bundler/gem_tasks"
2
+ require "kitchen/driver/hyperv_version"
3
+
4
+ require "rake/testtask"
5
+ Rake::TestTask.new(:unit) do |t|
6
+ t.libs.push "lib"
7
+ t.test_files = FileList["spec/**/*_spec.rb"]
8
+ t.verbose = true
9
+ end
10
+
11
+ desc "Run all test suites"
12
+ task test: :unit
13
+
14
+ begin
15
+ require "chefstyle"
16
+ require "rubocop/rake_task"
17
+ RuboCop::RakeTask.new(:style) do |task|
18
+ task.options += ["--display-cop-names", "--no-color"]
19
+ end
20
+ rescue LoadError
21
+ puts "chefstyle is not available. (sudo) gem install chefstyle to do style checking."
22
+ end
23
+
24
+ desc "Run all quality tasks"
25
+ task quality: :style
26
+
27
+ begin
28
+ require "yard" unless defined?(YARD)
29
+ YARD::Rake::YardocTask.new
30
+ rescue LoadError
31
+ puts "yard is not available. (sudo) gem install yard to generate yard documentation."
32
+ end
33
+
34
+ task default: %i{test quality}
35
+ begin
36
+ require "github_changelog_generator/task"
37
+
38
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
39
+ config.future_release = "v#{Kitchen::Driver::HYPERV_VERSION}"
40
+ config.issues = false
41
+ config.pulls = true
42
+ config.user = "test-kitchen"
43
+ config.project = "kitchen-hyperv"
44
+ end
45
+ rescue LoadError
46
+ puts "github_changelog_generator is not available. " \
47
+ "gem install github_changelog_generator to generate changelogs"
48
+ end
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require "kitchen/driver/hyperv_version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kitchen-hyperv"
8
+ spec.version = Kitchen::Driver::HYPERV_VERSION
9
+ spec.authors = ["Steven Murawski"]
10
+ spec.email = ["steven.murawski@gmail.com"]
11
+ spec.summary = "Hyper-V Driver for Test-Kitchen"
12
+ spec.description = "Hyper-V Driver for Test-Kitchen"
13
+ spec.homepage = "https://github.com/test-kitchen/kitchen-hyperv"
14
+ spec.license = "Apache-2.0"
15
+
16
+ spec.files = %w{LICENSE kitchen-hyperv.gemspec Gemfile Rakefile support/hyperv.ps1} + Dir.glob("lib/**/*")
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "rake"
20
+ spec.add_development_dependency "minitest", "~> 5.3", "< 5.15"
21
+ spec.add_development_dependency "minitest-stub-const"
22
+ spec.add_development_dependency "mocha", "~> 1.1"
23
+
24
+ spec.add_dependency "test-kitchen", ">= 1.4", "< 4"
25
+ spec.add_dependency "train", "~> 3.5"
26
+ spec.add_dependency "train-winrm", "~> 0.2"
27
+ end
@@ -1,279 +1,354 @@
1
- #
2
- # Author:: Steven Murawski <smurawski@chef.io>
3
- # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require 'kitchen'
20
- require 'kitchen/driver'
21
- require 'kitchen/driver/hyperv_version'
22
- require 'kitchen/driver/powershell'
23
- require 'mixlib/shellout'
24
- require 'fileutils'
25
- require 'json'
26
-
27
- module Kitchen
28
-
29
- module Driver
30
-
31
- # Driver for Hyper-V
32
- class Hyperv < Kitchen::Driver::Base
33
-
34
- kitchen_driver_api_version 2
35
- plugin_version Kitchen::Driver::HYPERV_VERSION
36
-
37
- default_config :parent_vhd_folder
38
- default_config :parent_vhd_name
39
- default_config :memory_startup_bytes, 536_870_912
40
- default_config :dynamic_memory_min_bytes, 536_870_912
41
- default_config :dynamic_memory_max_bytes, 2_147_483_648
42
- default_config :dynamic_memory, false
43
- default_config :processor_count, 2
44
- default_config :ip_address
45
- default_config :gateway
46
- default_config :dns_servers
47
- default_config :subnet, '255.255.255.0'
48
- default_config :vm_switch
49
- default_config :vm_vlan_id
50
- default_config :iso_path
51
- default_config :boot_iso_path
52
- default_config :enable_guest_services
53
- default_config :vm_note
54
- default_config :resize_vhd
55
- default_config :additional_disks
56
- default_config :vm_generation, 1
57
- default_config :disable_secureboot, false
58
- default_config :static_mac_address
59
- default_config :disk_type do |driver|
60
- File.extname(driver[:parent_vhd_name])
61
- end
62
-
63
- include Kitchen::Driver::PowerShellScripts
64
-
65
- def create(state)
66
- @state = state
67
- validate_vm_settings
68
- create_new_differencing_disk
69
- create_additional_disks
70
- create_virtual_machine
71
- set_virtual_machine_note
72
- update_state
73
- mount_virtual_machine_iso
74
- instance.transport.connection(@state).wait_until_ready
75
- copy_vm_files
76
- info("Hyper-V instance #{instance.to_str} created.")
77
- end
78
-
79
- def destroy(state)
80
- @state = state
81
- if differencing_disk_exists && !vm_exists_silent
82
- remove_differencing_disk
83
- end
84
- return unless vm_exists
85
- instance.transport.connection(state).close
86
- remove_virtual_machine
87
- remove_differencing_disk
88
- remove_additional_disks
89
- info("The Hyper-V instance #{instance.to_str} has been removed.")
90
- state.delete(:id)
91
- end
92
-
93
- private
94
-
95
- def validate_vm_settings
96
- raise "Missing parent_vhd_folder" unless vhd_folder?
97
- raise "Missing parent_vhd_name" unless vhd?
98
- if config[:dynamic_memory]
99
- startup_bytes = config[:memory_startup_bytes]
100
- min = config[:dynamic_memory_min_bytes]
101
- max = config[:dynamic_memory_max_bytes]
102
- memory_valid = startup_bytes.between?(min, max)
103
- warning = "memory_startup_bytes (#{startup_bytes}) must" \
104
- " fall within dynamic memory range (#{min}-#{max})"
105
- raise warning unless memory_valid
106
- end
107
- config[:vm_switch] = vm_switch
108
- if config[:vm_vlan_id]
109
- vm_vlan_id = config[:vm_vlan_id]
110
- vm_vlan_id_min = 1
111
- vm_vlan_id_max = 4094
112
- vm_vlan_id_valid = vm_vlan_id.between?(vm_vlan_id_min, vm_vlan_id_max)
113
- vm_vlan_id_warning = "vm_vlan_id (#{vm_vlan_id}) must be a valid 802.1Q" \
114
- " VLAN ID between (#{vm_vlan_id_min}-#{vm_vlan_id_max})"
115
- raise vm_vlan_id_warning unless vm_vlan_id_valid
116
- end
117
- end
118
-
119
- def create_new_differencing_disk
120
- info("Creating differencing disk for #{instance.name}.")
121
- run_ps new_differencing_disk_ps
122
- info("Created differencing disk for #{instance.name}.")
123
- set_new_vhd_size
124
- end
125
-
126
- def create_additional_disks
127
- return if config[:additional_disks].nil?
128
- @additional_disk_objects = []
129
- config[:additional_disks].each do |additional_disk|
130
- raise "Missing name for additional disk" unless additional_disk[:name]
131
- disk_type = additional_disk[:type] || config[:disk_type]
132
- disk_path = additional_disk_path(additional_disk[:name], disk_type)
133
- raise "Additional disk file already exists: #{disk_path}" unless !File.exist?(disk_path)
134
- disk_size = additional_disk[:size_gb] || 5
135
- info("Creating additional disk #{additional_disk[:name]} for #{instance.name}.")
136
- run_ps new_additional_disk_ps(disk_path, disk_size)
137
- info("Created additional disk #{additional_disk[:name]} for #{instance.name}.")
138
- @additional_disk_objects.push(disk_path)
139
- end
140
- end
141
-
142
- def vm_switch
143
- default_switch_object = run_ps vm_default_switch_ps
144
- if default_switch_object.nil? ||
145
- !default_switch_object.key?('Name') ||
146
- default_switch_object['Name'].empty?
147
- raise "Failed to find a default VM Switch."
148
- end
149
- default_switch_object['Name']
150
- end
151
-
152
- def create_virtual_machine
153
- return if vm_exists
154
- info("Creating virtual machine for #{instance.name}.")
155
- new_vm_object = run_ps new_vm_ps
156
- @state[:id] = new_vm_object['Id']
157
- info("Created virtual machine for #{instance.name}.")
158
- end
159
-
160
- def update_state
161
- vm_details
162
- @state[:id] = @vm['Id']
163
- @state[:hostname] = @vm['IpAddress']
164
- @state[:vm_name] = @vm['Name']
165
- end
166
-
167
- def vm_details
168
- run_ps set_vm_ipaddress_ps if config[:ip_address]
169
- @vm = run_ps vm_details_ps
170
- end
171
-
172
- def mount_virtual_machine_iso
173
- return unless config[:iso_path]
174
- info("Mounting #{config[:iso_path]}")
175
- run_ps mount_vm_iso
176
- info("Done mounting #{config[:iso_path]}")
177
- end
178
-
179
- def set_new_vhd_size
180
- return unless config[:resize_vhd]
181
- info("Resizing differencing disk for #{instance.name}.")
182
- run_ps resize_vhd
183
- info("Resized differencing disk for #{instance.name}.")
184
- end
185
-
186
- def set_virtual_machine_note
187
- return unless config[:vm_note]
188
- info("Adding note to VM: '#{config[:vm_note]}'")
189
- run_ps set_vm_note
190
- end
191
-
192
- def copy_vm_files
193
- return if config[:copy_vm_files].nil?
194
- info("Copying files to virtual machine")
195
- config[:copy_vm_files].each do |file_info|
196
- run_ps copy_vm_file_ps(file_info[:source], file_info[:dest])
197
- end
198
- info("Copied files to virtual machine")
199
- end
200
-
201
- def vm_exists
202
- info('Checking for existing virtual machine.')
203
- return false unless @state.key?(:id) && !@state[:id].nil?
204
- existing_vm = run_ps ensure_vm_running_ps
205
- return false if existing_vm.nil? || existing_vm['Id'].nil?
206
- info("Found an exising VM with an ID: #{existing_vm['Id']}")
207
- true
208
- end
209
-
210
- # Used in testing if a stale diff disk exists. Silent so the output doesn't
211
- # appear twice on the kitchen destroy command for the second check for vm_exists
212
- def vm_exists_silent
213
- return false unless @state.key?(:id) && !@state[:id].nil?
214
- existing_vm = run_ps ensure_vm_running_ps
215
- return false if existing_vm.nil? || existing_vm['Id'].nil?
216
- true
217
- end
218
-
219
- def differencing_disk_exists
220
- return unless File.exist? differencing_disk_path
221
- true
222
- end
223
-
224
- def remove_virtual_machine
225
- info("Deleting virtual machine for #{instance.name}")
226
- run_ps delete_vm_ps
227
- info("Deleted virtual machine for #{instance.name}")
228
- end
229
-
230
- def remove_differencing_disk
231
- info("Removing the differencing disk for #{instance.name}.")
232
- FileUtils.rm(differencing_disk_path)
233
- info("Removed the differencing disk for #{instance.name}.")
234
- end
235
-
236
- def remove_additional_disks
237
- return if config[:additional_disks].nil?
238
- config[:additional_disks].each do |additional_disk|
239
- raise "Missing name for additional disk" unless additional_disk[:name]
240
- disk_type = additional_disk[:type] || config[:disk_type]
241
- disk_path = additional_disk_path(additional_disk[:name], disk_type)
242
- if File.exist?(disk_path)
243
- info("Removing additional disk #{additional_disk[:name]} for #{instance.name}.")
244
- FileUtils.rm(disk_path)
245
- info("Removed additional disk #{additional_disk[:name]} for #{instance.name}.")
246
- end
247
- end
248
- end
249
-
250
- def kitchen_vm_path
251
- @kitchen_vm_path ||= File.join(config[:kitchen_root], ".kitchen/#{instance.name}")
252
- end
253
-
254
- def boot_iso_path
255
- @boot_iso_path ||= config[:boot_iso_path]
256
- end
257
-
258
- def differencing_disk_path
259
- @differencing_disk_path ||= File.join(kitchen_vm_path, "diff" + "#{config[:disk_type]}")
260
- end
261
-
262
- def additional_disk_path(disk_name, disk_type)
263
- File.join(kitchen_vm_path, disk_name + disk_type)
264
- end
265
-
266
- def parent_vhd_path
267
- @parent_vhd_path ||= File.join(config[:parent_vhd_folder], config[:parent_vhd_name])
268
- end
269
-
270
- def vhd_folder?
271
- config[:parent_vhd_folder] && Dir.exist?(config[:parent_vhd_folder])
272
- end
273
-
274
- def vhd?
275
- config[:parent_vhd_name] && File.exist?(parent_vhd_path)
276
- end
277
- end
278
- end
279
- end
1
+ #
2
+ # Author:: Steven Murawski <smurawski@chef.io>
3
+ # Copyright:: Copyright (c) 2020 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "kitchen"
20
+ require "kitchen/driver"
21
+ require_relative "hyperv_version"
22
+ require_relative "powershell"
23
+ require "mixlib/shellout" unless defined?(Mixlib::ShellOut)
24
+ require "fileutils" unless defined?(FileUtils)
25
+ require "json" unless defined?(JSON)
26
+ require "train" unless defined?(Train)
27
+ require "train-winrm" unless defined?(TrainPlugins::WinRM)
28
+
29
+ module Kitchen
30
+
31
+ module Driver
32
+
33
+ # Driver for Hyper-V
34
+ class Hyperv < Kitchen::Driver::Base
35
+
36
+ kitchen_driver_api_version 2
37
+ plugin_version Kitchen::Driver::HYPERV_VERSION
38
+
39
+ required_config :parent_vhd_folder
40
+ required_config :parent_vhd_name
41
+
42
+ default_config :memory_startup_bytes, 536_870_912
43
+ default_config :dynamic_memory_min_bytes, 536_870_912
44
+ default_config :dynamic_memory_max_bytes, 2_147_483_648
45
+ default_config :dynamic_memory, false
46
+ default_config :processor_count, 2
47
+ default_config :ip_address
48
+ default_config :gateway
49
+ default_config :dns_servers
50
+ default_config :subnet, "255.255.255.0"
51
+ default_config :vm_switch
52
+ default_config :vm_vlan_id
53
+ default_config :iso_path
54
+ default_config :boot_iso_path
55
+ default_config :enable_guest_services
56
+ default_config :vm_note
57
+ default_config :resize_vhd
58
+ default_config :additional_disks
59
+ default_config :vm_generation, 1
60
+ default_config :disable_secureboot, false
61
+ default_config :static_mac_address
62
+ default_config :disk_type do |driver|
63
+ File.extname(driver[:parent_vhd_name])
64
+ end
65
+
66
+ default_config :hyperv_server, nil
67
+ default_config :hyperv_username, nil
68
+ default_config :hyperv_password, nil
69
+ default_config :hyperv_ssl, false
70
+ default_config :hyperv_insecure, true
71
+ default_config :remote_vm_path, 'C:\Users\Public\Documents\Hyper-V'
72
+
73
+ include Kitchen::Driver::PowerShellScripts
74
+
75
+ def create(state)
76
+ @state = state
77
+ validate_vm_settings
78
+ create_new_differencing_disk
79
+ create_additional_disks
80
+ create_virtual_machine
81
+ set_virtual_machine_note
82
+ update_state
83
+ mount_virtual_machine_iso
84
+ instance.transport.connection(@state).wait_until_ready
85
+ copy_vm_files
86
+ info("Hyper-V instance #{instance.to_str} created.")
87
+ end
88
+
89
+ def destroy(state)
90
+ @state = state
91
+ if differencing_disk_exists && !vm_exists_silent
92
+ remove_differencing_disk
93
+ end
94
+ return unless vm_exists
95
+
96
+ instance.transport.connection(state).close
97
+ remove_virtual_machine
98
+ remove_differencing_disk
99
+ remove_additional_disks
100
+ info("The Hyper-V instance #{instance.to_str} has been removed.")
101
+ state.delete(:id)
102
+ end
103
+
104
+ private
105
+
106
+ def validate_vm_settings
107
+ raise "Missing parent_vhd_folder" unless vhd_folder? || remote_hyperv
108
+ raise "Missing parent_vhd_name" unless vhd? || remote_hyperv
109
+
110
+ if config[:dynamic_memory]
111
+ startup_bytes = config[:memory_startup_bytes]
112
+ min = config[:dynamic_memory_min_bytes]
113
+ max = config[:dynamic_memory_max_bytes]
114
+ memory_valid = startup_bytes.between?(min, max)
115
+ warning = "memory_startup_bytes (#{startup_bytes}) must" \
116
+ " fall within dynamic memory range (#{min}-#{max})"
117
+ raise warning unless memory_valid
118
+ end
119
+ config[:vm_switch] = vm_switch
120
+ if config[:vm_vlan_id]
121
+ vm_vlan_id = config[:vm_vlan_id]
122
+ vm_vlan_id_min = 1
123
+ vm_vlan_id_max = 4094
124
+ vm_vlan_id_valid = vm_vlan_id.between?(vm_vlan_id_min, vm_vlan_id_max)
125
+ vm_vlan_id_warning = "vm_vlan_id (#{vm_vlan_id}) must be a valid 802.1Q" \
126
+ " VLAN ID between (#{vm_vlan_id_min}-#{vm_vlan_id_max})"
127
+ raise vm_vlan_id_warning unless vm_vlan_id_valid
128
+ end
129
+ end
130
+
131
+ def create_new_differencing_disk
132
+ info("Creating differencing disk for #{instance.name}.")
133
+ run_ps new_differencing_disk_ps
134
+ info("Created differencing disk for #{instance.name}.")
135
+ set_new_vhd_size
136
+ end
137
+
138
+ def create_additional_disks
139
+ return if config[:additional_disks].nil?
140
+
141
+ @additional_disk_objects = []
142
+ config[:additional_disks].each do |additional_disk|
143
+ raise "Missing name for additional disk" unless additional_disk[:name]
144
+
145
+ disk_type = additional_disk[:type] || config[:disk_type]
146
+ disk_path = additional_disk_path(additional_disk[:name], disk_type)
147
+ raise "Additional disk file already exists: #{disk_path}" if File.exist?(disk_path)
148
+
149
+ disk_size = additional_disk[:size_gb] || 5
150
+ info("Creating additional disk #{additional_disk[:name]} for #{instance.name}.")
151
+ run_ps new_additional_disk_ps(disk_path, disk_size)
152
+ info("Created additional disk #{additional_disk[:name]} for #{instance.name}.")
153
+ @additional_disk_objects.push(disk_path)
154
+ end
155
+ end
156
+
157
+ def vm_switch
158
+ default_switch_object = run_ps vm_default_switch_ps
159
+ if default_switch_object.nil? ||
160
+ !default_switch_object.key?("Name") ||
161
+ default_switch_object["Name"].empty?
162
+ raise "Failed to find a default VM Switch."
163
+ end
164
+
165
+ default_switch_object["Name"]
166
+ end
167
+
168
+ def create_virtual_machine
169
+ return if vm_exists
170
+
171
+ info("Creating virtual machine for #{instance.name}.")
172
+ new_vm_object = run_ps new_vm_ps
173
+ raise "Unable to create virtual machine for #{instance.name}." if new_vm_object.nil?
174
+ @state[:id] = new_vm_object["Id"]
175
+ info("Created virtual machine for #{instance.name}.")
176
+ end
177
+
178
+ def update_state
179
+ vm_details
180
+ @state[:id] = @vm["Id"]
181
+ @state[:hostname] = @vm["IpAddress"]
182
+ @state[:vm_name] = @vm["Name"]
183
+ end
184
+
185
+ def vm_details
186
+ run_ps set_vm_ipaddress_ps if config[:ip_address]
187
+ @vm = run_ps vm_details_ps
188
+ end
189
+
190
+ def mount_virtual_machine_iso
191
+ return unless config[:iso_path]
192
+
193
+ info("Mounting #{config[:iso_path]}")
194
+ run_ps mount_vm_iso
195
+ info("Done mounting #{config[:iso_path]}")
196
+ end
197
+
198
+ def set_new_vhd_size
199
+ return unless config[:resize_vhd]
200
+
201
+ info("Resizing differencing disk for #{instance.name}.")
202
+ run_ps resize_vhd
203
+ info("Resized differencing disk for #{instance.name}.")
204
+ end
205
+
206
+ def set_virtual_machine_note
207
+ return unless config[:vm_note]
208
+
209
+ info("Adding note to VM: '#{config[:vm_note]}'")
210
+ run_ps set_vm_note
211
+ end
212
+
213
+ def copy_vm_files
214
+ return if config[:copy_vm_files].nil?
215
+
216
+ info("Copying files to virtual machine")
217
+ config[:copy_vm_files].each do |file_info|
218
+ run_ps copy_vm_file_ps(file_info[:source], file_info[:dest])
219
+ end
220
+ info("Copied files to virtual machine")
221
+ end
222
+
223
+ def vm_exists
224
+ info("Checking for existing virtual machine.")
225
+ return false unless @state.key?(:id) && !@state[:id].nil?
226
+
227
+ existing_vm = run_ps ensure_vm_running_ps
228
+ return false if existing_vm.nil? || existing_vm["Id"].nil?
229
+
230
+ info("Found an exising VM with an ID: #{existing_vm["Id"]}")
231
+ true
232
+ end
233
+
234
+ # Used in testing if a stale diff disk exists. Silent so the output doesn't
235
+ # appear twice on the kitchen destroy command for the second check for vm_exists
236
+ def vm_exists_silent
237
+ return false unless @state.key?(:id) && !@state[:id].nil?
238
+
239
+ existing_vm = run_ps ensure_vm_running_ps
240
+ return false if existing_vm.nil? || existing_vm["Id"].nil?
241
+
242
+ true
243
+ end
244
+
245
+ def differencing_disk_exists
246
+ return unless File.exist? differencing_disk_path
247
+
248
+ true
249
+ end
250
+
251
+ def remove_virtual_machine
252
+ info("Deleting virtual machine for #{instance.name}")
253
+ run_ps delete_vm_ps
254
+ info("Deleted virtual machine for #{instance.name}")
255
+ end
256
+
257
+ def remove_differencing_disk
258
+ return unless differencing_disk_exists
259
+
260
+ info("Removing the differencing disk for #{instance.name}.")
261
+ FileUtils.rm(differencing_disk_path)
262
+ info("Removed the differencing disk for #{instance.name}.")
263
+ end
264
+
265
+ def remove_additional_disks
266
+ return if config[:additional_disks].nil?
267
+
268
+ config[:additional_disks].each do |additional_disk|
269
+ raise "Missing name for additional disk" unless additional_disk[:name]
270
+
271
+ disk_type = additional_disk[:type] || config[:disk_type]
272
+ disk_path = additional_disk_path(additional_disk[:name], disk_type)
273
+ if File.exist?(disk_path)
274
+ info("Removing additional disk #{additional_disk[:name]} for #{instance.name}.")
275
+ FileUtils.rm(disk_path)
276
+ info("Removed additional disk #{additional_disk[:name]} for #{instance.name}.")
277
+ end
278
+ end
279
+ end
280
+
281
+ def kitchen_vm_path
282
+ @kitchen_vm_path ||= File.join(config[:kitchen_root], ".kitchen/#{instance.name}")
283
+ end
284
+
285
+ def remote_kitchen_vm_path
286
+ config[:remote_vm_path]
287
+ end
288
+
289
+ def boot_iso_path
290
+ @boot_iso_path ||= config[:boot_iso_path]
291
+ end
292
+
293
+ def differencing_disk_path
294
+ kitchen_vm_base = remote_hyperv ? remote_kitchen_vm_path : kitchen_vm_path
295
+
296
+ @differencing_disk_path ||= File.join(kitchen_vm_base, "diff" + "#{config[:disk_type]}")
297
+ end
298
+
299
+ def additional_disk_path(disk_name, disk_type)
300
+ File.join(kitchen_vm_path, disk_name + disk_type)
301
+ end
302
+
303
+ def parent_vhd_path
304
+ @parent_vhd_path ||= File.join(config[:parent_vhd_folder], config[:parent_vhd_name])
305
+ end
306
+
307
+ def vhd_folder?
308
+ config[:parent_vhd_folder] && Dir.exist?(config[:parent_vhd_folder])
309
+ end
310
+
311
+ def vhd?
312
+ config[:parent_vhd_name] && File.exist?(parent_vhd_path)
313
+ end
314
+
315
+ def remote_hyperv
316
+ !!config[:hyperv_server]
317
+ end
318
+
319
+ def connection
320
+ return @connection if @connection
321
+
322
+ backend = remote_hyperv ? "winrm" : "local"
323
+
324
+ train = Train.create(backend, {
325
+ host: config[:hyperv_server],
326
+ user: config[:hyperv_username],
327
+ password: config[:hyperv_password],
328
+ ssl: config[:hyperv_ssl],
329
+ self_signed: config[:hyperv_insecure],
330
+ })
331
+ @connection = train.connection
332
+
333
+ # Copy support PS1
334
+ @connection.upload(local_script_path, remote_script_path)
335
+
336
+ @connection
337
+ end
338
+
339
+ def base_script_path
340
+ return remote_script_path if remote_hyperv
341
+
342
+ local_script_path
343
+ end
344
+
345
+ def local_script_path
346
+ File.join(File.dirname(__FILE__), "/../../../support/hyperv.ps1")
347
+ end
348
+
349
+ def remote_script_path
350
+ File.join(config[:kitchen_root], "kitchen-hyperv", "hyperv.ps1")
351
+ end
352
+ end
353
+ end
354
+ end
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Steven Murawski <smurawski@chef.io>
3
- # Copyright:: Copyright (c) 2015-2018 Chef Software, Inc.
3
+ # Copyright:: Copyright (c) 2015-2020 Chef Software, Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,6 +17,6 @@
17
17
 
18
18
  module Kitchen
19
19
  module Driver
20
- HYPERV_VERSION = '0.5.3'.freeze
20
+ HYPERV_VERSION = "0.7.0".freeze
21
21
  end
22
22
  end
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Steven Murawski <smurawski@chef.io>
3
- # Copyright:: Copyright (c) 2015 Chef Software, Inc.
3
+ # Copyright:: Copyright (c) 2020 Chef Software, Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,28 +15,31 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
- require 'mixlib/shellout'
19
- require 'fileutils'
20
- require 'json'
18
+ require "mixlib/shellout" unless defined?(Mixlib::ShellOut)
19
+ require "benchmark" unless defined?(Benchmark)
20
+ require "fileutils" unless defined?(FileUtils)
21
+ require "json" unless defined?(JSON)
21
22
 
22
23
  module Kitchen
23
24
  module Driver
24
25
  module PowerShellScripts
25
26
  def encode_command(script)
26
- encoded_script = script.encode('UTF-16LE', 'UTF-8')
27
+ encoded_script = script.encode("UTF-16LE", "UTF-8")
27
28
  Base64.strict_encode64(encoded_script)
28
29
  end
29
30
 
30
31
  def is_64bit?
31
- os_arch = ENV['PROCESSOR_ARCHITEW6432'] || ENV['PROCESSOR_ARCHITECTURE']
32
- ruby_arch = ['foo'].pack('p').size == 4 ? 32 : 64
33
- os_arch == 'AMD64' && ruby_arch == 64
32
+ return true if remote_hyperv
33
+
34
+ os_arch = ENV["PROCESSOR_ARCHITEW6432"] || ENV["PROCESSOR_ARCHITECTURE"]
35
+ ruby_arch = ["foo"].pack("p").size == 4 ? 32 : 64
36
+ os_arch == "AMD64" && ruby_arch == 64
34
37
  end
35
38
 
36
39
  def is_32bit?
37
- os_arch = ENV['PROCESSOR_ARCHITEW6432'] || ENV['PROCESSOR_ARCHITECTURE']
38
- ruby_arch = ['foo'].pack('p').size == 4 ? 32 : 64
39
- os_arch != 'AMD64' && ruby_arch == 32
40
+ os_arch = ENV["PROCESSOR_ARCHITEW6432"] || ENV["PROCESSOR_ARCHITECTURE"]
41
+ ruby_arch = ["foo"].pack("p").size == 4 ? 32 : 64
42
+ os_arch != "AMD64" && ruby_arch == 32
40
43
  end
41
44
 
42
45
  def powershell_64_bit
@@ -48,7 +51,6 @@ module Kitchen
48
51
  end
49
52
 
50
53
  def wrap_command(script)
51
- base_script_path = File.join(File.dirname(__FILE__), '/../../../support/hyperv.ps1')
52
54
  debug("Loading functions from #{base_script_path}")
53
55
  new_script = [ ". #{base_script_path}", "#{script}" ].join(";\n")
54
56
  debug("Wrapped script: #{new_script}")
@@ -64,18 +66,23 @@ module Kitchen
64
66
  # @api private
65
67
  def run_ps(cmd, options = {})
66
68
  cmd = "echo #{cmd}" if config[:dry_run]
67
- debug('Preparing to run: ')
69
+ debug("Preparing to run: ")
68
70
  debug(" #{cmd}")
69
71
  wrapped_command = wrap_command cmd
70
72
  execute_command wrapped_command, options
71
73
  end
72
74
 
73
75
  def execute_command(cmd, options = {})
74
- debug("#Local Command BEGIN (#{cmd})")
75
- sh = Mixlib::ShellOut.new(cmd, options)
76
- sh.run_command
77
- debug("Local Command END #{Util.duration(sh.execution_time)}")
78
- raise "Failed: #{sh.stderr}" if sh.error?
76
+ debug("#Command BEGIN (#{cmd})")
77
+
78
+ sh = nil
79
+ bm = Benchmark.measure do
80
+ sh = connection.run_command(cmd, options)
81
+ end
82
+
83
+ debug("Command END #{Util.duration(bm.total)}")
84
+ raise "Failed: #{sh.stderr}" if sh.exit_status != 0
85
+
79
86
  stdout = sanitize_stdout(sh.stdout)
80
87
  JSON.parse(stdout) if stdout.length > 2
81
88
  end
@@ -112,12 +119,12 @@ module Kitchen
112
119
  Generation = #{config[:vm_generation]}
113
120
  DisableSecureBoot = "#{config[:disable_secureboot]}"
114
121
  MemoryStartupBytes = #{config[:memory_startup_bytes]}
115
- StaticMacAddress = "#{config[:static_mac_address]}"
122
+ StaticMacAddress = "#{config[:static_mac_address].to_s}"
116
123
  Name = "#{instance.name}"
117
124
  Path = "#{kitchen_vm_path}"
118
125
  VHDPath = "#{differencing_disk_path}"
119
126
  SwitchName = "#{config[:vm_switch]}"
120
- VlanId = #{config[:vm_vlan_id] || '$null'}
127
+ VlanId = #{config[:vm_vlan_id] || "$null"}
121
128
  ProcessorCount = #{config[:processor_count]}
122
129
  UseDynamicMemory = "#{config[:dynamic_memory]}"
123
130
  DynamicMemoryMinBytes = #{config[:dynamic_memory_min_bytes]}
@@ -132,11 +139,13 @@ module Kitchen
132
139
 
133
140
  def additional_disks
134
141
  return if config[:additional_disks].nil?
142
+
135
143
  <<-EOH
136
144
  AdditionalDisks = @("#{@additional_disk_objects.join('","')}")
137
145
  EOH
138
146
  end
139
147
 
148
+ # TODO: Report if VM has no IP address instead of silently waiting forever
140
149
  def vm_details_ps
141
150
  <<-DETAILS
142
151
 
@@ -159,7 +168,7 @@ module Kitchen
159
168
  while ((Get-VM -id "#{@state[:id]}").NetworkAdapters[0].Status -ne 'Ok'){
160
169
  start-sleep 10
161
170
  }
162
-
171
+
163
172
  (Get-VM -id "#{@state[:id]}").NetworkAdapters |
164
173
  Set-VMNetworkConfiguration -ipaddress "#{config[:ip_address]}" `
165
174
  -subnet "#{config[:subnet]}" `
@@ -171,7 +180,7 @@ module Kitchen
171
180
 
172
181
  def vm_default_switch_ps
173
182
  <<-VMSWITCH
174
- Get-DefaultVMSwitch #{config[:vm_switch]} | ConvertTo-Json
183
+ Get-DefaultVMSwitch "#{config[:vm_switch]}" | ConvertTo-Json
175
184
  VMSWITCH
176
185
  end
177
186
 
@@ -233,7 +242,8 @@ module Kitchen
233
242
 
234
243
  def ruby_array_to_ps_array(list)
235
244
  return "@()" if list.nil? || list.empty?
236
- list.to_s.tr('[]','()').prepend('@')
245
+
246
+ list.to_s.tr("[]", "()").prepend("@")
237
247
  end
238
248
  end
239
249
  end
data/support/hyperv.ps1 CHANGED
@@ -18,8 +18,8 @@ function New-DifferencingDisk {
18
18
  [parameter(Mandatory)]
19
19
  [ValidateNotNullOrEmpty()]
20
20
  [string[]]$Path,
21
- [parameter(Mandatory)]
22
- [ValidateNotNullOrEmpty()]
21
+ [parameter(Mandatory)]
22
+ [ValidateNotNullOrEmpty()]
23
23
  [string]$ParentPath
24
24
  )
25
25
  if (-not (Test-Path $Path)) {
@@ -73,9 +73,9 @@ function New-KitchenVM {
73
73
  $EnableGuestServices,
74
74
  $AdditionalDisks
75
75
  )
76
- $null = $psboundparameters.remove('DisableSecureBoot')
76
+ $null = $psboundparameters.remove('DisableSecureBoot')
77
77
  $null = $psboundparameters.remove('ProcessorCount')
78
- $null = $psboundparameters.remove('StaticMacAddress')
78
+ $null = $psboundparameters.remove('StaticMacAddress')
79
79
  $null = $psboundparameters.remove('UseDynamicMemory')
80
80
  $null = $psboundparameters.remove('DynamicMemoryMinBytes')
81
81
  $null = $psboundparameters.remove('DynamicMemoryMaxBytes')
@@ -99,7 +99,7 @@ function New-KitchenVM {
99
99
  if (-not [string]::IsNullOrEmpty($boot_iso_path)) {
100
100
  Mount-VMISO -Id $vm.Id -Path $boot_iso_path
101
101
  }
102
- if ($StaticMacAddress -ne $null) {
102
+ if (-not [string]::IsNullOrEmpty($StaticMacAddress)) {
103
103
  Set-VMNetworkAdapter -VMName $vm.VMName -StaticMacAddress $StaticMacAddress
104
104
  }
105
105
  if ($EnableGuestServices -and (Get-command Enable-VMIntegrationService -ErrorAction SilentlyContinue)) {
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-hyperv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Murawski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-20 00:00:00.000000000 Z
11
+ date: 2021-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rake
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -39,49 +25,27 @@ dependencies:
39
25
  - !ruby/object:Gem::Version
40
26
  version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
- name: pry
28
+ name: minitest
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
- - - ">="
31
+ - - "~>"
46
32
  - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: cane
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
33
+ version: '5.3'
34
+ - - "<"
60
35
  - !ruby/object:Gem::Version
61
- version: '0'
36
+ version: '5.15'
62
37
  type: :development
63
38
  prerelease: false
64
39
  version_requirements: !ruby/object:Gem::Requirement
65
40
  requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: finstyle
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
41
+ - - "~>"
74
42
  - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
43
+ version: '5.3'
44
+ - - "<"
81
45
  - !ruby/object:Gem::Version
82
- version: '0'
46
+ version: '5.15'
83
47
  - !ruby/object:Gem::Dependency
84
- name: rubocop
48
+ name: minitest-stub-const
85
49
  requirement: !ruby/object:Gem::Requirement
86
50
  requirements:
87
51
  - - ">="
@@ -95,81 +59,67 @@ dependencies:
95
59
  - !ruby/object:Gem::Version
96
60
  version: '0'
97
61
  - !ruby/object:Gem::Dependency
98
- name: yard
62
+ name: mocha
99
63
  requirement: !ruby/object:Gem::Requirement
100
64
  requirements:
101
- - - ">="
65
+ - - "~>"
102
66
  - !ruby/object:Gem::Version
103
- version: '0'
67
+ version: '1.1'
104
68
  type: :development
105
69
  prerelease: false
106
70
  version_requirements: !ruby/object:Gem::Requirement
107
71
  requirements:
108
- - - ">="
72
+ - - "~>"
109
73
  - !ruby/object:Gem::Version
110
- version: '0'
74
+ version: '1.1'
111
75
  - !ruby/object:Gem::Dependency
112
- name: countloc
76
+ name: test-kitchen
113
77
  requirement: !ruby/object:Gem::Requirement
114
78
  requirements:
115
79
  - - ">="
116
80
  - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: minitest-stub-const
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
81
+ version: '1.4'
82
+ - - "<"
130
83
  - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
84
+ version: '4'
85
+ type: :runtime
133
86
  prerelease: false
134
87
  version_requirements: !ruby/object:Gem::Requirement
135
88
  requirements:
136
89
  - - ">="
137
90
  - !ruby/object:Gem::Version
138
- version: '0'
91
+ version: '1.4'
92
+ - - "<"
93
+ - !ruby/object:Gem::Version
94
+ version: '4'
139
95
  - !ruby/object:Gem::Dependency
140
- name: mocha
96
+ name: train
141
97
  requirement: !ruby/object:Gem::Requirement
142
98
  requirements:
143
- - - ">="
99
+ - - "~>"
144
100
  - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :development
101
+ version: '3.5'
102
+ type: :runtime
147
103
  prerelease: false
148
104
  version_requirements: !ruby/object:Gem::Requirement
149
105
  requirements:
150
- - - ">="
106
+ - - "~>"
151
107
  - !ruby/object:Gem::Version
152
- version: '0'
108
+ version: '3.5'
153
109
  - !ruby/object:Gem::Dependency
154
- name: test-kitchen
110
+ name: train-winrm
155
111
  requirement: !ruby/object:Gem::Requirement
156
112
  requirements:
157
- - - ">="
113
+ - - "~>"
158
114
  - !ruby/object:Gem::Version
159
- version: '1.4'
160
- - - "<"
161
- - !ruby/object:Gem::Version
162
- version: '3'
115
+ version: '0.2'
163
116
  type: :runtime
164
117
  prerelease: false
165
118
  version_requirements: !ruby/object:Gem::Requirement
166
119
  requirements:
167
- - - ">="
168
- - !ruby/object:Gem::Version
169
- version: '1.4'
170
- - - "<"
120
+ - - "~>"
171
121
  - !ruby/object:Gem::Version
172
- version: '3'
122
+ version: '0.2'
173
123
  description: Hyper-V Driver for Test-Kitchen
174
124
  email:
175
125
  - steven.murawski@gmail.com
@@ -177,7 +127,10 @@ executables: []
177
127
  extensions: []
178
128
  extra_rdoc_files: []
179
129
  files:
130
+ - Gemfile
180
131
  - LICENSE
132
+ - Rakefile
133
+ - kitchen-hyperv.gemspec
181
134
  - lib/kitchen/driver/hyperv.rb
182
135
  - lib/kitchen/driver/hyperv_version.rb
183
136
  - lib/kitchen/driver/powershell.rb
@@ -201,8 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
154
  - !ruby/object:Gem::Version
202
155
  version: '0'
203
156
  requirements: []
204
- rubyforge_project:
205
- rubygems_version: 2.7.6
157
+ rubygems_version: 3.0.3
206
158
  signing_key:
207
159
  specification_version: 4
208
160
  summary: Hyper-V Driver for Test-Kitchen