Commit 398b8fc8 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

common/command: add -var-file support for user vars

parent 7affef46
...@@ -12,6 +12,7 @@ func BuildOptionFlags(fs *flag.FlagSet, f *BuildOptions) { ...@@ -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.Except), "except", "build all builds except these")
fs.Var((*SliceValue)(&f.Only), "only", "only build the given builds by name") 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((*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 // userVarValue is a flag.Value that parses out user variables in
......
...@@ -17,6 +17,8 @@ func TestBuildOptionFlags(t *testing.T) { ...@@ -17,6 +17,8 @@ func TestBuildOptionFlags(t *testing.T) {
"-var=foo=bar", "-var=foo=bar",
"-var", "bar=baz", "-var", "bar=baz",
"-var=foo=bang", "-var=foo=bang",
"-var-file=foo",
"-var-file=bar",
} }
err := fs.Parse(args) err := fs.Parse(args)
...@@ -45,6 +47,11 @@ func TestBuildOptionFlags(t *testing.T) { ...@@ -45,6 +47,11 @@ func TestBuildOptionFlags(t *testing.T) {
if opts.UserVars["bar"] != "baz" { if opts.UserVars["bar"] != "baz" {
t.Fatalf("bad: %#v", opts.UserVars) 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) { func TestUserVarValue_implements(t *testing.T) {
......
...@@ -2,6 +2,23 @@ package command ...@@ -2,6 +2,23 @@ package command
import "strings" 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 // 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 // strings to be given on the command line and properly parsed into a slice
// of strings internally. // of strings internally.
......
...@@ -6,6 +6,32 @@ import ( ...@@ -6,6 +6,32 @@ import (
"testing" "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) { func TestSliceValue_implements(t *testing.T) {
var raw interface{} var raw interface{}
raw = new(SliceValue) raw = new(SliceValue)
......
package command package command
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"io/ioutil"
"log" "log"
"os"
) )
// BuildOptions is a set of options related to builds that can be set // BuildOptions is a set of options related to builds that can be set
// from the command line. // from the command line.
type BuildOptions struct { type BuildOptions struct {
UserVars map[string]string UserVarFiles []string
Except []string UserVars map[string]string
Only []string Except []string
Only []string
} }
// Validate validates the options // Validate validates the options
...@@ -21,6 +25,14 @@ func (f *BuildOptions) Validate() error { ...@@ -21,6 +25,14 @@ func (f *BuildOptions) Validate() error {
return errors.New("Only one of '-except' or '-only' may be specified.") 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 return nil
} }
...@@ -29,6 +41,18 @@ func (f *BuildOptions) Validate() error { ...@@ -29,6 +41,18 @@ func (f *BuildOptions) Validate() error {
func (f *BuildOptions) AllUserVars() (map[string]string, error) { func (f *BuildOptions) AllUserVars() (map[string]string, error) {
all := make(map[string]string) 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 // Copy in the command-line vars
for k, v := range f.UserVars { for k, v := range f.UserVars {
all[k] = v all[k] = v
...@@ -84,3 +108,18 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([ ...@@ -84,3 +108,18 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([
return builds, nil 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) { ...@@ -35,3 +35,19 @@ func TestBuildOptionsValidate(t *testing.T) {
t.Fatalf("err: %s", err) 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")
}
}
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