Skip to content

Commit 0a8ddcd

Browse files
author
Ryan Johnson
authored
feat: upload max retries (#379)
- Updates the `vsphere` post-processor to support `max_retries` for the upload to a vSphere endpoint. Defaults to 5. - Sets and uses constants for `DefaultMaxRetries`, `DefaultDiskMode`, and `OvftoolWindows`. Ref: #30 Signed-off-by: Ryan Johnson <ryan@tenthirtyam.org>
1 parent 310be57 commit 0a8ddcd

4 files changed

Lines changed: 45 additions & 16 deletions

File tree

.web-docs/components/post-processor/vsphere/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ Optional:
7474
See [VMware KB 1003746](https://kb.vmware.com/s/article/1003746) for more information on the
7575
virtual hardware versions supported.
7676

77+
- `max_retries` (int) - Specifies the maximum number of times to retry the upload operation if it fails.
78+
Defaults to `5`.
79+
7780
<!-- End of code generated from the comments of the Config struct in post-processor/vsphere/post-processor.go; -->
7881

7982

docs-partials/post-processor/vsphere/Config-not-required.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,7 @@
3939
See [VMware KB 1003746](https://kb.vmware.com/s/article/1003746) for more information on the
4040
virtual hardware versions supported.
4141

42+
- `max_retries` (int) - Specifies the maximum number of times to retry the upload operation if it fails.
43+
Defaults to `5`.
44+
4245
<!-- End of code generated from the comments of the Config struct in post-processor/vsphere/post-processor.go; -->

post-processor/vsphere/post-processor.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ import (
2121
"github.com/hashicorp/hcl/v2/hcldec"
2222
"github.com/hashicorp/packer-plugin-sdk/common"
2323
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
24+
"github.com/hashicorp/packer-plugin-sdk/retry"
2425
shelllocal "github.com/hashicorp/packer-plugin-sdk/shell-local"
2526
"github.com/hashicorp/packer-plugin-sdk/template/config"
2627
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
2728
)
2829

30+
const DefaultMaxRetries = 5
31+
const DefaultDiskMode = "thick"
32+
const OvftoolWindows = "ovftool.exe"
33+
2934
var ovftool string = "ovftool"
3035

3136
var (
@@ -94,6 +99,9 @@ type Config struct {
9499
// See [VMware KB 1003746](https://kb.vmware.com/s/article/1003746) for more information on the
95100
// virtual hardware versions supported.
96101
HardwareVersion string `mapstructure:"hardware_version"`
102+
// Specifies the maximum number of times to retry the upload operation if it fails.
103+
// Defaults to `5`.
104+
MaxRetries int `mapstructure:"max_retries"`
97105

98106
ctx interpolate.Context
99107
}
@@ -117,16 +125,21 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
117125
return err
118126
}
119127

128+
// Set default value for MaxRetries if not provided.
129+
if p.config.MaxRetries == 0 {
130+
p.config.MaxRetries = DefaultMaxRetries // Set default value
131+
}
132+
120133
// Defaults
121134
if p.config.DiskMode == "" {
122-
p.config.DiskMode = "thick"
135+
p.config.DiskMode = DefaultDiskMode
123136
}
124137

125138
// Accumulate any errors
126139
errs := new(packersdk.MultiError)
127140

128141
if runtime.GOOS == "windows" {
129-
ovftool = "ovftool.exe"
142+
ovftool = OvftoolWindows
130143
}
131144

132145
if _, err := exec.LookPath(ovftool); err != nil {
@@ -171,7 +184,7 @@ func (p *PostProcessor) generateURI() (*url.URL, error) {
171184

172185
u, err := url.Parse(ovftool_uri)
173186
if err != nil {
174-
return nil, fmt.Errorf("Couldn't generate uri for ovftool: %s", err)
187+
return nil, fmt.Errorf("error generating uri for ovftool: %s", err)
175188
}
176189
u.User = url.UserPassword(p.config.Username, p.config.Password)
177190

@@ -207,7 +220,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa
207220
}
208221

209222
if source == "" {
210-
return nil, false, false, fmt.Errorf("VMX, OVF or OVA file not found")
223+
return nil, false, false, fmt.Errorf("error locating expected .vmx, .ovf, or .ova artifact")
211224
}
212225

213226
ovftool_uri, err := p.generateURI()
@@ -224,31 +237,39 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa
224237
ui.Message(fmt.Sprintf("Failed: %s\n", err))
225238
}
226239

227-
ui.Message(fmt.Sprintf("Uploading %s to vSphere", source))
240+
ui.Message(fmt.Sprintf("Uploading %s to vSphere...", source))
228241

229242
log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " "))
230243

231-
ui.Message("Validating Username and Password with dry-run")
244+
ui.Message("Validating username and password with dry-run...")
232245
err = p.ValidateOvfTool(args, ovftool)
233246
if err != nil {
234247
return nil, false, false, err
235248
}
236249

237250
// Validation has passed, so run for real.
238-
ui.Message("Calling OVFtool to upload vm")
251+
ui.Message("Uploading virtual machine using OVFtool...")
239252
commandAndArgs := []string{ovftool}
240253
commandAndArgs = append(commandAndArgs, args...)
241254
comm := &shelllocal.Communicator{
242255
ExecuteCommand: commandAndArgs,
243256
}
244257
flattenedCmd := strings.Join(commandAndArgs, " ")
245-
cmd := &packersdk.RemoteCmd{Command: flattenedCmd}
246-
log.Printf("[INFO] (vsphere): starting ovftool command: %s", flattenedCmd)
247-
err = cmd.RunWithUi(ctx, comm, ui)
248-
if err != nil || cmd.ExitStatus() != 0 {
249-
return nil, false, false, fmt.Errorf(
250-
"Error uploading virtual machine: Please see output above for more information.")
251-
}
258+
err = retry.Config{
259+
Tries: p.config.MaxRetries,
260+
ShouldRetry: func(err error) bool {
261+
return err != nil
262+
},
263+
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
264+
}.Run(ctx, func(ctx context.Context) error {
265+
cmd := &packersdk.RemoteCmd{Command: flattenedCmd}
266+
log.Printf("Starting OVFtool command: %s", flattenedCmd)
267+
err = cmd.RunWithUi(ctx, comm, ui)
268+
if err != nil || cmd.ExitStatus() != 0 {
269+
return fmt.Errorf("error uploading virtual machine")
270+
}
271+
return nil
272+
})
252273

253274
artifact = NewArtifact(p.config.Datastore, p.config.VMFolder, p.config.VMName, artifact.Files())
254275

@@ -275,8 +296,8 @@ func (p *PostProcessor) ValidateOvfTool(args []string, ofvtool string) error {
275296
if err := cmd.Run(); err != nil {
276297
outString := out.String()
277298
if strings.Contains(outString, "Enter login information for") {
278-
err = fmt.Errorf("Error performing OVFtool dry run; the username " +
279-
"or password you provided to ovftool is likely invalid.")
299+
err = fmt.Errorf("error performing ovftool dry run; the username " +
300+
"or password you provided may be incorrect")
280301
return err
281302
}
282303
return nil

post-processor/vsphere/post-processor.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.

0 commit comments

Comments
 (0)