knife-vcenter 5.0.5 → 5.1.1

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: ff2523f169506ffe2b9099c744fca7d6cb17f78b1491e9d358144e1d241428c8
4
- data.tar.gz: 2aba720664dca48806e9e2abe92def20aac39ed8b4a09cb8be8f3bbffbb20f9b
3
+ metadata.gz: 5deb02621e85d8a3bbd047ac7a93b51c7e39c3c5320738c7f33fb939e648b200
4
+ data.tar.gz: e2fb1fdb8a4cbcb68211ced8e143ccb671b08bfdb9214c13439d60cc82391d77
5
5
  SHA512:
6
- metadata.gz: 3182c42e1cf563cc4141087657deaf5abac56f3065dfcc92ede22e5e3e263d29cc6583a4d82e8407bbdbdf285aec024b57c3c377ebcab0a73bb9104ae6422ea4
7
- data.tar.gz: 72750e64d6e5de6adab941cd8865f18ddff8ae4b88e269ab2f6a27aa91b5f0bbe8e0aae1551461e8c36ff6d29bb7cbd4335ad42d90013d63f2d2e3e139be2bbb
6
+ metadata.gz: e4aa67a6997a6a79c8fd46d02a4aafcc9fd375cbb0778e37f4c8b7d9919326196346c6020e6979008596841737fdd470d1e7729dcf1493cfafb42ce7f10cfbba
7
+ data.tar.gz: 5a9f4a0a63469a836196374ec2fca3bcb1287c17e4bd0751ddf4446147bb3214c278b20b9aaa484d3ec6d5e2e36b7ee623f95fae53b42b36393cd5ce747e810c
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require "chef/knife"
21
+ require "chef/knife/cloud/chefbootstrap/ssh_bootstrap_protocol"
22
+
23
+ class Chef
24
+ # The main knife class
25
+ class Knife
26
+ # The main cloud class from knife-cloud
27
+ class Cloud
28
+ # Patches SshBootstrapProtocol: upstream's wait_for_server_ready never
29
+ # falls back to port 22 when --connection-port/--ssh-port aren't set,
30
+ # so it probes TCPSocket.new(host, nil) and hangs forever. This adds
31
+ # the missing default, treats EADDRNOTAVAIL as a transient/retryable
32
+ # error, and adds backoff plus periodic status output while waiting.
33
+ class SshBootstrapProtocol
34
+ # Maximum backoff between probe attempts, in seconds.
35
+ SSH_PROBE_MAX_BACKOFF = 20
36
+ # Minimum interval between status updates surfaced to the user, in seconds.
37
+ SSH_PROBE_STATUS_INTERVAL = 15
38
+ # Standard SSH port used when neither --connection-port nor --ssh-port is given.
39
+ DEFAULT_SSH_PORT = 22
40
+
41
+ # Waits until sshd is reachable on the bootstrap IP before returning.
42
+ # Falls back to the standard SSH port 22 when neither
43
+ # --connection-port nor --ssh-port is configured (see class comment).
44
+ #
45
+ # @return [void]
46
+ def wait_for_server_ready
47
+ print "\n#{ui.color("Waiting for sshd to host (#{config[:bootstrap_ip_address]})", :magenta)}"
48
+
49
+ ssh_gateway = get_ssh_gateway_for(config[:bootstrap_ip_address])
50
+ ssh_port = config[:connection_port] || config[:ssh_port] || DEFAULT_SSH_PORT
51
+
52
+ if ssh_gateway
53
+ print(".") until tunnel_test_ssh(ssh_gateway, config[:bootstrap_ip_address]) do
54
+ @initial_sleep_delay = !!config[:subnet_id] ? 40 : 10
55
+ sleep @initial_sleep_delay
56
+ puts("done")
57
+ end
58
+ else
59
+ print(".") until tcp_test_ssh(config[:bootstrap_ip_address], ssh_port) do
60
+ @initial_sleep_delay = !!config[:subnet_id] ? 40 : 10
61
+ sleep @initial_sleep_delay
62
+ puts("done")
63
+ end
64
+ end
65
+ end
66
+
67
+ # Tests whether sshd is accepting connections on the given host/port
68
+ #
69
+ # @param [String] hostname The host to test the SSH connection against
70
+ # @param [Integer] ssh_port The port to test the SSH connection against
71
+ # @return [Boolean] true if sshd is accepting connections, false otherwise
72
+ def tcp_test_ssh(hostname, ssh_port)
73
+ tcp_socket = TCPSocket.new(hostname, ssh_port)
74
+ readable = IO.select([tcp_socket], nil, nil, 5)
75
+ if readable
76
+ ssh_banner = tcp_socket.gets
77
+ if ssh_banner.nil? || ssh_banner.empty?
78
+ record_ssh_probe_failure(hostname, "TCP connected but received no SSH banner yet")
79
+ false
80
+ else
81
+ Chef::Log.debug("ssh accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
82
+ yield
83
+ true
84
+ end
85
+ else
86
+ record_ssh_probe_failure(hostname, "TCP connect did not become readable within 5s")
87
+ false
88
+ end
89
+ rescue Errno::EPERM, Errno::ETIMEDOUT => e
90
+ Chef::Log.debug("ssh timed out: #{hostname}")
91
+ record_ssh_probe_failure(hostname, e)
92
+ false
93
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::EADDRNOTAVAIL, IOError => e
94
+ Chef::Log.debug("ssh failed to connect: #{hostname}")
95
+ record_ssh_probe_failure(hostname, e)
96
+ backoff_before_next_ssh_probe
97
+ false
98
+ rescue Errno::ECONNRESET => e
99
+ Chef::Log.debug("ssh reset its connection: #{hostname}")
100
+ record_ssh_probe_failure(hostname, e)
101
+ backoff_before_next_ssh_probe
102
+ false
103
+ ensure
104
+ tcp_socket && tcp_socket.close
105
+ end
106
+
107
+ private
108
+
109
+ # Records a failed probe attempt and surfaces a throttled status line.
110
+ #
111
+ # @param [String] hostname The host being probed
112
+ # @param [String, Exception] reason Why this attempt failed
113
+ # @return [void]
114
+ def record_ssh_probe_failure(hostname, reason)
115
+ @ssh_probe_started_at ||= Time.now
116
+ @ssh_probe_attempts = (@ssh_probe_attempts || 0) + 1
117
+
118
+ detail = reason.is_a?(Exception) ? "#{reason.class}: #{reason.message}" : reason
119
+ elapsed = Time.now - @ssh_probe_started_at
120
+ Chef::Log.debug(format("SSH readiness probe attempt #%d for %s failed after %.1fs total: %s", @ssh_probe_attempts, hostname, elapsed, detail))
121
+
122
+ return if @ssh_probe_last_status_at && (Time.now - @ssh_probe_last_status_at) < SSH_PROBE_STATUS_INTERVAL
123
+
124
+ @ssh_probe_last_status_at = Time.now
125
+ ui.warn(format("\nStill waiting for sshd on %s (attempt #%d, %ds elapsed) - last result: %s", hostname, @ssh_probe_attempts, elapsed.to_i, detail))
126
+ end
127
+
128
+ # Sleeps a capped exponential backoff (with jitter) before the next probe.
129
+ #
130
+ # @return [void]
131
+ def backoff_before_next_ssh_probe
132
+ attempt = @ssh_probe_attempts || 1
133
+ base_delay = [2 * (2**(attempt - 1)), SSH_PROBE_MAX_BACKOFF].min
134
+ sleep(base_delay + rand * 0.5)
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +25,13 @@ require_relative "../../../support/clone_vm"
25
25
  require "uri" unless defined?(URI)
26
26
  require "json" unless defined?(JSON)
27
27
  require "ostruct" unless defined?(OpenStruct)
28
- require "vsphere-automation-cis"
28
+ require "vsphere-automation-cis/api/session_api"
29
+ require "vsphere-automation-cis/models/cis_session_create_result"
30
+ require "vsphere-automation-cis/models/vapi_std_errors_unauthenticated"
31
+ require "vsphere-automation-cis/models/vapi_std_errors_unauthenticated_error"
32
+ require "vsphere-automation-cis/models/vapi_std_errors_service_unavailable"
33
+ require "vsphere-automation-cis/models/vapi_std_errors_service_unavailable_error"
34
+ require "vsphere-automation-cis/models/vapi_std_localizable_message"
29
35
  require "vsphere-automation-vcenter"
30
36
  require "set" unless defined?(Set)
31
37
 
@@ -41,35 +47,34 @@ class Chef
41
47
  attr_reader :api_client, :session_api, :session_id
42
48
  attr_reader :connection_options, :ipaddress
43
49
 
44
- def initialize(options = {})
45
- super(options)
50
+ def initialize(config: nil, host: nil, username: nil, password: nil, verify_ssl: true, logs: false)
51
+ super(config: config)
46
52
 
47
53
  configuration = VSphereAutomation::Configuration.new.tap do |c|
48
- c.host = options[:host]
49
- c.username = options[:username]
50
- c.password = options[:password]
54
+ c.host = host
55
+ c.username = username
56
+ c.password = password
51
57
  c.scheme = "https"
52
- c.verify_ssl = options[:verify_ssl]
53
- c.verify_ssl_host = options[:verify_ssl]
58
+ c.verify_ssl = verify_ssl
59
+ c.verify_ssl_host = verify_ssl
54
60
  end
55
61
 
56
- Base.log.warn("SSL Verification is turned OFF") if options[:logs] && !options[:verify_ssl]
62
+ Base.log.warn("SSL Verification is turned OFF") if logs && !verify_ssl
57
63
 
58
64
  @api_client = VSphereAutomation::ApiClient.new(configuration)
59
65
  api_client.default_headers["Authorization"] = configuration.basic_auth_token
60
66
 
61
- session_api = VSphereAutomation::CIS::SessionApi.new(api_client)
62
- session_id = session_api.create("").value
63
-
64
- api_client.default_headers["vmware-api-session-id"] = session_id
65
-
66
67
  # Set the class properties for the rbvmomi connections
67
68
  @connection_options = {
68
- user: options[:username],
69
- password: options[:password],
70
- insecure: options[:verify_ssl] ? false : true,
71
- host: options[:host],
69
+ user: username,
70
+ password: password,
71
+ insecure: verify_ssl ? false : true,
72
+ host: host,
72
73
  }
74
+
75
+ @session_id = create_api_session_id
76
+
77
+ api_client.default_headers["vmware-api-session-id"] = @session_id
73
78
  end
74
79
 
75
80
  # Creates the server
@@ -130,6 +135,11 @@ class Chef
130
135
  rescue StandardError => e
131
136
  puts e.message
132
137
  end
138
+
139
+ # vm_api.create only returns the new VM's ID as a raw String, not
140
+ # a summary object with .vm/.name/.power_state (unlike "clone"
141
+ # above), so fetch the full summary the same way.
142
+ get_server(options[:name])
133
143
  end
134
144
  end
135
145
 
@@ -137,6 +147,7 @@ class Chef
137
147
  #
138
148
  def list_servers
139
149
  vms = VSphereAutomation::VCenter::VMApi.new(api_client).list.value
150
+ vms = validate_list_response!(vms, "virtual machines")
140
151
 
141
152
  # list_resource_command uses .send(:name) syntax, so convert to OpenStruct to keep it compatible
142
153
  vms.map { |vmsummary| OpenStruct.new(vmsummary.to_hash) }
@@ -145,19 +156,22 @@ class Chef
145
156
  # Return a list of the hosts in the vCenter
146
157
  #
147
158
  def list_hosts
148
- VSphereAutomation::VCenter::HostApi.new(api_client).list.value
159
+ hosts = VSphereAutomation::VCenter::HostApi.new(api_client).list.value
160
+ validate_list_response!(hosts, "hosts")
149
161
  end
150
162
 
151
163
  # Return a list of the datacenters in the vCenter
152
164
  #
153
165
  def list_datacenters
154
- VSphereAutomation::VCenter::DatacenterApi.new(api_client).list.value
166
+ datacenters = VSphereAutomation::VCenter::DatacenterApi.new(api_client).list.value
167
+ validate_list_response!(datacenters, "datacenters")
155
168
  end
156
169
 
157
170
  # Return a list of the clusters in the vCenter
158
171
  #
159
172
  def list_clusters
160
- VSphereAutomation::VCenter::ClusterApi.new(api_client).list.value
173
+ clusters = VSphereAutomation::VCenter::ClusterApi.new(api_client).list.value
174
+ validate_list_response!(clusters, "clusters")
161
175
  end
162
176
 
163
177
  # Checks to see if the datacenter exists in the vCenter
@@ -300,6 +314,32 @@ class Chef
300
314
 
301
315
  private
302
316
 
317
+ def create_api_session_id
318
+ session_api = VSphereAutomation::CIS::SessionApi.new(api_client)
319
+ session_response = session_api.create("")
320
+
321
+ if session_response.respond_to?(:value) && session_response.value.is_a?(String) && !session_response.value.empty?
322
+ return session_response.value
323
+ end
324
+
325
+ response_type = session_response.class.name
326
+ if response_type.end_with?("VapiStdErrorsUnauthenticatedError") || response_type.end_with?("VapiStdErrorsUnauthenticated")
327
+ raise format("Authentication to vCenter failed for user %s on host %s", connection_options[:user], connection_options[:host])
328
+ end
329
+
330
+ raise format("Unable to establish a vCenter session on host %s: %s", connection_options[:host], response_type)
331
+ end
332
+
333
+ def validate_list_response!(resources, resource_type)
334
+ return resources if resources.is_a?(Array)
335
+
336
+ if resources.class.name.end_with?("VapiStdErrorsUnauthenticated")
337
+ raise format("Authentication to vCenter failed for user %s on host %s", connection_options[:user], connection_options[:host])
338
+ end
339
+
340
+ raise format("Unexpected vCenter response while listing %s: %s", resource_type, resources.class.name)
341
+ end
342
+
303
343
  def cleanup
304
344
  session_svc.delete unless session_id.nil?
305
345
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) Chef Software Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,9 +32,9 @@ class Chef
32
32
  #
33
33
  def create_service_instance
34
34
  Chef::Knife::Cloud::VcenterService.new(username: config[:vcenter_username],
35
- password: config[:vcenter_password],
36
- host: config[:vcenter_host],
37
- verify_ssl: verify_ssl?)
35
+ password: config[:vcenter_password],
36
+ host: config[:vcenter_host],
37
+ verify_ssl: verify_ssl?)
38
38
  end
