@@ -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+
2934var ovftool string = "ovftool"
3035
3136var (
@@ -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
0 commit comments