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!
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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!
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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!
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
b.runner = b.createRunner(&steps, ui)
|
||||
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(b.stateBag)
|
||||
|
||||
// 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) {
|
||||
account, err := client.AccountsClient.GetProperties(resourceGroupName, storageAccountName)
|
||||
if err != nil {
|
||||
|
|
|
@ -73,15 +73,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
}
|
||||
|
||||
// Run the steps
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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)
|
||||
|
||||
// Run!
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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.
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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)
|
||||
|
||||
// Run!
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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!
|
||||
if b.config.PackerDebug {
|
||||
b.runner = &multistep.DebugRunner{
|
||||
Steps: steps,
|
||||
PauseFn: common.MultistepDebugFn(ui),
|
||||
}
|
||||
} else {
|
||||
b.runner = &multistep.BasicRunner{Steps: steps}
|
||||
}
|
||||
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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)
|
||||
|
||||
// Run
|
||||
if b.config.PackerDebug {
|
||||
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 = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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.
|
||||
if b.config.PackerDebug {
|
||||
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 = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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)
|
||||
|
||||
// Run
|
||||
if b.config.PackerDebug {
|
||||
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 = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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)
|
||||
|
||||
// Run
|
||||
if b.config.PackerDebug {
|
||||
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 = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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.
|
||||
if b.config.PackerDebug {
|
||||
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 = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(state)
|
||||
|
||||
// Report any errors.
|
||||
|
|
|
@ -305,17 +305,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
}
|
||||
|
||||
// Run!
|
||||
if b.config.PackerDebug {
|
||||
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 = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(state)
|
||||
|
||||
// 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.
|
||||
if b.config.PackerDebug {
|
||||
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 = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(state)
|
||||
|
||||
// Report any errors.
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mitchellh/packer/helper/enumflag"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/template"
|
||||
)
|
||||
|
@ -20,11 +21,14 @@ type BuildCommand struct {
|
|||
|
||||
func (c BuildCommand) Run(args []string) int {
|
||||
var cfgColor, cfgDebug, cfgForce, cfgParallel bool
|
||||
var cfgOnError string
|
||||
flags := c.Meta.FlagSet("build", FlagSetBuildFilter|FlagSetVars)
|
||||
flags.Usage = func() { c.Ui.Say(c.Help()) }
|
||||
flags.BoolVar(&cfgColor, "color", true, "")
|
||||
flags.BoolVar(&cfgDebug, "debug", false, "")
|
||||
flags.BoolVar(&cfgForce, "force", false, "")
|
||||
flagOnError := enumflag.New(&cfgOnError, "cleanup", "abort", "ask")
|
||||
flags.Var(flagOnError, "on-error", "")
|
||||
flags.BoolVar(&cfgParallel, "parallel", true, "")
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
|
@ -99,12 +103,14 @@ func (c BuildCommand) Run(args []string) int {
|
|||
|
||||
log.Printf("Build debug mode: %v", cfgDebug)
|
||||
log.Printf("Force build: %v", cfgForce)
|
||||
log.Printf("On error: %v", cfgOnError)
|
||||
|
||||
// Set the debug and force mode and prepare all the builds
|
||||
for _, b := range builds {
|
||||
log.Printf("Preparing build: %s", b.Name())
|
||||
b.SetDebug(cfgDebug)
|
||||
b.SetForce(cfgForce)
|
||||
b.SetOnError(cfgOnError)
|
||||
|
||||
warnings, err := b.Prepare()
|
||||
if err != nil {
|
||||
|
@ -284,7 +290,7 @@ Options:
|
|||
-except=foo,bar,baz Build all builds other than these
|
||||
-force Force a build to continue if artifacts exist, deletes existing artifacts
|
||||
-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)
|
||||
-var 'key=value' Variable for templates, can be used multiple times.
|
||||
-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"`
|
||||
PackerDebug bool `mapstructure:"packer_debug"`
|
||||
PackerForce bool `mapstructure:"packer_force"`
|
||||
PackerOnError string `mapstructure:"packer_on_error"`
|
||||
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.
|
||||
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 = "packer_template_path"
|
||||
|
||||
|
@ -67,6 +73,12 @@ type Build interface {
|
|||
// When SetForce is set to true, existing artifacts from the build are
|
||||
// deleted prior to the build.
|
||||
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
|
||||
|
@ -86,6 +98,7 @@ type coreBuild struct {
|
|||
|
||||
debug bool
|
||||
force bool
|
||||
onError string
|
||||
l sync.Mutex
|
||||
prepareCalled bool
|
||||
}
|
||||
|
@ -129,6 +142,7 @@ func (b *coreBuild) Prepare() (warn []string, err error) {
|
|||
BuilderTypeConfigKey: b.builderType,
|
||||
DebugConfigKey: b.debug,
|
||||
ForceConfigKey: b.force,
|
||||
OnErrorConfigKey: b.onError,
|
||||
TemplatePathKey: b.templatePath,
|
||||
UserVariablesConfigKey: b.variables,
|
||||
}
|
||||
|
@ -306,6 +320,14 @@ func (b *coreBuild) SetForce(val bool) {
|
|||
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.
|
||||
func (b *coreBuild) Cancel() {
|
||||
b.builder.Cancel()
|
||||
|
|
|
@ -23,6 +23,7 @@ func testBuild() *coreBuild {
|
|||
},
|
||||
},
|
||||
variables: make(map[string]string),
|
||||
onError: "cleanup",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +33,7 @@ func testDefaultPackerConfig() map[string]interface{} {
|
|||
BuilderTypeConfigKey: "foo",
|
||||
DebugConfigKey: false,
|
||||
ForceConfigKey: false,
|
||||
OnErrorConfigKey: "cleanup",
|
||||
TemplatePathKey: "",
|
||||
UserVariablesConfigKey: make(map[string]string),
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"net/rpc"
|
||||
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
// 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() {
|
||||
if err := b.client.Call("Build.Cancel", new(interface{}), new(interface{})); err != nil {
|
||||
panic(err)
|
||||
|
@ -134,6 +141,11 @@ func (b *BuildServer) SetForce(val *bool, reply *interface{}) error {
|
|||
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 {
|
||||
b.build.Cancel()
|
||||
return nil
|
||||
|
|
|
@ -2,9 +2,10 @@ package rpc
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
var testBuildArtifact = &packer.MockArtifact{}
|
||||
|
@ -18,6 +19,7 @@ type testBuild struct {
|
|||
runUi packer.Ui
|
||||
setDebugCalled bool
|
||||
setForceCalled bool
|
||||
setOnErrorCalled bool
|
||||
cancelCalled bool
|
||||
|
||||
errRunResult bool
|
||||
|
@ -53,6 +55,10 @@ func (b *testBuild) SetForce(bool) {
|
|||
b.setForceCalled = true
|
||||
}
|
||||
|
||||
func (b *testBuild) SetOnError(string) {
|
||||
b.setOnErrorCalled = true
|
||||
}
|
||||
|
||||
func (b *testBuild) Cancel() {
|
||||
b.cancelCalled = true
|
||||
}
|
||||
|
@ -116,6 +122,12 @@ func TestBuild(t *testing.T) {
|
|||
t.Fatal("should be called")
|
||||
}
|
||||
|
||||
// Test SetOnError
|
||||
bClient.SetOnError("ask")
|
||||
if !b.setOnErrorCalled {
|
||||
t.Fatal("should be called")
|
||||
}
|
||||
|
||||
// Test Cancel
|
||||
bClient.Cancel()
|
||||
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
|
||||
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
|
||||
comma-separated names. Build names by default are the names of their
|
||||
builders, unless a specific `name` attribute is specified within
|
||||
the configuration.
|
||||
|
||||
- `-parallel=false` - Disable parallelization of multiple builders (on by
|
||||
default).
|
||||
|
|
Loading…
Reference in New Issue