diff --git a/template/parse.go b/template/parse.go index 3cb9e288e..a96117111 100644 --- a/template/parse.go +++ b/template/parse.go @@ -31,7 +31,9 @@ func (r *rawTemplate) Template() (*Template, error) { var errs error // Let's start by gathering all the builders - result.Builders = make(map[string]*Builder) + if len(r.Builders) > 0 { + result.Builders = make(map[string]*Builder, len(r.Builders)) + } for i, rawB := range r.Builders { var b Builder if err := mapstructure.WeakDecode(rawB, &b); err != nil { @@ -72,6 +74,39 @@ func (r *rawTemplate) Template() (*Template, error) { result.Builders[b.Name] = &b } + // Gather all the provisioners + if len(r.Provisioners) > 0 { + result.Provisioners = make([]*Provisioner, 0, len(r.Provisioners)) + } + for i, v := range r.Provisioners { + var p Provisioner + if err := r.decoder(&p, nil).Decode(v); err != nil { + errs = multierror.Append(errs, fmt.Errorf( + "provisioner %d: %s", i+1, err)) + continue + } + + // Type is required before any richer validation + if p.Type == "" { + errs = multierror.Append(errs, fmt.Errorf( + "provisioner %d: missing 'type'", i+1)) + continue + } + + // Copy the configuration + delete(v, "except") + delete(v, "only") + delete(v, "override") + delete(v, "pause_before") + delete(v, "type") + if len(v) > 0 { + p.Config = v + } + + // TODO: stuff + result.Provisioners = append(result.Provisioners, &p) + } + // If we have errors, return those with a nil result if errs != nil { return nil, errs @@ -80,6 +115,23 @@ func (r *rawTemplate) Template() (*Template, error) { return &result, nil } +func (r *rawTemplate) decoder( + result interface{}, + md *mapstructure.Metadata) *mapstructure.Decoder { + d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + Metadata: md, + Result: result, + }) + if err != nil { + // This really shouldn't happen since we have firm control over + // all the arguments and they're all unit tested. So we use a + // panic here to note this would definitely be a bug. + panic(err) + } + return d +} + // Parse takes the given io.Reader and parses a Template object out of it. func Parse(r io.Reader) (*Template, error) { // First, decode the object into an interface{}. We do this instead of diff --git a/template/parse_test.go b/template/parse_test.go index b7789298c..2c5b8f735 100644 --- a/template/parse_test.go +++ b/template/parse_test.go @@ -4,6 +4,7 @@ import ( "os" "reflect" "testing" + "time" ) func TestParse(t *testing.T) { @@ -12,6 +13,9 @@ func TestParse(t *testing.T) { Result *Template Err bool }{ + /* + * Builders + */ { "parse-basic.json", &Template{ @@ -34,6 +38,85 @@ func TestParse(t *testing.T) { nil, true, }, + + /* + * Provisioners + */ + { + "parse-provisioner-basic.json", + &Template{ + Provisioners: []*Provisioner{ + &Provisioner{ + Type: "something", + }, + }, + }, + false, + }, + + { + "parse-provisioner-pause-before.json", + &Template{ + Provisioners: []*Provisioner{ + &Provisioner{ + Type: "something", + PauseBefore: 1 * time.Second, + }, + }, + }, + false, + }, + + { + "parse-provisioner-only.json", + &Template{ + Provisioners: []*Provisioner{ + &Provisioner{ + Type: "something", + OnlyExcept: OnlyExcept{ + Only: []string{"foo"}, + }, + }, + }, + }, + false, + }, + + { + "parse-provisioner-except.json", + &Template{ + Provisioners: []*Provisioner{ + &Provisioner{ + Type: "something", + OnlyExcept: OnlyExcept{ + Except: []string{"foo"}, + }, + }, + }, + }, + false, + }, + + { + "parse-provisioner-override.json", + &Template{ + Provisioners: []*Provisioner{ + &Provisioner{ + Type: "something", + Override: map[string]interface{}{ + "foo": map[string]interface{}{}, + }, + }, + }, + }, + false, + }, + + { + "parse-provisioner-no-type.json", + nil, + true, + }, } for _, tc := range cases { @@ -49,7 +132,7 @@ func TestParse(t *testing.T) { } if !reflect.DeepEqual(tpl, tc.Result) { - t.Fatalf("bad: %#v", tpl) + t.Fatalf("bad: %s\n\n%#v\n\n%#v", tc.File, tpl, tc.Result) } } } diff --git a/template/template.go b/template/template.go index 477a6d824..daee508fc 100644 --- a/template/template.go +++ b/template/template.go @@ -36,12 +36,12 @@ type PostProcessor struct { // Provisioner represents a provisioner within the template. type Provisioner struct { - OnlyExcept + OnlyExcept `mapstructure:",squash"` Type string Config map[string]interface{} Override map[string]interface{} - PauseBefore time.Duration + PauseBefore time.Duration `mapstructure:"pause_before"` } // Push represents the configuration for pushing the template to Atlas. @@ -75,3 +75,7 @@ type OnlyExcept struct { func (b *Builder) GoString() string { return fmt.Sprintf("*%#v", *b) } + +func (p *Provisioner) GoString() string { + return fmt.Sprintf("*%#v", *p) +} diff --git a/template/test-fixtures/parse-provisioner-basic.json b/template/test-fixtures/parse-provisioner-basic.json new file mode 100644 index 000000000..bf0d8d910 --- /dev/null +++ b/template/test-fixtures/parse-provisioner-basic.json @@ -0,0 +1,5 @@ +{ + "provisioners": [ + {"type": "something"} + ] +} diff --git a/template/test-fixtures/parse-provisioner-except.json b/template/test-fixtures/parse-provisioner-except.json new file mode 100644 index 000000000..8c7f0c8f5 --- /dev/null +++ b/template/test-fixtures/parse-provisioner-except.json @@ -0,0 +1,8 @@ +{ + "provisioners": [ + { + "type": "something", + "except": ["foo"] + } + ] +} diff --git a/template/test-fixtures/parse-provisioner-no-type.json b/template/test-fixtures/parse-provisioner-no-type.json new file mode 100644 index 000000000..40bc214d2 --- /dev/null +++ b/template/test-fixtures/parse-provisioner-no-type.json @@ -0,0 +1,5 @@ +{ + "provisioners": [ + {"foo": "something"} + ] +} diff --git a/template/test-fixtures/parse-provisioner-only.json b/template/test-fixtures/parse-provisioner-only.json new file mode 100644 index 000000000..3bbb534b2 --- /dev/null +++ b/template/test-fixtures/parse-provisioner-only.json @@ -0,0 +1,8 @@ +{ + "provisioners": [ + { + "type": "something", + "only": ["foo"] + } + ] +} diff --git a/template/test-fixtures/parse-provisioner-override.json b/template/test-fixtures/parse-provisioner-override.json new file mode 100644 index 000000000..5b55099ba --- /dev/null +++ b/template/test-fixtures/parse-provisioner-override.json @@ -0,0 +1,10 @@ +{ + "provisioners": [ + { + "type": "something", + "override": { + "foo": {} + } + } + ] +} diff --git a/template/test-fixtures/parse-provisioner-pause-before.json b/template/test-fixtures/parse-provisioner-pause-before.json new file mode 100644 index 000000000..70640847b --- /dev/null +++ b/template/test-fixtures/parse-provisioner-pause-before.json @@ -0,0 +1,8 @@ +{ + "provisioners": [ + { + "type": "something", + "pause_before": "1s" + } + ] +}