39
39
 
40
40
  # Do we have valid SSL?
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ # knife-cloud's WinrmBootstrapProtocol does
21
+ # `require "winrm" unless defined?(WinRM::Connection)`. The upstream "winrm"
22
+ # gem is unmaintained and not a declared dependency of this plugin, so this
23
+ # raises a LoadError on every --connection-protocol winrm bootstrap. The
24
+ # actively maintained fork "chef-winrm" defines the same WinRM::Connection
25
+ # class, satisfying the guard, so we require it here first (falling back to
26
+ # "winrm" directly in case a future environment has the real gem installed).
27
+ begin
28
+ require "chef-winrm" unless defined?(WinRM::Connection)
29
+ rescue LoadError
30
+ require "winrm" unless defined?(WinRM::Connection)
31
+ end
32
+
33
+ require "chef/knife"
34
+ require "chef/knife/cloud/chefbootstrap/winrm_bootstrap_protocol"
35
+
36
+ class Chef
37
+ # The main knife class
38
+ class Knife
39
+ # The main cloud class from knife-cloud
40
+ class Cloud
41
+ # Patches WinrmBootstrapProtocol: upstream's wait_for_server_ready has
42
+ # no port default at all (unlike SSH, which falls back to --ssh-port),
43
+ # so it probes TCPSocket.new(host, nil) and hangs forever. This adds
44
+ # a default of 5985 (or 5986 with --winrm-ssl) and treats
45
+ # EADDRNOTAVAIL as a transient/retryable error.
46
+ class WinrmBootstrapProtocol
47
+ # Standard WinRM ports used when --connection-port isn't given.
48
+ DEFAULT_WINRM_PORT = 5985
49
+ DEFAULT_WINRM_SSL_PORT = 5986
50
+
51
+ def wait_for_server_ready
52
+ print "\n#{ui.color("Waiting for winrm to host (#{@config[:bootstrap_ip_address]})", :magenta)}"
53
+
54
+ winrm_port = @config[:connection_port] || (@config[:winrm_ssl] ? DEFAULT_WINRM_SSL_PORT : DEFAULT_WINRM_PORT)
55
+
56
+ print(".") until tcp_test_winrm(@config[:bootstrap_ip_address], winrm_port) do
57
+ sleep @initial_sleep_delay ||= 10
58
+ puts("done")
59
+ end
60
+ end
61
+
62
+ def tcp_test_winrm(hostname, port)
63
+ tcp_socket = TCPSocket.new(hostname, port)
64
+ true
65
+ rescue SocketError
66
+ sleep 2
67
+ false
68
+ rescue Errno::ETIMEDOUT
69
+ false
70
+ rescue Errno::EPERM
71
+ false
72
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::EADDRNOTAVAIL
73
+ sleep 2
74
+ false
75
+ ensure
76
+ tcp_socket && tcp_socket.close
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -2,7 +2,7 @@
2
2
 
3
3
  #
4
4
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
5
- # Copyright:: Copyright (c) Chef Software Inc.
5
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
6
6
  # License:: Apache License, Version 2.0
7
7
  #
8
8
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,6 +38,8 @@ class Chef
38
38
  # lazy load this file as it includes vmware deps that we only want at plugin runtime
39
39
  deps do
40
40
  require_relative "cloud/vcenter_service"
41
+ require_relative "cloud/ssh_bootstrap_protocol_patch"
42
+ require_relative "cloud/winrm_bootstrap_protocol_patch"
41
43
  include VcenterServiceHelpers
42
44
  end
43
45
 
@@ -116,6 +118,40 @@ class Chef
116
118
 
117
119
  ipaddress.nil? ? server.name : ipaddress
118
120
  end
121
+
122
+ # Retry bootstrap when local networking has a transient source-address failure.
123
+ # This keeps a successful clone from failing permanently on a temporary route/interface flap.
124
+ def after_exec_command
125
+ bootstrap_attempt = 0
126
+ max_bootstrap_retries = 3
127
+
128
+ begin
129
+ bootstrap
130
+ rescue Errno::EADDRNOTAVAIL => e
131
+ bootstrap_attempt += 1
132
+
133
+ if bootstrap_attempt <= max_bootstrap_retries
134
+ retry_wait = bootstrap_attempt * 5
135
+ ui.warn(format("Transient local networking error while bootstrapping (%s). Retrying in %d seconds (attempt %d/%d).", e.message, retry_wait, bootstrap_attempt, max_bootstrap_retries))
136
+ sleep retry_wait
137
+ retry
138
+ end
139
+
140
+ error_message = format("Bootstrap failed after %d retries due to local networking error: %s", max_bootstrap_retries, e.message)
141
+ ui.fatal(error_message)
142
+ cleanup_on_failure
143
+ raise e, error_message
144
+ rescue CloudExceptions::BootstrapError => e
145
+ ui.fatal(e.message)
146
+ cleanup_on_failure
147
+ raise e
148
+ rescue StandardError => e
149
+ error_message = format("Check if --connection-protocol and --image-os-type is correct. %s", e.message)
150
+ ui.fatal(error_message)
151
+ cleanup_on_failure
152
+ raise e, error_message
153
+ end
154
+ end
119
155
  end
120
156
  end
121
157
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) Chef Software Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) Chef Software Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,5 +20,5 @@
20
20
  # Provisions machines in vCenter
