common/command: introduce this package for common cmmand stuff
This commit is contained in:
parent
180d5c8275
commit
a0033fba35
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
cmdcommon "github.com/mitchellh/packer/common/command"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
@ -22,15 +23,13 @@ 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 cfgForce bool
|
||||||
var cfgExcept []string
|
buildFilters := new(cmdcommon.BuildFilters)
|
||||||
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.BoolVar(&cfgForce, "force", false, "force a build if artifacts exist")
|
||||||
cmdFlags.Var((*stringSliceValue)(&cfgExcept), "except", "build all builds except these")
|
cmdcommon.BuildFilterFlags(cmdFlags, buildFilters)
|
||||||
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 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -41,8 +40,9 @@ func (c Command) Run(env packer.Environment, args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cfgOnly) > 0 && len(cfgExcept) > 0 {
|
if err := buildFilters.Validate(); err != nil {
|
||||||
env.Ui().Error("Only one of '-except' or '-only' may be specified.\n")
|
env.Ui().Error(err.Error())
|
||||||
|
env.Ui().Error("")
|
||||||
env.Ui().Error(c.Help())
|
env.Ui().Error(c.Help())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -72,49 +72,12 @@ func (c Command) Run(env packer.Environment, args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go through each builder and compile the builds that we care about
|
// Go through each builder and compile the builds that we care about
|
||||||
buildNames := tpl.BuildNames()
|
builds, err := buildFilters.Builds(tpl, components)
|
||||||
builds := make([]packer.Build, 0, len(buildNames))
|
|
||||||
for _, buildName := range buildNames {
|
|
||||||
if len(cfgExcept) > 0 {
|
|
||||||
found := false
|
|
||||||
for _, only := range cfgExcept {
|
|
||||||
if buildName == only {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found {
|
|
||||||
log.Printf("Skipping build '%s' because specified by -except.", buildName)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfgOnly) > 0 {
|
|
||||||
found := false
|
|
||||||
for _, only := range cfgOnly {
|
|
||||||
if buildName == only {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
log.Printf("Skipping build '%s' because not specified by -only.", buildName)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Creating build: %s", buildName)
|
|
||||||
build, err := tpl.Build(buildName, components)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.Ui().Error(fmt.Sprintf("Failed to create build '%s': \n\n%s", buildName, err))
|
env.Ui().Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
builds = append(builds, build)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfgDebug {
|
if cfgDebug {
|
||||||
env.Ui().Say("Debug mode enabled. Builds will not be parallelized.")
|
env.Ui().Say("Debug mode enabled. Builds will not be parallelized.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
package build
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
type stringSliceValue []string
|
|
||||||
|
|
||||||
func (s *stringSliceValue) String() string {
|
|
||||||
return strings.Join(*s, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stringSliceValue) Set(value string) error {
|
|
||||||
*s = strings.Split(value, ",")
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildFilterFlags sets the proper command line flags needed for
|
||||||
|
// build filters.
|
||||||
|
func BuildFilterFlags(fs *flag.FlagSet, f *BuildFilters) {
|
||||||
|
fs.Var((*SliceValue)(&f.Except), "except", "build all builds except these")
|
||||||
|
fs.Var((*SliceValue)(&f.Only), "only", "only build the given builds by name")
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// SliceValue implements the flag.Value interface and allows a list of
|
||||||
|
// strings to be given on the command line and properly parsed into a slice
|
||||||
|
// of strings internally.
|
||||||
|
type SliceValue []string
|
||||||
|
|
||||||
|
func (s *SliceValue) String() string {
|
||||||
|
return strings.Join(*s, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SliceValue) Set(value string) error {
|
||||||
|
*s = strings.Split(value, ",")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSliceValue_implements(t *testing.T) {
|
||||||
|
var raw interface{}
|
||||||
|
raw = new(SliceValue)
|
||||||
|
if _, ok := raw.(flag.Value); !ok {
|
||||||
|
t.Fatalf("SliceValue should be a Value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceValueSet(t *testing.T) {
|
||||||
|
sv := new(SliceValue)
|
||||||
|
err := sv.Set("foo,bar,baz")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []string{"foo", "bar", "baz"}
|
||||||
|
if !reflect.DeepEqual([]string(*sv), expected) {
|
||||||
|
t.Fatalf("Bad: %#v", sv)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildFilters is a set of options to filter the builds out of a template.
|
||||||
|
type BuildFilters struct {
|
||||||
|
Except []string
|
||||||
|
Only []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the filter settings
|
||||||
|
func (f *BuildFilters) Validate() error {
|
||||||
|
if len(f.Except) > 0 && len(f.Only) > 0 {
|
||||||
|
return errors.New("Only one of '-except' or '-only' may be specified.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds returns the builds out of the given template that pass the
|
||||||
|
// configured filters.
|
||||||
|
func (f *BuildFilters) Builds(t *packer.Template, cf *packer.ComponentFinder) ([]packer.Build, error) {
|
||||||
|
buildNames := t.BuildNames()
|
||||||
|
builds := make([]packer.Build, 0, len(buildNames))
|
||||||
|
for _, buildName := range buildNames {
|
||||||
|
if len(f.Except) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, except := range f.Except {
|
||||||
|
if buildName == except {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
log.Printf("Skipping build '%s' because specified by -except.", buildName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(f.Only) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, only := range f.Only {
|
||||||
|
if buildName == only {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
log.Printf("Skipping build '%s' because not specified by -only.", buildName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Creating build: %s", buildName)
|
||||||
|
build, err := t.Build(buildName, cf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to create build '%s': \n\n%s", buildName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
builds = append(builds, build)
|
||||||
|
}
|
||||||
|
|
||||||
|
return builds, nil
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildFiltersValidate(t *testing.T) {
|
||||||
|
bf := new(BuildFilters)
|
||||||
|
|
||||||
|
err := bf.Validate()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both set
|
||||||
|
bf.Except = make([]string, 1)
|
||||||
|
bf.Only = make([]string, 1)
|
||||||
|
err = bf.Validate()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// One set
|
||||||
|
bf.Except = make([]string, 1)
|
||||||
|
bf.Only = make([]string, 0)
|
||||||
|
err = bf.Validate()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bf.Except = make([]string, 0)
|
||||||
|
bf.Only = make([]string, 1)
|
||||||
|
err = bf.Validate()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue