config: understand "provisioner" blocks

This commit is contained in:
Mitchell Hashimoto 2014-07-02 20:20:13 -07:00 committed by Armon Dadgar
parent 305994036d
commit e250a6f36c
4 changed files with 145 additions and 6 deletions

View File

@ -31,9 +31,16 @@ type ProviderConfig struct {
// A Terraform resource is something that represents some component that
// can be created and managed, and has some properties associated with it.
type Resource struct {
Name string
Name string
Type string
Count int
RawConfig *RawConfig
Provisioners []*Provisioner
}
// Provisioner is a configured provisioner step on a resource.
type Provisioner struct {
Type string
Count int
RawConfig *RawConfig
}

View File

@ -312,6 +312,10 @@ func loadResourcesLibucl(o *libucl.Object) ([]*Resource, error) {
// Remove the "count" from the config, since we treat that special
delete(config, "count")
// Delete the "provisioner" section from the config since
// that is treated specially.
delete(config, "provisioner")
rawConfig, err := NewRawConfig(config)
if err != nil {
return nil, fmt.Errorf(
@ -335,14 +339,86 @@ func loadResourcesLibucl(o *libucl.Object) ([]*Resource, error) {
}
}
// If we have provisioners, then parse those out
var provisioners []*Provisioner
if po := r.Get("provisioner"); po != nil {
var err error
provisioners, err = loadProvisionersLibucl(po)
po.Close()
if err != nil {
return nil, fmt.Errorf(
"Error reading provisioners for %s[%s]: %s",
t.Key(),
r.Key(),
err)
}
}
result = append(result, &Resource{
Name: r.Key(),
Type: t.Key(),
Count: count,
RawConfig: rawConfig,
Name: r.Key(),
Type: t.Key(),
Count: count,
RawConfig: rawConfig,
Provisioners: provisioners,
})
}
}
return result, nil
}
func loadProvisionersLibucl(o *libucl.Object) ([]*Provisioner, error) {
pos := make([]*libucl.Object, 0, int(o.Len()))
// Accumulate all the actual provisioner configuration objects. We
// have to iterate twice here:
//
// 1. The first iteration is of the list of `provisioner` blocks.
// 2. The second iteration is of the dictionary within the
// provisioner which will have only one element which is the
// type of provisioner to use along with tis config.
//
// In JSON it looks kind of like this:
//
// [
// {
// "shell": {
// ...
// }
// }
// ]
//
iter := o.Iterate(false)
for o1 := iter.Next(); o1 != nil; o1 = iter.Next() {
iter2 := o1.Iterate(true)
for o2 := iter2.Next(); o2 != nil; o2 = iter2.Next() {
pos = append(pos, o2)
}
o1.Close()
iter2.Close()
}
iter.Close()
result := make([]*Provisioner, 0, len(pos))
for _, po := range pos {
defer po.Close()
var config map[string]interface{}
if err := po.Decode(&config); err != nil {
return nil, err
}
rawConfig, err := NewRawConfig(config)
if err != nil {
return nil, err
}
result = append(result, &Provisioner{
Type: po.Key(),
RawConfig: rawConfig,
})
}
return result, nil
}

View File

@ -131,6 +131,22 @@ func outputsStr(os map[string]*Output) string {
return strings.TrimSpace(result)
}
func TestLoad_provisioners(t *testing.T) {
c, err := Load(filepath.Join(fixtureDir, "provisioners.tf"))
if err != nil {
t.Fatalf("err: %s", err)
}
if c == nil {
t.Fatal("config should not be nil")
}
actual := resourcesStr(c.Resources)
if actual != strings.TrimSpace(provisionerResourcesStr) {
t.Fatalf("bad:\n%s", actual)
}
}
// This helper turns a provider configs field into a deterministic
// string value for comparison in tests.
func providerConfigsStr(pcs map[string]*ProviderConfig) string {
@ -213,6 +229,23 @@ func resourcesStr(rs []*Resource) string {
result += fmt.Sprintf(" %s\n", k)
}
if len(r.Provisioners) > 0 {
result += fmt.Sprintf(" provisioners\n")
for _, p := range r.Provisioners {
result += fmt.Sprintf(" %s\n", p.Type)
ks := make([]string, 0, len(p.RawConfig.Raw))
for k, _ := range p.RawConfig.Raw {
ks = append(ks, k)
}
sort.Strings(ks)
for _, k := range ks {
result += fmt.Sprintf(" %s\n", k)
}
}
}
if len(r.RawConfig.Variables) > 0 {
result += fmt.Sprintf(" vars\n")
@ -328,6 +361,18 @@ foo
bar
`
const provisionerResourcesStr = `
aws_instance[web]
ami
security_groups
provisioners
shell
path
vars
resource: aws_security_group.firewall.foo
user: var.foo
`
const variablesVariablesStr = `
bar
<>

View File

@ -0,0 +1,11 @@
resource "aws_instance" "web" {
ami = "${var.foo}"
security_groups = [
"foo",
"${aws_security_group.firewall.foo}"
]
provisioner "shell" {
path = "foo"
}
}