Commit 2e492fd7 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

Merge pull request #173 from jasonberanek/force-build

packer, builder/*: Add support for -force flag on builds [GH-119]
parents 60ee20f3 13c733a3
......@@ -54,6 +54,7 @@ type config struct {
PackerBuildName string `mapstructure:"packer_build_name"`
PackerDebug bool `mapstructure:"packer_debug"`
PackerForce bool `mapstructure:"packer_force"`
RawBootWait string `mapstructure:"boot_wait"`
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
......@@ -219,7 +220,12 @@ func (b *Builder) Prepare(raws ...interface{}) error {
}
if _, err := os.Stat(b.config.OutputDir); err == nil {
errs = append(errs, errors.New("Output directory already exists. It must not exist."))
if b.config.PackerForce {
log.Printf("Build forced, removing existing output directory: %s", string(b.config.OutputDir))
os.RemoveAll(b.config.OutputDir)
} else {
errs = append(errs, errors.New("Output directory already exists. It must not exist."))
}
}
b.config.BootWait, err = time.ParseDuration(b.config.RawBootWait)
......
......@@ -55,6 +55,7 @@ type config struct {
PackerBuildName string `mapstructure:"packer_build_name"`
PackerDebug bool `mapstructure:"packer_debug"`
PackerForce bool `mapstructure:"packer_force"`
RawBootWait string `mapstructure:"boot_wait"`
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
......@@ -175,7 +176,12 @@ func (b *Builder) Prepare(raws ...interface{}) error {
}
if _, err := os.Stat(b.config.OutputDir); err == nil {
errs = append(errs, errors.New("Output directory already exists. It must not exist."))
if b.config.PackerForce {
log.Printf("Build forced, removing existing output directory: %s", string(b.config.OutputDir))
os.RemoveAll(b.config.OutputDir)
} else {
errs = append(errs, errors.New("Output directory already exists. It must not exist."))
}
}
if b.config.SSHUser == "" {
......
......@@ -21,12 +21,14 @@ func (Command) Help() string {
func (c Command) Run(env packer.Environment, args []string) int {
var cfgDebug bool
var cfgForce bool
var cfgExcept []string
var cfgOnly []string
cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError)
cmdFlags.Usage = func() { env.Ui().Say(c.Help()) }
cmdFlags.BoolVar(&cfgDebug, "debug", false, "debug mode for builds")
cmdFlags.BoolVar(&cfgForce, "force", false, "force a build if artifacts exist")
cmdFlags.Var((*stringSliceValue)(&cfgExcept), "except", "build all builds except these")
cmdFlags.Var((*stringSliceValue)(&cfgOnly), "only", "only build the given builds by name")
if err := cmdFlags.Parse(args); err != nil {
......@@ -141,11 +143,13 @@ func (c Command) Run(env packer.Environment, args []string) int {
env.Ui().Say("")
log.Printf("Build debug mode: %v", cfgDebug)
log.Printf("Force build: %v", cfgForce)
// Set the debug mode and prepare all the builds
// 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)
err := b.Prepare()
if err != nil {
env.Ui().Error(err.Error())
......
......@@ -9,6 +9,7 @@ Usage: packer build [options] TEMPLATE
Options:
-debug Debug mode enabled for builds
-force Force a build to continue if artifacts exist, deletes existing artifacts
-except=foo,bar,baz Build all builds other than these
-only=foo,bar,baz Only build the given builds by name
`
......@@ -14,6 +14,10 @@ const BuildNameConfigKey = "packer_build_name"
// debugging is enabled.
const DebugConfigKey = "packer_debug"
// This is the key in configurations that is set to "true" when Packer
// force build is enabled.
const ForceConfigKey = "packer_force"
// A Build represents a single job within Packer that is responsible for
// building some machine image artifact. Builds are meant to be parallelized.
type Build interface {
......@@ -42,6 +46,12 @@ type Build interface {
// When SetDebug is set to true, parallelism between builds is
// strictly prohibited.
SetDebug(bool)
// SetForce will enable/disable forcing a build when artifacts exist.
//
// When SetForce is set to true, existing artifacts from the build are
// deleted prior to the build.
SetForce(bool)
}
// A build struct represents a single build job, the result of which should
......@@ -58,6 +68,7 @@ type coreBuild struct {
provisioners []coreBuildProvisioner
debug bool
force bool
l sync.Mutex
prepareCalled bool
}
......@@ -98,6 +109,7 @@ func (b *coreBuild) Prepare() (err error) {
packerConfig := map[string]interface{}{
BuildNameConfigKey: b.name,
DebugConfigKey: b.debug,
ForceConfigKey: b.force,
}
// Prepare the builder
......@@ -265,6 +277,14 @@ func (b *coreBuild) SetDebug(val bool) {
b.debug = val
}
func (b *coreBuild) SetForce(val bool) {
if b.prepareCalled {
panic("prepare has already been called")
}
b.force = val
}
// Cancels the build if it is running.
func (b *coreBuild) Cancel() {
b.builder.Cancel()
......
......@@ -42,6 +42,7 @@ func TestBuild_Prepare(t *testing.T) {
packerConfig := map[string]interface{}{
BuildNameConfigKey: "test",
DebugConfigKey: false,
ForceConfigKey: false,
}
build := testBuild()
......@@ -88,6 +89,7 @@ func TestBuild_Prepare_Debug(t *testing.T) {
packerConfig := map[string]interface{}{
BuildNameConfigKey: "test",
DebugConfigKey: true,
ForceConfigKey: false,
}
build := testBuild()
......
......@@ -69,6 +69,12 @@ func (b *build) SetDebug(val bool) {
}
}
func (b *build) SetForce(val bool) {
if err := b.client.Call("Build.SetForce", 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)
......@@ -111,6 +117,11 @@ func (b *BuildServer) SetDebug(val *bool, reply *interface{}) error {
return nil
}
func (b *BuildServer) SetForce(val *bool, reply *interface{}) error {
b.build.SetForce(*val)
return nil
}
func (b *BuildServer) Cancel(args *interface{}, reply *interface{}) error {
b.build.Cancel()
return nil
......
......@@ -17,6 +17,7 @@ type testBuild struct {
runCache packer.Cache
runUi packer.Ui
setDebugCalled bool
setForceCalled bool
cancelCalled bool
errRunResult bool
......@@ -48,6 +49,10 @@ func (b *testBuild) SetDebug(bool) {
b.setDebugCalled = true
}
func (b *testBuild) SetForce(bool) {
b.setForceCalled = true
}
func (b *testBuild) Cancel() {
b.cancelCalled = true
}
......@@ -104,6 +109,10 @@ func TestBuildRPC(t *testing.T) {
bClient.SetDebug(true)
assert.True(b.setDebugCalled, "should be called")
// Test SetForce
bClient.SetForce(true)
assert.True(b.setForceCalled, "should be called")
// Test Cancel
bClient.Cancel()
assert.True(b.cancelCalled, "cancel should be called")
......
......@@ -17,6 +17,12 @@ artifacts that are created will be outputted at the end of the build.
between each step, waiting for keyboard input before continuing. This will allow
the user to inspect state and so on.
* `-force` - Forces a builder to run when artifacts from a previous build prevent
a build from running. The exact behavior of a forced build is left to the builder.
In general, a builder supporting the forced build will remove the artifacts from
the previous build. This will allow the user to repeat a build without having to
manually clean these artifacts beforehand.
* `-except=foo,bar,baz` - Builds all the builds except those 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.
......
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