21
21
  module KnifeVcenter
22
22
  # The version of this amazing Gem, you should <3 it.
23
- VERSION = "5.0.5"
23
+ VERSION = "5.1.1"
24
24
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2017-2018 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,8 +45,8 @@ class Support
45
45
  relocate_spec.pool = options[:resource_pool]
46
46
 
47
47
  clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(location: relocate_spec,
48
- powerOn: options[:poweron],
49
- template: false)
48
+ powerOn: options[:poweron],
49
+ template: false)
50
50
 
51
51
  # Set the folder to use
52
52
  dest_folder = options[:folder].nil? ? src_vm.parent : options[:folder][:id]
@@ -57,19 +57,56 @@ class Support
57
57
  task.wait_for_completion
58
58
 
59
59
  # get the IP address of the machine for bootstrapping
60
- # machine name is based on the path, e.g. that includes the folder
61
- name = options[:folder].nil? ? options[:name] : format("%s/%s", options[:folder][:name], options[:name])
62
- new_vm = dc.find_vm(name)
60
+ new_vm = locate_new_vm(dc)
63
61
 
64
62
  if new_vm.nil?
65
- puts format("Unable to find machine: %s", name)
63
+ puts format("Unable to find machine: %s", options[:name])
66
64
  else
67
65
  puts "Waiting for network interfaces to become available..."
