Commit 4583ed61 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

template: parse provisioners

parent 95890003
...@@ -31,7 +31,9 @@ func (r *rawTemplate) Template() (*Template, error) { ...@@ -31,7 +31,9 @@ func (r *rawTemplate) Template() (*Template, error) {
var errs error var errs error
// Let's start by gathering all the builders // 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 { for i, rawB := range r.Builders {
var b Builder var b Builder
if err := mapstructure.WeakDecode(rawB, &b); err != nil { if err := mapstructure.WeakDecode(rawB, &b); err != nil {
...@@ -72,6 +74,39 @@ func (r *rawTemplate) Template() (*Template, error) { ...@@ -72,6 +74,39 @@ func (r *rawTemplate) Template() (*Template, error) {
result.Builders[b.Name] = &b 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 we have errors, return those with a nil result
if errs != nil { if errs != nil {
return nil, errs return nil, errs
...@@ -80,6 +115,23 @@ func (r *rawTemplate) Template() (*Template, error) { ...@@ -80,6 +115,23 @@ func (r *rawTemplate) Template() (*Template, error) {
return &result, nil 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. // Parse takes the given io.Reader and parses a Template object out of it.
func Parse(r io.Reader) (*Template, error) { func Parse(r io.Reader) (*Template, error) {
// First, decode the object into an interface{}. We do this instead of // First, decode the object into an interface{}. We do this instead of
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"os" "os"
"reflect" "reflect"
"testing" "testing"
"time"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
...@@ -12,6 +13,9 @@ func TestParse(t *testing.T) { ...@@ -12,6 +13,9 @@ func TestParse(t *testing.T) {
Result *Template Result *Template
Err bool Err bool
}{ }{
/*
* Builders
*/
{ {
"parse-basic.json", "parse-basic.json",
&Template{ &Template{
...@@ -34,6 +38,85 @@ func TestParse(t *testing.T) { ...@@ -34,6 +38,85 @@ func TestParse(t *testing.T) {
nil, nil,
true, 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 { for _, tc := range cases {
...@@ -49,7 +132,7 @@ func TestParse(t *testing.T) { ...@@ -49,7 +132,7 @@ func TestParse(t *testing.T) {
} }
if !reflect.DeepEqual(tpl, tc.Result) { 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)
} }
} }
} }
...@@ -36,12 +36,12 @@ type PostProcessor struct { ...@@ -36,12 +36,12 @@ type PostProcessor struct {
// Provisioner represents a provisioner within the template. // Provisioner represents a provisioner within the template.
type Provisioner struct { type Provisioner struct {
OnlyExcept OnlyExcept `mapstructure:",squash"`
Type string Type string
Config map[string]interface{} Config map[string]interface{}
Override 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. // Push represents the configuration for pushing the template to Atlas.
...@@ -75,3 +75,7 @@ type OnlyExcept struct { ...@@ -75,3 +75,7 @@ type OnlyExcept struct {
func (b *Builder) GoString() string { func (b *Builder) GoString() string {
return fmt.Sprintf("*%#v", *b) return fmt.Sprintf("*%#v", *b)
} }
func (p *Provisioner) GoString() string {
return fmt.Sprintf("*%#v", *p)
}
{
"provisioners": [
{"type": "something"}
]
}
{
"provisioners": [
{
"type": "something",
"except": ["foo"]
}
]
}
{
"provisioners": [
{"foo": "something"}
]
}
{
"provisioners": [
{
"type": "something",
"only": ["foo"]
}
]
}
{
"provisioners": [
{
"type": "something",
"override": {
"foo": {}
}
}
]
}
{
"provisioners": [
{
"type": "something",
"pause_before": "1s"
}
]
}
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