Merge pull request #173 from jasonberanek/force-build
packer, builder/*: Add support for -force flag on builds [GH-119]
This commit is contained in:
commit
2e492fd747
|
@ -54,6 +54,7 @@ type config struct {
|
||||||
|
|
||||||
PackerBuildName string `mapstructure:"packer_build_name"`
|
PackerBuildName string `mapstructure:"packer_build_name"`
|
||||||
PackerDebug bool `mapstructure:"packer_debug"`
|
PackerDebug bool `mapstructure:"packer_debug"`
|
||||||
|
PackerForce bool `mapstructure:"packer_force"`
|
||||||
|
|
||||||
RawBootWait string `mapstructure:"boot_wait"`
|
RawBootWait string `mapstructure:"boot_wait"`
|
||||||
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
|
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 {
|
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)
|
b.config.BootWait, err = time.ParseDuration(b.config.RawBootWait)
|
||||||
|
|
|
@ -55,6 +55,7 @@ type config struct {
|
||||||
|
|
||||||
PackerBuildName string `mapstructure:"packer_build_name"`
|
PackerBuildName string `mapstructure:"packer_build_name"`
|
||||||
PackerDebug bool `mapstructure:"packer_debug"`
|
PackerDebug bool `mapstructure:"packer_debug"`
|
||||||
|
PackerForce bool `mapstructure:"packer_force"`
|
||||||
|
|
||||||
RawBootWait string `mapstructure:"boot_wait"`
|
RawBootWait string `mapstructure:"boot_wait"`
|
||||||
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
|
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 {
|
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 == "" {
|
if b.config.SSHUser == "" {
|
||||||
|
|
|
@ -21,12 +21,14 @@ func (Command) Help() string {
|
||||||
|
|
||||||
func (c Command) Run(env packer.Environment, args []string) int {
|
func (c Command) Run(env packer.Environment, args []string) int {
|
||||||
var cfgDebug bool
|
var cfgDebug bool
|
||||||
|
var cfgForce bool
|
||||||
var cfgExcept []string
|
var cfgExcept []string
|
||||||
var cfgOnly []string
|
var cfgOnly []string
|
||||||
|
|
||||||
cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError)
|
cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError)
|
||||||
cmdFlags.Usage = func() { env.Ui().Say(c.Help()) }
|
cmdFlags.Usage = func() { env.Ui().Say(c.Help()) }
|
||||||
cmdFlags.BoolVar(&cfgDebug, "debug", false, "debug mode for builds")
|
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)(&cfgExcept), "except", "build all builds except these")
|
||||||
cmdFlags.Var((*stringSliceValue)(&cfgOnly), "only", "only build the given builds by name")
|
cmdFlags.Var((*stringSliceValue)(&cfgOnly), "only", "only build the given builds by name")
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
|
@ -141,11 +143,13 @@ func (c Command) Run(env packer.Environment, args []string) int {
|
||||||
env.Ui().Say("")
|
env.Ui().Say("")
|
||||||
|
|
||||||
log.Printf("Build debug mode: %v", cfgDebug)
|
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 {
|
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)
|
||||||
err := b.Prepare()
|
err := b.Prepare()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.Ui().Error(err.Error())
|
env.Ui().Error(err.Error())
|
||||||
|
|
|
@ -9,6 +9,7 @@ Usage: packer build [options] TEMPLATE
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
-debug Debug mode enabled for builds
|
-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
|
-except=foo,bar,baz Build all builds other than these
|
||||||
-only=foo,bar,baz Only build the given builds by name
|
-only=foo,bar,baz Only build the given builds by name
|
||||||
`
|
`
|
||||||
|
|
|
@ -14,6 +14,10 @@ const BuildNameConfigKey = "packer_build_name"
|
||||||
// debugging is enabled.
|
// debugging is enabled.
|
||||||
const DebugConfigKey = "packer_debug"
|
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
|
// A Build represents a single job within Packer that is responsible for
|
||||||
// building some machine image artifact. Builds are meant to be parallelized.
|
// building some machine image artifact. Builds are meant to be parallelized.
|
||||||
type Build interface {
|
type Build interface {
|
||||||
|
@ -42,6 +46,12 @@ type Build interface {
|
||||||
// When SetDebug is set to true, parallelism between builds is
|
// When SetDebug is set to true, parallelism between builds is
|
||||||
// strictly prohibited.
|
// strictly prohibited.
|
||||||
SetDebug(bool)
|
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
|
// A build struct represents a single build job, the result of which should
|
||||||
|
@ -58,6 +68,7 @@ type coreBuild struct {
|
||||||
provisioners []coreBuildProvisioner
|
provisioners []coreBuildProvisioner
|
||||||
|
|
||||||
debug bool
|
debug bool
|
||||||
|
force bool
|
||||||
l sync.Mutex
|
l sync.Mutex
|
||||||
prepareCalled bool
|
prepareCalled bool
|
||||||
}
|
}
|
||||||
|
@ -98,6 +109,7 @@ func (b *coreBuild) Prepare() (err error) {
|
||||||
packerConfig := map[string]interface{}{
|
packerConfig := map[string]interface{}{
|
||||||
BuildNameConfigKey: b.name,
|
BuildNameConfigKey: b.name,
|
||||||
DebugConfigKey: b.debug,
|
DebugConfigKey: b.debug,
|
||||||
|
ForceConfigKey: b.force,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the builder
|
// Prepare the builder
|
||||||
|
@ -265,6 +277,14 @@ func (b *coreBuild) SetDebug(val bool) {
|
||||||
b.debug = val
|
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.
|
// Cancels the build if it is running.
|
||||||
func (b *coreBuild) Cancel() {
|
func (b *coreBuild) Cancel() {
|
||||||
b.builder.Cancel()
|
b.builder.Cancel()
|
||||||
|
|
|
@ -42,6 +42,7 @@ func TestBuild_Prepare(t *testing.T) {
|
||||||
packerConfig := map[string]interface{}{
|
packerConfig := map[string]interface{}{
|
||||||
BuildNameConfigKey: "test",
|
BuildNameConfigKey: "test",
|
||||||
DebugConfigKey: false,
|
DebugConfigKey: false,
|
||||||
|
ForceConfigKey: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
build := testBuild()
|
build := testBuild()
|
||||||
|
@ -88,6 +89,7 @@ func TestBuild_Prepare_Debug(t *testing.T) {
|
||||||
packerConfig := map[string]interface{}{
|
packerConfig := map[string]interface{}{
|
||||||
BuildNameConfigKey: "test",
|
BuildNameConfigKey: "test",
|
||||||
DebugConfigKey: true,
|
DebugConfigKey: true,
|
||||||
|
ForceConfigKey: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
build := testBuild()
|
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() {
|
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)
|
||||||
|
@ -111,6 +117,11 @@ func (b *BuildServer) SetDebug(val *bool, reply *interface{}) error {
|
||||||
return nil
|
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 {
|
func (b *BuildServer) Cancel(args *interface{}, reply *interface{}) error {
|
||||||
b.build.Cancel()
|
b.build.Cancel()
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -17,6 +17,7 @@ type testBuild struct {
|
||||||
runCache packer.Cache
|
runCache packer.Cache
|
||||||
runUi packer.Ui
|
runUi packer.Ui
|
||||||
setDebugCalled bool
|
setDebugCalled bool
|
||||||
|
setForceCalled bool
|
||||||
cancelCalled bool
|
cancelCalled bool
|
||||||
|
|
||||||
errRunResult bool
|
errRunResult bool
|
||||||
|
@ -48,6 +49,10 @@ func (b *testBuild) SetDebug(bool) {
|
||||||
b.setDebugCalled = true
|
b.setDebugCalled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBuild) SetForce(bool) {
|
||||||
|
b.setForceCalled = true
|
||||||
|
}
|
||||||
|
|
||||||
func (b *testBuild) Cancel() {
|
func (b *testBuild) Cancel() {
|
||||||
b.cancelCalled = true
|
b.cancelCalled = true
|
||||||
}
|
}
|
||||||
|
@ -104,6 +109,10 @@ func TestBuildRPC(t *testing.T) {
|
||||||
bClient.SetDebug(true)
|
bClient.SetDebug(true)
|
||||||
assert.True(b.setDebugCalled, "should be called")
|
assert.True(b.setDebugCalled, "should be called")
|
||||||
|
|
||||||
|
// Test SetForce
|
||||||
|
bClient.SetForce(true)
|
||||||
|
assert.True(b.setForceCalled, "should be called")
|
||||||
|
|
||||||
// Test Cancel
|
// Test Cancel
|
||||||
bClient.Cancel()
|
bClient.Cancel()
|
||||||
assert.True(b.cancelCalled, "cancel should be called")
|
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
|
between each step, waiting for keyboard input before continuing. This will allow
|
||||||
the user to inspect state and so on.
|
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
|
* `-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,
|
comma-separated names. Build names by default are the names of their builders,
|
||||||
unless a specific `name` attribute is specified within the configuration.
|
unless a specific `name` attribute is specified within the configuration.
|
||||||
|
|
Loading…
Reference in New Issue