68
- sleep 2 while new_vm.guest.net.empty? || !new_vm.guest.ipAddress
69
- new_vm.guest.net[0].ipConfig.ipAddress.detect do |addr|
70
- addr.origin != "linklayer"
71
- end.ipAddress
66
+ bootstrap_ip_for_vm(new_vm)
72
67
  end
73
68
  end
69
+
70
+ private
71
+
72
+ def locate_new_vm(datacenter)
73
+ vm_name = options[:name]
74
+
75
+ if options[:folder]
76
+ folder_vm_name = format("%s/%s", options[:folder][:name], vm_name)
77
+ vm = datacenter.find_vm(folder_vm_name)
78
+ return vm unless vm.nil?
79
+ end
80
+
81
+ datacenter.find_vm(vm_name)
82
+ end
83
+
84
+ def bootstrap_ip_for_vm(vm, timeout: 300, interval: 2)
85
+ deadline = Time.now + timeout
86
+
87
+ loop do
88
+ ipaddress = extract_bootstrap_ip(vm)
89
+ return ipaddress unless ipaddress.nil?
90
+
91
+ return nil if Time.now >= deadline
92
+
93
+ sleep interval
94
+ end
95
+ end
96
+
97
+ def extract_bootstrap_ip(vm)
98
+ guest = vm.guest
99
+
100
+ addresses = guest.net.to_a.flat_map do |nic|
101
+ nic.ipConfig&.ipAddress.to_a.map(&:ipAddress)
102
+ end.compact
103
+
104
+ ipv4 = addresses.find { |addr| addr.include?(".") && !addr.start_with?("169.254.") }
105
+ return ipv4 unless ipv4.nil?
106
+
107
+ return nil if guest.ipAddress.nil? || !guest.ipAddress.include?(".")
108
+
109
+ guest.ipAddress.start_with?("169.254.") ? nil : guest.ipAddress
110
+ end
74
111
  end
