Commit dd0a7755 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

common/command: delete

parent d4b489a9
package command
import (
"flag"
"fmt"
"strings"
)
// BuildOptionFlags sets the proper command line flags needed for
// build options.
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
// the form of 'key=value' and sets it on this map.
type userVarValue map[string]string
func (v *userVarValue) String() string {
return ""
}
func (v *userVarValue) Set(raw string) error {
idx := strings.Index(raw, "=")
if idx == -1 {
return fmt.Errorf("No '=' value in arg: %s", raw)
}
if *v == nil {
*v = make(map[string]string)
}
key, value := raw[0:idx], raw[idx+1:]
(*v)[key] = value
return nil
}
package command
import (
"flag"
"reflect"
"testing"
)
func TestBuildOptionFlags(t *testing.T) {
opts := new(BuildOptions)
fs := flag.NewFlagSet("test", flag.ContinueOnError)
BuildOptionFlags(fs, opts)
args := []string{
"-except=foo,bar,baz",
"-only=a,b",
"-var=foo=bar",
"-var", "bar=baz",
"-var=foo=bang",
"-var-file=foo",
"-var-file=bar",
}
err := fs.Parse(args)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := []string{"foo", "bar", "baz"}
if !reflect.DeepEqual(opts.Except, expected) {
t.Fatalf("bad: %#v", opts.Except)
}
expected = []string{"a", "b"}
if !reflect.DeepEqual(opts.Only, expected) {
t.Fatalf("bad: %#v", opts.Only)
}
if len(opts.UserVars) != 2 {
t.Fatalf("bad: %#v", opts.UserVars)
}
if opts.UserVars["foo"] != "bang" {
t.Fatalf("bad: %#v", opts.UserVars)
}
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) {
var raw interface{}
raw = new(userVarValue)
if _, ok := raw.(flag.Value); !ok {
t.Fatalf("userVarValue should be a Value")
}
}
func TestUserVarValueSet(t *testing.T) {
sv := new(userVarValue)
err := sv.Set("key=value")
if err != nil {
t.Fatalf("err: %s", err)
}
vars := map[string]string(*sv)
if vars["key"] != "value" {
t.Fatalf("Bad: %#v", vars)
}
// Empty value
err = sv.Set("key=")
if err != nil {
t.Fatalf("err: %s", err)
}
vars = map[string]string(*sv)
if vars["key"] != "" {
t.Fatalf("Bad: %#v", vars)
}
// Equal in value
err = sv.Set("key=foo=bar")
if err != nil {
t.Fatalf("err: %s", err)
}
vars = map[string]string(*sv)
if vars["key"] != "foo=bar" {
t.Fatalf("Bad: %#v", vars)
}
// No equal
err = sv.Set("key")
if err == nil {
t.Fatal("should have error")
}
}
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.
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
}
package command
import (
"flag"
"reflect"
"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)
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)
}
}
package command
import (
"errors"
"fmt"
jsonutil "github.com/mitchellh/packer/common/json"
"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 {
UserVarFiles []string
UserVars map[string]string
Except []string
Only []string
}
// Validate validates the options
func (f *BuildOptions) Validate() error {
if len(f.Except) > 0 && len(f.Only) > 0 {
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
}
// AllUserVars returns the user variables, compiled from both the
// file paths and the vars on the command line.
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
}
return all, nil
}
// Builds returns the builds out of the given template that pass the
// configured options.
func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([]packer.Build, error) {
buildNames := t.BuildNames()
// Process the name
tpl, _, err := t.NewConfigTemplate()
if err != nil {
return nil, err
}
checks := make(map[string][]string)
checks["except"] = f.Except
checks["only"] = f.Only
for t, ns := range checks {
for _, n := range ns {
found := false
for _, actual := range buildNames {
var processed string
processed, err = tpl.Process(actual, nil)
if err != nil {
return nil, err
}
if actual == n || processed == n {
found = true
break
}
}
if !found {
return nil, fmt.Errorf(
"Unknown build in '%s' flag: %s", t, n)
}
}
}
builds := make([]packer.Build, 0, len(buildNames))
for _, buildName := range buildNames {
var processedBuildName string
processedBuildName, err = tpl.Process(buildName, nil)
if err != nil {
return nil, err
}
if len(f.Except) > 0 {
found := false
for _, except := range f.Except {
if buildName == except || processedBuildName == except {
found = true
break
}
}
if found {
log.Printf("Skipping build '%s' because specified by -except.", processedBuildName)
continue
}
}
if len(f.Only) > 0 {
found := false
for _, only := range f.Only {
if buildName == only || processedBuildName == only {
found = true
break
}
}
if !found {
log.Printf("Skipping build '%s' because not specified by -only.", processedBuildName)
continue
}
}
log.Printf("Creating build: %s", processedBuildName)
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
}
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 = jsonutil.Unmarshal(bytes, &vars)
if err != nil {
return nil, err
}
return vars, nil
}
package command
import (
"github.com/mitchellh/packer/packer"
"testing"
)
func testTemplate() (*packer.Template, *packer.ComponentFinder) {
tplData := `{
"variables": {
"foo": null
},
"builders": [
{
"type": "foo"
},
{
"name": "{{user \"foo\"}}",
"type": "bar"
}
]
}
`
tpl, err := packer.ParseTemplate([]byte(tplData), map[string]string{"foo": "bar"})
if err != nil {
panic(err)
}
cf := &packer.ComponentFinder{
Builder: func(string) (packer.Builder, error) { return new(packer.MockBuilder), nil },
}
return tpl, cf
}
func TestBuildOptionsBuilds(t *testing.T) {
opts := new(BuildOptions)
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 2 {
t.Fatalf("bad: %d", len(bs))
}
}
func TestBuildOptionsBuilds_except(t *testing.T) {
opts := new(BuildOptions)
opts.Except = []string{"foo"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "bar" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
//Test to make sure the build name pattern matches
func TestBuildOptionsBuilds_exceptConfigTemplateRaw(t *testing.T) {
opts := new(BuildOptions)
opts.Except = []string{"{{user \"foo\"}}"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "foo" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
//Test to make sure the processed build name matches
func TestBuildOptionsBuilds_exceptConfigTemplateProcessed(t *testing.T) {
opts := new(BuildOptions)
opts.Except = []string{"bar"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "foo" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
func TestBuildOptionsBuilds_only(t *testing.T) {
opts := new(BuildOptions)
opts.Only = []string{"foo"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "foo" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
//Test to make sure the build name pattern matches
func TestBuildOptionsBuilds_onlyConfigTemplateRaw(t *testing.T) {
opts := new(BuildOptions)
opts.Only = []string{"{{user \"foo\"}}"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "bar" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
//Test to make sure the processed build name matches
func TestBuildOptionsBuilds_onlyConfigTemplateProcessed(t *testing.T) {
opts := new(BuildOptions)
opts.Only = []string{"bar"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "bar" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
func TestBuildOptionsBuilds_exceptNonExistent(t *testing.T) {
opts := new(BuildOptions)
opts.Except = []string{"i-dont-exist"}
_, err := opts.Builds(testTemplate())
if err == nil {
t.Fatal("err should not be nil")
}
}
func TestBuildOptionsBuilds_onlyNonExistent(t *testing.T) {
opts := new(BuildOptions)
opts.Only = []string{"i-dont-exist"}
_, err := opts.Builds(testTemplate())
if err == nil {
t.Fatal("err should not be nil")
}
}
func TestBuildOptionsValidate(t *testing.T) {
bf := new(BuildOptions)
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)
}
}
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