kitchen-hyperv 0.5.3 → 0.7.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.
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