75
112
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-vcenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.5
4
+ version: 5.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Partner Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-21 00:00:00.000000000 Z
11
+ date: 2026-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '15.11'
19
+ version: '18.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '15.11'
26
+ version: '18.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: knife-cloud
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -94,9 +94,11 @@ extensions: []
94
94
  extra_rdoc_files: []
95
95
  files:
96
96
  - LICENSE
97
+ - lib/chef/knife/cloud/ssh_bootstrap_protocol_patch.rb
97
98
  - lib/chef/knife/cloud/vcenter_service.rb
98
99
  - lib/chef/knife/cloud/vcenter_service_helpers.rb
99
100
  - lib/chef/knife/cloud/vcenter_service_options.rb
101
+ - lib/chef/knife/cloud/winrm_bootstrap_protocol_patch.rb
100
102
  - lib/chef/knife/vcenter_cluster_list.rb
101
103
  - lib/chef/knife/vcenter_datacenter_list.rb
102
104
  - lib/chef/knife/vcenter_host_list.rb
@@ -119,14 +121,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
119
121
  requirements:
120
122
  - - ">="
121
123
  - !ruby/object:Gem::Version
122
- version: '2.6'
124
+ version: '3.1'
123
125
  required_rubygems_version: !ruby/object:Gem::Requirement
124
126
  requirements:
125
127
  - - ">="
126
128
  - !ruby/object:Gem::Version
127
129
  version: '0'
128
130
  requirements: []
129
- rubygems_version: 3.0.3
131
+ rubygems_version: 3.3.27
130
132
  signing_key:
131
133
  specification_version: 4
132
134
  summary: Knife plugin to VMware vCenter.