Commit 717746ce authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

provisioner/shell: retry uploads if reboot [GH-282]

parent faf6eb1c
...@@ -5,6 +5,11 @@ IMPROVEMENTS: ...@@ -5,6 +5,11 @@ IMPROVEMENTS:
* post-processor/vagrant: the file being compressed will be shown * post-processor/vagrant: the file being compressed will be shown
in the UI [GH-314] in the UI [GH-314]
BUG FIXES:
* provisioner/shell: Retry shell script uploads, making reboots more
robust if they happen to fail in this stage. [GH-282]
## 0.3.3 (August 19, 2013) ## 0.3.3 (August 19, 2013)
FEATURES: FEATURES:
......
...@@ -237,7 +237,9 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { ...@@ -237,7 +237,9 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
defer f.Close() defer f.Close()
log.Printf("Uploading %s => %s", path, p.config.RemotePath) log.Printf("Uploading %s => %s", path, p.config.RemotePath)
err = comm.Upload(p.config.RemotePath, f) err = p.retryable(func() error {
return comm.Upload(p.config.RemotePath, f)
})
if err != nil { if err != nil {
return fmt.Errorf("Error uploading shell script: %s", err) return fmt.Errorf("Error uploading shell script: %s", err)
} }
...@@ -258,26 +260,13 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { ...@@ -258,26 +260,13 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
} }
cmd := &packer.RemoteCmd{Command: command} cmd := &packer.RemoteCmd{Command: command}
startTimeout := time.After(p.config.startRetryTimeout)
log.Printf("Executing command: %s", cmd.Command) log.Printf("Executing command: %s", cmd.Command)
for { err = p.retryable(func() error {
if err := cmd.StartWithUi(comm, ui); err == nil { return cmd.StartWithUi(comm, ui)
break })
}
// Create an error and log it if err != nil {
err = fmt.Errorf("Error executing command: %s", err) return err
log.Printf(err.Error())
// Check if we timed out, otherwise we retry. It is safe to
// retry since the only error case above is if the command
// failed to START.
select {
case <-startTimeout:
return err
default:
time.Sleep(2 * time.Second)
}
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus != 0 {
...@@ -287,3 +276,29 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { ...@@ -287,3 +276,29 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
return nil return nil
} }
// retryable will retry the given function over and over until a
// non-error is returned.
func (p *Provisioner) retryable(f func() error) error {
startTimeout := time.After(p.config.startRetryTimeout)
for {
var err error
if err = f(); err == nil {
return nil
}
// Create an error and log it
err = fmt.Errorf("Retryable error: %s", err)
log.Printf(err.Error())
// Check if we timed out, otherwise we retry. It is safe to
// retry since the only error case above is if the command
// failed to START.
select {
case <-startTimeout:
return err
default:
time.Sleep(2 * time.Second)
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment