diff --git a/config/config.go b/config/config.go index 63c5651a5..737ec0916 100644 --- a/config/config.go +++ b/config/config.go @@ -15,6 +15,7 @@ type Config struct { ProviderConfigs map[string]*ProviderConfig Resources []*Resource Variables map[string]*Variable + Outputs map[string]*Output } // ProviderConfig is the configuration for a resource provider. @@ -42,6 +43,13 @@ type Variable struct { defaultSet bool } +// Output is an output defined within the configuration. An output is +// resulting data that is highlighted by Terraform when finished. +type Output struct { + Name string + RawConfig *RawConfig +} + // An InterpolatedVariable is a variable that is embedded within a string // in the configuration, such as "hello ${world}" (world in this case is // an interpolated variable). diff --git a/config/loader_libucl.go b/config/loader_libucl.go index 4d5644a26..bf219f0b0 100644 --- a/config/loader_libucl.go +++ b/config/loader_libucl.go @@ -78,6 +78,16 @@ func (t *libuclConfigurable) Config() (*Config, error) { } } + // Build the outputs + if outputs := t.Object.Get("output"); outputs != nil { + var err error + config.Outputs, err = loadOutputsLibucl(outputs) + outputs.Close() + if err != nil { + return nil, err + } + } + return config, nil } @@ -145,6 +155,52 @@ func loadFileLibucl(root string) (configurable, []string, error) { return result, importPaths, nil } +// LoadOutputsLibucl recurses into the given libucl object and turns +// it into a mapping of outputs. +func loadOutputsLibucl(o *libucl.Object) (map[string]*Output, error) { + objects := make(map[string]*libucl.Object) + + // Iterate over all the "output" blocks and get the keys along with + // their raw configuration objects. We'll parse those later. + 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() { + objects[o2.Key()] = o2 + defer o2.Close() + } + + o1.Close() + iter2.Close() + } + iter.Close() + + // Go through each object and turn it into an actual result. + result := make(map[string]*Output) + for n, o := range objects { + var config map[string]interface{} + + if err := o.Decode(&config); err != nil { + return nil, err + } + + rawConfig, err := NewRawConfig(config) + if err != nil { + return nil, fmt.Errorf( + "Error reading config for output %s: %s", + n, + err) + } + + result[n] = &Output{ + Name: n, + RawConfig: rawConfig, + } + } + + return result, nil +} + // LoadProvidersLibucl recurses into the given libucl object and turns // it into a mapping of provider configs. func loadProvidersLibucl(o *libucl.Object) (map[string]*ProviderConfig, error) { diff --git a/config/loader_test.go b/config/loader_test.go index b9340d3a0..362d90989 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -39,6 +39,11 @@ func TestLoadBasic(t *testing.T) { if actual != strings.TrimSpace(basicResourcesStr) { t.Fatalf("bad:\n%s", actual) } + + actual = outputsStr(c.Outputs) + if actual != strings.TrimSpace(basicOutputsStr) { + t.Fatalf("bad:\n%s", actual) + } } func TestLoadBasic_import(t *testing.T) { @@ -92,6 +97,40 @@ func TestLoad_variables(t *testing.T) { } } +func outputsStr(os map[string]*Output) string { + ns := make([]string, 0, len(os)) + for n, _ := range os { + ns = append(ns, n) + } + sort.Strings(ns) + + result := "" + for _, n := range ns { + o := os[n] + + result += fmt.Sprintf("%s\n", n) + + if len(o.RawConfig.Variables) > 0 { + result += fmt.Sprintf(" vars\n") + for _, rawV := range o.RawConfig.Variables { + kind := "unknown" + str := rawV.FullKey() + + switch rawV.(type) { + case *ResourceVariable: + kind = "resource" + case *UserVariable: + kind = "user" + } + + result += fmt.Sprintf(" %s: %s\n", kind, str) + } + } + } + + return strings.TrimSpace(result) +} + // This helper turns a provider configs field into a deterministic // string value for comparison in tests. func providerConfigsStr(pcs map[string]*ProviderConfig) string { @@ -219,6 +258,12 @@ func variablesStr(vs map[string]*Variable) string { return strings.TrimSpace(result) } +const basicOutputsStr = ` +web_ip + vars + resource: aws_instance.web.private_ip +` + const basicProvidersStr = ` aws access_key diff --git a/config/test-fixtures/basic.tf b/config/test-fixtures/basic.tf index 07aa5ff95..84cbd8b31 100644 --- a/config/test-fixtures/basic.tf +++ b/config/test-fixtures/basic.tf @@ -32,3 +32,7 @@ resource aws_instance "web" { resource "aws_instance" "db" { security_groups = "${aws_security_group.firewall.*.id}" } + +output "web_ip" { + value = "${aws_instance.web.private_ip}" +}