common/command: add -var-file support for user vars
This commit is contained in:
parent
7affef4650
commit
398b8fc8d4
|
@ -12,6 +12,7 @@ func BuildOptionFlags(fs *flag.FlagSet, f *BuildOptions) {
|
|||
fs.Var((*SliceValue)(&f.Except), "except", "build all builds except these")
|
||||
fs.Var((*SliceValue)(&f.Only), "only", "only build the given builds by name")
|
||||
fs.Var((*userVarValue)(&f.UserVars), "var", "specify a user variable")
|
||||
fs.Var((*AppendSliceValue)(&f.UserVarFiles), "var-file", "file with user variables")
|
||||
}
|
||||
|
||||
// userVarValue is a flag.Value that parses out user variables in
|
||||
|
|
|
@ -17,6 +17,8 @@ func TestBuildOptionFlags(t *testing.T) {
|
|||
"-var=foo=bar",
|
||||
"-var", "bar=baz",
|
||||
"-var=foo=bang",
|
||||
"-var-file=foo",
|
||||
"-var-file=bar",
|
||||
}
|
||||
|
||||
err := fs.Parse(args)
|
||||
|
@ -45,6 +47,11 @@ func TestBuildOptionFlags(t *testing.T) {
|
|||
if opts.UserVars["bar"] != "baz" {
|
||||
t.Fatalf("bad: %#v", opts.UserVars)
|
||||
}
|
||||
|
||||
expected = []string{"foo", "bar"}
|
||||
if !reflect.DeepEqual(opts.UserVarFiles, expected) {
|
||||
t.Fatalf("bad: %#v", opts.UserVarFiles)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserVarValue_implements(t *testing.T) {
|
||||
|
|
|
@ -2,6 +2,23 @@ package command
|
|||
|
||||
import "strings"
|
||||
|
||||
// AppendSliceValue implements the flag.Value interface and allows multiple
|
||||
// calls to the same variable to append a list.
|
||||
type AppendSliceValue []string
|
||||
|
||||
func (s *AppendSliceValue) String() string {
|
||||
return strings.Join(*s, ",")
|
||||
}
|
||||
|
||||
func (s *AppendSliceValue) Set(value string) error {
|
||||
if *s == nil {
|
||||
*s = make([]string, 0, 1)
|
||||
}
|
||||
|
||||
*s = append(*s, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -6,6 +6,32 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestAppendSliceValue_implements(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = new(AppendSliceValue)
|
||||
if _, ok := raw.(flag.Value); !ok {
|
||||
t.Fatalf("AppendSliceValue should be a Value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendSliceValueSet(t *testing.T) {
|
||||
sv := new(AppendSliceValue)
|
||||
err := sv.Set("foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = sv.Set("bar")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
expected := []string{"foo", "bar"}
|
||||
if !reflect.DeepEqual([]string(*sv), expected) {
|
||||
t.Fatalf("Bad: %#v", sv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSliceValue_implements(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = new(SliceValue)
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// BuildOptions is a set of options related to builds that can be set
|
||||
// from the command line.
|
||||
type BuildOptions struct {
|
||||
UserVars map[string]string
|
||||
Except []string
|
||||
Only []string
|
||||
UserVarFiles []string
|
||||
UserVars map[string]string
|
||||
Except []string
|
||||
Only []string
|
||||
}
|
||||
|
||||
// Validate validates the options
|
||||
|
@ -21,6 +25,14 @@ func (f *BuildOptions) Validate() error {
|
|||
return errors.New("Only one of '-except' or '-only' may be specified.")
|
||||
}
|
||||
|
||||
if len(f.UserVarFiles) > 0 {
|
||||
for _, path := range f.UserVarFiles {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return fmt.Errorf("Cannot access: %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -29,6 +41,18 @@ func (f *BuildOptions) Validate() error {
|
|||
func (f *BuildOptions) AllUserVars() (map[string]string, error) {
|
||||
all := make(map[string]string)
|
||||
|
||||
// Copy in the variables from the files
|
||||
for _, path := range f.UserVarFiles {
|
||||
fileVars, err := readFileVars(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range fileVars {
|
||||
all[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Copy in the command-line vars
|
||||
for k, v := range f.UserVars {
|
||||
all[k] = v
|
||||
|
@ -84,3 +108,18 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([
|
|||
|
||||
return builds, nil
|
||||
}
|
||||
|
||||
func readFileVars(path string) (map[string]string, error) {
|
||||
bytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vars := make(map[string]string)
|
||||
err = json.Unmarshal(bytes, &vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vars, nil
|
||||
}
|
||||
|
|
|
@ -35,3 +35,19 @@ func TestBuildOptionsValidate(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildOptionsValidate_userVarFiles(t *testing.T) {
|
||||
bf := new(BuildOptions)
|
||||
|
||||
err := bf.Validate()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Non-existent file
|
||||
bf.UserVarFiles = []string{"ireallyshouldntexistanywhere"}
|
||||
err = bf.Validate()
|
||||
if err == nil {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue