Skip to content

Commit 449b5f0

Browse files
committed
feat: add disable_ip_wait option to skip ip wait
Adds the `disable_ip_wait` option (vsphere-iso and vsphere-clone) to allow skipping the guest-reported IP wait when VMware Tools / open-vm-tools are unavailable. You must set `ssh_host` or `winrm_host`; reachability timing uses `ssh_timeout` or `winrm_timeout`, not `ip_wait_timeout`. Signed-off-by: Ryan Johnson <rya@tenthirtyam.org>
1 parent 465f00d commit 449b5f0

13 files changed

Lines changed: 180 additions & 12 deletions

File tree

.web-docs/components/builder/vsphere-clone/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1107,12 +1107,23 @@ wget http://{{ .HTTPIP }}:{{ .HTTPPort }}/foo/bar/preseed.cfg
11071107
documentation for full details.
11081108

11091109
- `ip_wait_address` (\*string) - Set this to a CIDR address to cause the service to wait for an address that is contained in
1110-
this network range. Defaults to `0.0.0.0/0` for any IPv4 address. Examples include:
1110+
this network range.
11111111

1112+
-> **Note:** This only filters which guest-reported IP is accepted; it does not disable IP wait. Use `disable_ip_wait` to skip
1113+
waiting for a guest-reported IP entirely.
1114+
1115+
Examples include:
11121116
* empty string ("") - remove all filters
11131117
* `0:0:0:0:0:0:0:0/0` - allow only ipv6 addresses
11141118
* `192.168.1.0/24` - only allow ipv4 addresses from 192.168.1.1 to 192.168.1.254
11151119

1120+
- `disable_ip_wait` (bool) - When true, skip waiting for a guest-reported IP from vCenter. The default wait relies on
1121+
VMware Tools or open-vm-tools guest information. Use when they cannot be installed during
1122+
guest operating system install. Defaults to `false`.
1123+
1124+
-> **Note:** You must set `ssh_host` or `winrm_host`; reachability timing uses `ssh_timeout`
1125+
or `winrm_timeout`, not `ip_wait_timeout`.
1126+
11161127
<!-- End of code generated from the comments of the WaitIpConfig struct in builder/vsphere/common/step_wait_for_ip.go; -->
11171128

11181129

.web-docs/components/builder/vsphere-iso/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1141,12 +1141,23 @@ JSON Example:
11411141
documentation for full details.
11421142

11431143
- `ip_wait_address` (\*string) - Set this to a CIDR address to cause the service to wait for an address that is contained in
1144-
this network range. Defaults to `0.0.0.0/0` for any IPv4 address. Examples include:
1144+
this network range.
11451145

1146+
-> **Note:** This only filters which guest-reported IP is accepted; it does not disable IP wait. Use `disable_ip_wait` to skip
1147+
waiting for a guest-reported IP entirely.
1148+
1149+
Examples include:
11461150
* empty string ("") - remove all filters
11471151
* `0:0:0:0:0:0:0:0/0` - allow only ipv6 addresses
11481152
* `192.168.1.0/24` - only allow ipv4 addresses from 192.168.1.1 to 192.168.1.254
11491153

1154+
- `disable_ip_wait` (bool) - When true, skip waiting for a guest-reported IP from vCenter. The default wait relies on
1155+
VMware Tools or open-vm-tools guest information. Use when they cannot be installed during
1156+
guest operating system install. Defaults to `false`.
1157+
1158+
-> **Note:** You must set `ssh_host` or `winrm_host`; reachability timing uses `ssh_timeout`
1159+
or `winrm_timeout`, not `ip_wait_timeout`.
1160+
11501161
<!-- End of code generated from the comments of the WaitIpConfig struct in builder/vsphere/common/step_wait_for_ip.go; -->
11511162

11521163

