Merge pull request #3885 from orivej/on-error
Add -on-error command line argument to allow preserving artifacts on builder errors
This commit is contained in:
commit
13c9db5dd5
|
@ -262,15 +262,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run!
|
// Run!
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: common.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -179,15 +179,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run!
|
// Run!
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: common.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -262,15 +262,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run!
|
// Run!
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: common.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -157,7 +157,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
ui.Message(fmt.Sprintf("temp admin password: '%s'", b.config.Password))
|
ui.Message(fmt.Sprintf("temp admin password: '%s'", b.config.Password))
|
||||||
}
|
}
|
||||||
|
|
||||||
b.runner = b.createRunner(&steps, ui)
|
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner.Run(b.stateBag)
|
b.runner.Run(b.stateBag)
|
||||||
|
|
||||||
// Report any errors.
|
// Report any errors.
|
||||||
|
@ -198,19 +198,6 @@ func (b *Builder) Cancel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) createRunner(steps *[]multistep.Step, ui packer.Ui) multistep.Runner {
|
|
||||||
if b.config.PackerDebug {
|
|
||||||
return &multistep.DebugRunner{
|
|
||||||
Steps: *steps,
|
|
||||||
PauseFn: packerCommon.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &multistep.BasicRunner{
|
|
||||||
Steps: *steps,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) getBlobEndpoint(client *AzureClient, resourceGroupName string, storageAccountName string) (string, error) {
|
func (b *Builder) getBlobEndpoint(client *AzureClient, resourceGroupName string, storageAccountName string) (string, error) {
|
||||||
account, err := client.AccountsClient.GetProperties(resourceGroupName, storageAccountName)
|
account, err := client.AccountsClient.GetProperties(resourceGroupName, storageAccountName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -73,15 +73,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the steps
|
// Run the steps
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: common.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -78,15 +78,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("driver", driver)
|
state.Put("driver", driver)
|
||||||
|
|
||||||
// Run!
|
// Run!
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: common.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -73,14 +73,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the steps.
|
// Run the steps.
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: common.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// Report any errors.
|
// Report any errors.
|
||||||
|
|
|
@ -46,15 +46,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
// Run!
|
// Run!
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: common.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -116,15 +116,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run!
|
// Run!
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: common.MultistepDebugFn(ui),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -215,17 +215,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||||
pauseFn := common.MultistepDebugFn(ui)
|
|
||||||
state.Put("pauseFn", pauseFn)
|
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: pauseFn,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -108,16 +108,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the steps.
|
// Run the steps.
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||||
pauseFn := common.MultistepDebugFn(ui)
|
|
||||||
state.Put("pauseFn", pauseFn)
|
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: pauseFn,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// Report any errors.
|
// Report any errors.
|
||||||
|
|
|
@ -422,17 +422,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||||
pauseFn := common.MultistepDebugFn(ui)
|
|
||||||
state.Put("pauseFn", pauseFn)
|
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: pauseFn,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -275,17 +275,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||||
pauseFn := common.MultistepDebugFn(ui)
|
|
||||||
state.Put("pauseFn", pauseFn)
|
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: pauseFn,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -135,16 +135,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the steps.
|
// Run the steps.
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||||
pauseFn := common.MultistepDebugFn(ui)
|
|
||||||
state.Put("pauseFn", pauseFn)
|
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: pauseFn,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// Report any errors.
|
// Report any errors.
|
||||||
|
|
|
@ -305,17 +305,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run!
|
// Run!
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||||
pauseFn := common.MultistepDebugFn(ui)
|
|
||||||
state.Put("pauseFn", pauseFn)
|
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: pauseFn,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
// If there was an error, return that
|
||||||
|
|
|
@ -122,16 +122,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the steps.
|
// Run the steps.
|
||||||
if b.config.PackerDebug {
|
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||||
pauseFn := common.MultistepDebugFn(ui)
|
|
||||||
state.Put("pauseFn", pauseFn)
|
|
||||||
b.runner = &multistep.DebugRunner{
|
|
||||||
Steps: steps,
|
|
||||||
PauseFn: pauseFn,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
|
||||||
}
|
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// Report any errors.
|
// Report any errors.
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/mitchellh/packer/helper/enumflag"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"github.com/mitchellh/packer/template"
|
"github.com/mitchellh/packer/template"
|
||||||
)
|
)
|
||||||
|
@ -20,11 +21,14 @@ type BuildCommand struct {
|
||||||
|
|
||||||
func (c BuildCommand) Run(args []string) int {
|
func (c BuildCommand) Run(args []string) int {
|
||||||
var cfgColor, cfgDebug, cfgForce, cfgParallel bool
|
var cfgColor, cfgDebug, cfgForce, cfgParallel bool
|
||||||
|
var cfgOnError string
|
||||||
flags := c.Meta.FlagSet("build", FlagSetBuildFilter|FlagSetVars)
|
flags := c.Meta.FlagSet("build", FlagSetBuildFilter|FlagSetVars)
|
||||||
flags.Usage = func() { c.Ui.Say(c.Help()) }
|
flags.Usage = func() { c.Ui.Say(c.Help()) }
|
||||||
flags.BoolVar(&cfgColor, "color", true, "")
|
flags.BoolVar(&cfgColor, "color", true, "")
|
||||||
flags.BoolVar(&cfgDebug, "debug", false, "")
|
flags.BoolVar(&cfgDebug, "debug", false, "")
|
||||||
flags.BoolVar(&cfgForce, "force", false, "")
|
flags.BoolVar(&cfgForce, "force", false, "")
|
||||||
|
flagOnError := enumflag.New(&cfgOnError, "cleanup", "abort", "ask")
|
||||||
|
flags.Var(flagOnError, "on-error", "")
|
||||||
flags.BoolVar(&cfgParallel, "parallel", true, "")
|
flags.BoolVar(&cfgParallel, "parallel", true, "")
|
||||||
if err := flags.Parse(args); err != nil {
|
if err := flags.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
|
@ -99,12 +103,14 @@ func (c BuildCommand) Run(args []string) int {
|
||||||
|
|
||||||
log.Printf("Build debug mode: %v", cfgDebug)
|
log.Printf("Build debug mode: %v", cfgDebug)
|
||||||
log.Printf("Force build: %v", cfgForce)
|
log.Printf("Force build: %v", cfgForce)
|
||||||
|
log.Printf("On error: %v", cfgOnError)
|
||||||
|
|
||||||
// Set the debug and force mode and prepare all the builds
|
// Set the debug and force mode and prepare all the builds
|
||||||
for _, b := range builds {
|
for _, b := range builds {
|
||||||
log.Printf("Preparing build: %s", b.Name())
|
log.Printf("Preparing build: %s", b.Name())
|
||||||
b.SetDebug(cfgDebug)
|
b.SetDebug(cfgDebug)
|
||||||
b.SetForce(cfgForce)
|
b.SetForce(cfgForce)
|
||||||
|
b.SetOnError(cfgOnError)
|
||||||
|
|
||||||
warnings, err := b.Prepare()
|
warnings, err := b.Prepare()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -284,7 +290,7 @@ Options:
|
||||||
-except=foo,bar,baz Build all builds other than these
|
-except=foo,bar,baz Build all builds other than these
|
||||||
-force Force a build to continue if artifacts exist, deletes existing artifacts
|
-force Force a build to continue if artifacts exist, deletes existing artifacts
|
||||||
-machine-readable Machine-readable output
|
-machine-readable Machine-readable output
|
||||||
-only=foo,bar,baz Only build the given builds by name
|
-on-error=[cleanup|abort|ask] If the build fails do: clean up (default), abort, or ask
|
||||||
-parallel=false Disable parallelization (on by default)
|
-parallel=false Disable parallelization (on by default)
|
||||||
-var 'key=value' Variable for templates, can be used multiple times.
|
-var 'key=value' Variable for templates, can be used multiple times.
|
||||||
-var-file=path JSON file containing user variables.
|
-var-file=path JSON file containing user variables.
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newRunner(steps []multistep.Step, config PackerConfig, ui packer.Ui) (multistep.Runner, multistep.DebugPauseFn) {
|
||||||
|
switch config.PackerOnError {
|
||||||
|
case "", "cleanup":
|
||||||
|
case "abort":
|
||||||
|
for i, step := range steps {
|
||||||
|
steps[i] = abortStep{step, ui}
|
||||||
|
}
|
||||||
|
case "ask":
|
||||||
|
for i, step := range steps {
|
||||||
|
steps[i] = askStep{step, ui}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.PackerDebug {
|
||||||
|
pauseFn := MultistepDebugFn(ui)
|
||||||
|
return &multistep.DebugRunner{Steps: steps, PauseFn: pauseFn}, pauseFn
|
||||||
|
} else {
|
||||||
|
return &multistep.BasicRunner{Steps: steps}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRunner returns a multistep.Runner that runs steps augmented with support
|
||||||
|
// for -debug and -on-error command line arguments.
|
||||||
|
func NewRunner(steps []multistep.Step, config PackerConfig, ui packer.Ui) multistep.Runner {
|
||||||
|
runner, _ := newRunner(steps, config, ui)
|
||||||
|
return runner
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRunnerWithPauseFn returns a multistep.Runner that runs steps augmented
|
||||||
|
// with support for -debug and -on-error command line arguments. With -debug it
|
||||||
|
// puts the multistep.DebugPauseFn that will pause execution between steps into
|
||||||
|
// the state under the key "pauseFn".
|
||||||
|
func NewRunnerWithPauseFn(steps []multistep.Step, config PackerConfig, ui packer.Ui, state multistep.StateBag) multistep.Runner {
|
||||||
|
runner, pauseFn := newRunner(steps, config, ui)
|
||||||
|
if pauseFn != nil {
|
||||||
|
state.Put("pauseFn", pauseFn)
|
||||||
|
}
|
||||||
|
return runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeName(i interface{}) string {
|
||||||
|
return reflect.Indirect(reflect.ValueOf(i)).Type().Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
type abortStep struct {
|
||||||
|
step multistep.Step
|
||||||
|
ui packer.Ui
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s abortStep) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
return s.step.Run(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s abortStep) Cleanup(state multistep.StateBag) {
|
||||||
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||||
|
s.ui.Error("Interrupted, aborting...")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
||||||
|
s.ui.Error(fmt.Sprintf("Step %q failed, aborting...", typeName(s.step)))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
s.step.Cleanup(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
type askStep struct {
|
||||||
|
step multistep.Step
|
||||||
|
ui packer.Ui
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s askStep) Run(state multistep.StateBag) (action multistep.StepAction) {
|
||||||
|
for {
|
||||||
|
action = s.step.Run(state)
|
||||||
|
|
||||||
|
if action != multistep.ActionHalt {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ask(s.ui, typeName(s.step), state) {
|
||||||
|
case askCleanup:
|
||||||
|
return
|
||||||
|
case askAbort:
|
||||||
|
os.Exit(1)
|
||||||
|
case askRetry:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s askStep) Cleanup(state multistep.StateBag) {
|
||||||
|
s.step.Cleanup(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
type askResponse int
|
||||||
|
|
||||||
|
const (
|
||||||
|
askCleanup askResponse = iota
|
||||||
|
askAbort
|
||||||
|
askRetry
|
||||||
|
)
|
||||||
|
|
||||||
|
func ask(ui packer.Ui, name string, state multistep.StateBag) askResponse {
|
||||||
|
ui.Say(fmt.Sprintf("Step %q failed", name))
|
||||||
|
|
||||||
|
result := make(chan askResponse)
|
||||||
|
go func() {
|
||||||
|
result <- askPrompt(ui)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case response := <-result:
|
||||||
|
return response
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||||
|
return askCleanup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func askPrompt(ui packer.Ui) askResponse {
|
||||||
|
for {
|
||||||
|
line, err := ui.Ask("[c] Clean up and exit, [a] abort without cleanup, or [r] retry step (build may fail even if retry succeeds)?")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error asking for input: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
input := strings.ToLower(line) + "c"
|
||||||
|
switch input[0] {
|
||||||
|
case 'c':
|
||||||
|
return askCleanup
|
||||||
|
case 'a':
|
||||||
|
return askAbort
|
||||||
|
case 'r':
|
||||||
|
return askRetry
|
||||||
|
}
|
||||||
|
ui.Say(fmt.Sprintf("Incorrect input: %#v", line))
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,5 +8,6 @@ type PackerConfig struct {
|
||||||
PackerBuilderType string `mapstructure:"packer_builder_type"`
|
PackerBuilderType string `mapstructure:"packer_builder_type"`
|
||||||
PackerDebug bool `mapstructure:"packer_debug"`
|
PackerDebug bool `mapstructure:"packer_debug"`
|
||||||
PackerForce bool `mapstructure:"packer_force"`
|
PackerForce bool `mapstructure:"packer_force"`
|
||||||
|
PackerOnError string `mapstructure:"packer_on_error"`
|
||||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables"`
|
PackerUserVars map[string]string `mapstructure:"packer_user_variables"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package enumflag
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type enumFlag struct {
|
||||||
|
target *string
|
||||||
|
options []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a flag.Value implementation for parsing flags with a one-of-a-set value
|
||||||
|
func New(target *string, options ...string) *enumFlag {
|
||||||
|
return &enumFlag{target: target, options: options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *enumFlag) String() string {
|
||||||
|
return *f.target
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *enumFlag) Set(value string) error {
|
||||||
|
for _, v := range f.options {
|
||||||
|
if v == value {
|
||||||
|
*f.target = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("expected one of %q", f.options)
|
||||||
|
}
|
|
@ -24,6 +24,12 @@ const (
|
||||||
// force build is enabled.
|
// force build is enabled.
|
||||||
ForceConfigKey = "packer_force"
|
ForceConfigKey = "packer_force"
|
||||||
|
|
||||||
|
// This key determines what to do when a normal multistep step fails
|
||||||
|
// - "cleanup" - run cleanup steps
|
||||||
|
// - "abort" - exit without cleanup
|
||||||
|
// - "ask" - ask the user
|
||||||
|
OnErrorConfigKey = "packer_on_error"
|
||||||
|
|
||||||
// TemplatePathKey is the path to the template that configured this build
|
// TemplatePathKey is the path to the template that configured this build
|
||||||
TemplatePathKey = "packer_template_path"
|
TemplatePathKey = "packer_template_path"
|
||||||
|
|
||||||
|
@ -67,6 +73,12 @@ type Build interface {
|
||||||
// When SetForce is set to true, existing artifacts from the build are
|
// When SetForce is set to true, existing artifacts from the build are
|
||||||
// deleted prior to the build.
|
// deleted prior to the build.
|
||||||
SetForce(bool)
|
SetForce(bool)
|
||||||
|
|
||||||
|
// SetOnError will determine what to do when a normal multistep step fails
|
||||||
|
// - "cleanup" - run cleanup steps
|
||||||
|
// - "abort" - exit without cleanup
|
||||||
|
// - "ask" - ask the user
|
||||||
|
SetOnError(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A build struct represents a single build job, the result of which should
|
// A build struct represents a single build job, the result of which should
|
||||||
|
@ -86,6 +98,7 @@ type coreBuild struct {
|
||||||
|
|
||||||
debug bool
|
debug bool
|
||||||
force bool
|
force bool
|
||||||
|
onError string
|
||||||
l sync.Mutex
|
l sync.Mutex
|
||||||
prepareCalled bool
|
prepareCalled bool
|
||||||
}
|
}
|
||||||
|
@ -129,6 +142,7 @@ func (b *coreBuild) Prepare() (warn []string, err error) {
|
||||||
BuilderTypeConfigKey: b.builderType,
|
BuilderTypeConfigKey: b.builderType,
|
||||||
DebugConfigKey: b.debug,
|
DebugConfigKey: b.debug,
|
||||||
ForceConfigKey: b.force,
|
ForceConfigKey: b.force,
|
||||||
|
OnErrorConfigKey: b.onError,
|
||||||
TemplatePathKey: b.templatePath,
|
TemplatePathKey: b.templatePath,
|
||||||
UserVariablesConfigKey: b.variables,
|
UserVariablesConfigKey: b.variables,
|
||||||
}
|
}
|
||||||
|
@ -306,6 +320,14 @@ func (b *coreBuild) SetForce(val bool) {
|
||||||
b.force = val
|
b.force = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *coreBuild) SetOnError(val string) {
|
||||||
|
if b.prepareCalled {
|
||||||
|
panic("prepare has already been called")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.onError = val
|
||||||
|
}
|
||||||
|
|
||||||
// Cancels the build if it is running.
|
// Cancels the build if it is running.
|
||||||
func (b *coreBuild) Cancel() {
|
func (b *coreBuild) Cancel() {
|
||||||
b.builder.Cancel()
|
b.builder.Cancel()
|
||||||
|
|
|
@ -23,6 +23,7 @@ func testBuild() *coreBuild {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variables: make(map[string]string),
|
variables: make(map[string]string),
|
||||||
|
onError: "cleanup",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ func testDefaultPackerConfig() map[string]interface{} {
|
||||||
BuilderTypeConfigKey: "foo",
|
BuilderTypeConfigKey: "foo",
|
||||||
DebugConfigKey: false,
|
DebugConfigKey: false,
|
||||||
ForceConfigKey: false,
|
ForceConfigKey: false,
|
||||||
|
OnErrorConfigKey: "cleanup",
|
||||||
TemplatePathKey: "",
|
TemplatePathKey: "",
|
||||||
UserVariablesConfigKey: make(map[string]string),
|
UserVariablesConfigKey: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/packer/packer"
|
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
|
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An implementation of packer.Build where the build is actually executed
|
// An implementation of packer.Build where the build is actually executed
|
||||||
|
@ -79,6 +80,12 @@ func (b *build) SetForce(val bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *build) SetOnError(val string) {
|
||||||
|
if err := b.client.Call("Build.SetOnError", val, new(interface{})); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (b *build) Cancel() {
|
func (b *build) Cancel() {
|
||||||
if err := b.client.Call("Build.Cancel", new(interface{}), new(interface{})); err != nil {
|
if err := b.client.Call("Build.Cancel", new(interface{}), new(interface{})); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -134,6 +141,11 @@ func (b *BuildServer) SetForce(val *bool, reply *interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BuildServer) SetOnError(val *string, reply *interface{}) error {
|
||||||
|
b.build.SetOnError(*val)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BuildServer) Cancel(args *interface{}, reply *interface{}) error {
|
func (b *BuildServer) Cancel(args *interface{}, reply *interface{}) error {
|
||||||
b.build.Cancel()
|
b.build.Cancel()
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,9 +2,10 @@ package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/mitchellh/packer/packer"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testBuildArtifact = &packer.MockArtifact{}
|
var testBuildArtifact = &packer.MockArtifact{}
|
||||||
|
@ -18,6 +19,7 @@ type testBuild struct {
|
||||||
runUi packer.Ui
|
runUi packer.Ui
|
||||||
setDebugCalled bool
|
setDebugCalled bool
|
||||||
setForceCalled bool
|
setForceCalled bool
|
||||||
|
setOnErrorCalled bool
|
||||||
cancelCalled bool
|
cancelCalled bool
|
||||||
|
|
||||||
errRunResult bool
|
errRunResult bool
|
||||||
|
@ -53,6 +55,10 @@ func (b *testBuild) SetForce(bool) {
|
||||||
b.setForceCalled = true
|
b.setForceCalled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBuild) SetOnError(string) {
|
||||||
|
b.setOnErrorCalled = true
|
||||||
|
}
|
||||||
|
|
||||||
func (b *testBuild) Cancel() {
|
func (b *testBuild) Cancel() {
|
||||||
b.cancelCalled = true
|
b.cancelCalled = true
|
||||||
}
|
}
|
||||||
|
@ -116,6 +122,12 @@ func TestBuild(t *testing.T) {
|
||||||
t.Fatal("should be called")
|
t.Fatal("should be called")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test SetOnError
|
||||||
|
bClient.SetOnError("ask")
|
||||||
|
if !b.setOnErrorCalled {
|
||||||
|
t.Fatal("should be called")
|
||||||
|
}
|
||||||
|
|
||||||
// Test Cancel
|
// Test Cancel
|
||||||
bClient.Cancel()
|
bClient.Cancel()
|
||||||
if !b.cancelCalled {
|
if !b.cancelCalled {
|
||||||
|
|
|
@ -36,7 +36,17 @@ artifacts that are created will be outputted at the end of the build.
|
||||||
remove the artifacts from the previous build. This will allow the user to
|
remove the artifacts from the previous build. This will allow the user to
|
||||||
repeat a build without having to manually clean these artifacts beforehand.
|
repeat a build without having to manually clean these artifacts beforehand.
|
||||||
|
|
||||||
|
- `-on-error=cleanup` (default), `-on-error=abort`, `-on-error=ask` - Selects
|
||||||
|
what to do when the build fails. `cleanup` cleans up after the previous
|
||||||
|
steps, deleting temporary files and virtual machines. `abort` exits without
|
||||||
|
any cleanup, which might require the next build to use `-force`. `ask`
|
||||||
|
presents a prompt and waits for you to decide to clean up, abort, or retry
|
||||||
|
the failed step.
|
||||||
|
|
||||||
- `-only=foo,bar,baz` - Only build the builds with the given
|
- `-only=foo,bar,baz` - Only build the builds with the given
|
||||||
comma-separated names. Build names by default are the names of their
|
comma-separated names. Build names by default are the names of their
|
||||||
builders, unless a specific `name` attribute is specified within
|
builders, unless a specific `name` attribute is specified within
|
||||||
the configuration.
|
the configuration.
|
||||||
|
|
||||||
|
- `-parallel=false` - Disable parallelization of multiple builders (on by
|
||||||
|
default).
|
||||||
|
|
Loading…
Reference in New Issue