Merge pull request #173 from jasonberanek/force-build

packer, builder/*: Add support for -force flag on builds [GH-119]
This commit is contained in:
Mitchell Hashimoto 2013-07-12 18:00:21 -07:00
commit 2e492fd747
9 changed files with 68 additions and 3 deletions

View File

@ -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)

View File

@ -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 == "" {

View File

@ -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())

View File

@ -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
`

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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")

View File

@ -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.