builder/vsphere/clone/builder.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,15 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
146146
Ctx: b.config.ctx,
147147
VMName: b.config.VMName,
148148
},
149-
&common.StepWaitForIp{
149+
)
150+
151+
if !b.config.DisableIpWait {
152+
steps = append(steps, &common.StepWaitForIp{
150153
Config: &b.config.WaitIpConfig,
151-
},
154+
})
155+
}
156+
157+
steps = append(steps,
152158
&communicator.StepConnect{
153159
Config: &b.config.Comm,
154160
Host: common.CommHost(b.config.Comm.Host()),

builder/vsphere/clone/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
9999
errs = packersdk.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
100100
errs = packersdk.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
101101

102+
disableWarnings, disableErrs := c.ValidateDisableIpWait(c.Comm.Host())
103+
warnings = append(warnings, disableWarnings...)
104+
errs = packersdk.MultiErrorAppend(errs, disableErrs...)
105+
102106
_, shutdownErrs := c.ShutdownConfig.Prepare(c.Comm)
103107
// shutdownWarnings, shutdownErrs := c.ShutdownConfig.Prepare(c.Comm)
104108
// warnings = append(warnings, shutdownWarnings...)

builder/vsphere/clone/config.hcl2spec.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

builder/vsphere/clone/config_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package clone
66

77
import (
8+
"strings"
89
"testing"
910
"time"
1011
)
@@ -37,6 +38,35 @@ func TestCloneConfig_Timeout(t *testing.T) {
3738
}
3839
}
3940

41+
func TestCloneConfig_DisableIpWaitRequiresHost(t *testing.T) {
42+
raw := minimalConfig()
43+
raw["disable_ip_wait"] = true
44+
c := new(Config)
45+
_, err := c.Prepare(raw)
46+
testConfigErr(t, "disable_ip_wait", nil, err)
47+
}
48+
49+
func TestCloneConfig_DisableIpWaitWithSSHHost(t *testing.T) {
50+
raw := minimalConfig()
51+
raw["disable_ip_wait"] = true
52+
raw["ssh_host"] = "192.168.1.10"
53+
c := new(Config)
54+
warns, err := c.Prepare(raw)
55+
if err != nil {
56+
t.Fatalf("unexpected error: %s", err)
57+
}
58+
found := false
59+
for _, w := range warns {
60+
if strings.Contains(w, "disable_ip_wait") {
61+
found = true
62+
break
63+
}
64+
}
65+
if !found {
66+
t.Fatalf("expected disable_ip_wait warning, got %#v", warns)
67+
}
68+
}
69+
4070
func TestCloneConfig_RAMReservation(t *testing.T) {
4171
raw := minimalConfig()
4272
raw["RAM_reservation"] = 1000

builder/vsphere/common/step_wait_for_ip.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,24 @@ type WaitIpConfig struct {
3333
// documentation for full details.
3434
SettleTimeout time.Duration `mapstructure:"ip_settle_timeout"`
3535
// Set this to a CIDR address to cause the service to wait for an address that is contained in
36-
// this network range. Defaults to `0.0.0.0/0` for any IPv4 address. Examples include:
36+
// this network range.
3737
//
38+
// -> **Note:** This only filters which guest-reported IP is accepted; it does not disable IP wait. Use `disable_ip_wait` to skip
39+
// waiting for a guest-reported IP entirely.
40+
//
41+
// Examples include:
3842
// * empty string ("") - remove all filters
3943
// * `0:0:0:0:0:0:0:0/0` - allow only ipv6 addresses
4044
// * `192.168.1.0/24` - only allow ipv4 addresses from 192.168.1.1 to 192.168.1.254
4145
WaitAddress *string `mapstructure:"ip_wait_address"`
4246
ipnet *net.IPNet
43-
44-
// WaitTimeout is a total timeout. If the virtual machine changes IP frequently, and does not settle down, wait
45-
// until the timeout expires.
47+
// When true, skip waiting for a guest-reported IP from vCenter. The default wait relies on
48+
// VMware Tools or open-vm-tools guest information. Use when they cannot be installed during
49+
// guest operating system install. Defaults to `false`.
50+
//
51+
// -> **Note:** You must set `ssh_host` or `winrm_host`; reachability timing uses `ssh_timeout`
52+
// or `winrm_timeout`, not `ip_wait_timeout`.
53+
DisableIpWait bool `mapstructure:"disable_ip_wait"`
4654
}
4755

4856
type StepWaitForIp struct {
@@ -74,6 +82,24 @@ func (c *WaitIpConfig) Prepare() []error {
7482
return errs
7583
}
7684

85+
// ValidateDisableIpWait checks disable_ip_wait requirements against the communicator host.
86+
func (c *WaitIpConfig) ValidateDisableIpWait(commHost string) ([]string, []error) {
87+
if !c.DisableIpWait {
88+
return nil, nil
89+
}
90+
91+
var warnings []string
92+
var errs []error
93+
94+
if commHost == "" {
95+
errs = append(errs, fmt.Errorf("disable_ip_wait is true but no ssh_host or winrm_host was set"))
96+
}
97+
98+
warnings = append(warnings, "disable_ip_wait is set; ip_wait_timeout, ip_settle_timeout, and ip_wait_address are ignored")
99+
100+
return warnings, errs
101+
}
102+
77103
func (c *WaitIpConfig) GetIPNet() *net.IPNet {
78104
return c.ipnet
79105
}

builder/vsphere/common/step_wait_for_ip.hcl2spec.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// © Broadcom. All Rights Reserved.
2+
// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
package common
6+
7+
import (
8+
"strings"
9+
"testing"
10+
"time"
11+
)
12+
13+
func TestWaitIpConfig_Prepare_defaults(t *testing.T) {
14+
c := &WaitIpConfig{}
15+
errs := c.Prepare()
16+
if len(errs) > 0 {
17+
t.Fatalf("unexpected errors: %v", errs)
18+
}
19+
if c.WaitTimeout != 30*time.Minute {
20+
t.Fatalf("expected default wait timeout 30m, got %v", c.WaitTimeout)
21+
}
22+
if c.WaitAddress == nil || *c.WaitAddress != "0.0.0.0/0" {
23+
t.Fatalf("expected default ip_wait_address 0.0.0.0/0, got %v", c.WaitAddress)
24+
}
25+
}
26+
27+
func TestWaitIpConfig_ValidateDisableIpWait(t *testing.T) {
28+
t.Run("disabled by default", func(t *testing.T) {
29+
c := &WaitIpConfig{}
30+
warnings, errs := c.ValidateDisableIpWait("")
31+
if len(warnings) > 0 || len(errs) > 0 {
32+
t.Fatalf("expected no warnings or errors, got warnings=%v errs=%v", warnings, errs)
33+
}
34+
})
35+
36+
t.Run("requires communicator host", func(t *testing.T) {
37+
c := &WaitIpConfig{DisableIpWait: true}
38+
warnings, errs := c.ValidateDisableIpWait("")
39+
if len(errs) != 1 {
40+
t.Fatalf("expected one error, got %v", errs)
41+
}
42+
if len(warnings) != 1 {
43+
t.Fatalf("expected one warning, got %v", warnings)
44+
}
45+
})
46+
47+
t.Run("ok with ssh host", func(t *testing.T) {
48+
c := &WaitIpConfig{DisableIpWait: true}
49+
warnings, errs := c.ValidateDisableIpWait("192.168.1.10")
50+
if len(errs) > 0 {
51+
t.Fatalf("unexpected errors: %v", errs)
52+
}
53+
if len(warnings) != 1 || !strings.Contains(warnings[0], "disable_ip_wait") {
54+
t.Fatalf("expected disable_ip_wait warning, got %v", warnings)
55+
}
56+
})
57+
}

builder/vsphere/iso/builder.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
158158
)
159159

160160
if b.config.Comm.Type != "none" {
161-
steps = append(steps,
162-
&common.StepWaitForIp{
161+
if !b.config.DisableIpWait {
162+
steps = append(steps, &common.StepWaitForIp{
163163
Config: &b.config.WaitIpConfig,
164-
},
164+
})
165+
}
166+
steps = append(steps,
165167
&communicator.StepConnect{
166168
Config: &b.config.Comm,
167169
Host: common.CommHost(b.config.Comm.Host()),

0 commit comments

Comments
 (0)