diff --git a/config/config.go b/config/config.go index 3d21d891c..464bef5b7 100644 --- a/config/config.go +++ b/config/config.go @@ -14,7 +14,7 @@ import ( type Config struct { ProviderConfigs map[string]*ProviderConfig Resources []*Resource - Variables map[string]Variable + Variables map[string]*Variable } // ProviderConfig is the configuration for a resource provider. @@ -36,9 +36,11 @@ type Resource struct { Variables map[string]InterpolatedVariable } +// Variable is a variable defined within the configuration. type Variable struct { Default string Description string + defaultSet bool } // An InterpolatedVariable is a variable that is embedded within a string @@ -130,6 +132,11 @@ func (c *Config) ResourceGraph() *depgraph.Graph { } } +// Required tests whether a variable is required or not. +func (v *Variable) Required() bool { + return !v.defaultSet +} + func NewResourceVariable(key string) (*ResourceVariable, error) { parts := strings.SplitN(key, ".", 3) return &ResourceVariable{ diff --git a/config/loader_libucl.go b/config/loader_libucl.go index 08d9e5e53..32af9711e 100644 --- a/config/loader_libucl.go +++ b/config/loader_libucl.go @@ -23,18 +23,39 @@ func (t *libuclConfigurable) Close() error { } func (t *libuclConfigurable) Config() (*Config, error) { + type LibuclVariable struct { + Default string + Description string + Fields []string `libucl:",decodedFields"` + } + var rawConfig struct { - Variable map[string]Variable + Variable map[string]*LibuclVariable } if err := t.Object.Decode(&rawConfig); err != nil { return nil, err } - // Start building up the actual configuration. We first - // copy the fields that can be directly assigned. + // Start building up the actual configuration. We start with + // variables. config := new(Config) - config.Variables = rawConfig.Variable + config.Variables = make(map[string]*Variable) + for k, v := range rawConfig.Variable { + defaultSet := false + for _, f := range v.Fields { + if f == "Default" { + defaultSet = true + break + } + } + + config.Variables[k] = &Variable{ + Default: v.Default, + Description: v.Description, + defaultSet: defaultSet, + } + } // Build the provider configs providers := t.Object.Get("provider") diff --git a/config/loader_test.go b/config/loader_test.go index e28d72c91..81b5a2f35 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -66,6 +66,31 @@ func TestLoadBasic_import(t *testing.T) { } } +func TestLoad_variables(t *testing.T) { + c, err := Load(filepath.Join(fixtureDir, "variables.tf")) + if err != nil { + t.Fatalf("err: %s", err) + } + if c == nil { + t.Fatal("config should not be nil") + } + + actual := variablesStr(c.Variables) + if actual != strings.TrimSpace(variablesVariablesStr) { + t.Fatalf("bad:\n%s", actual) + } + + if !c.Variables["foo"].Required() { + t.Fatal("foo should be required") + } + if c.Variables["bar"].Required() { + t.Fatal("bar should not be required") + } + if c.Variables["baz"].Required() { + t.Fatal("baz should not be required") + } +} + // This helper turns a provider configs field into a deterministic // string value for comparison in tests. func providerConfigsStr(pcs map[string]*ProviderConfig) string { @@ -135,7 +160,7 @@ func resourcesStr(rs []*Resource) string { // This helper turns a variables field into a deterministic // string value for comparison in tests. -func variablesStr(vs map[string]Variable) string { +func variablesStr(vs map[string]*Variable) string { result := "" for k, v := range vs { if v.Default == "" { @@ -199,3 +224,15 @@ foo bar bar ` + +const variablesVariablesStr = ` +foo + <> + <> +bar + <> + <> +baz + foo + <> +` diff --git a/config/test-fixtures/variables.tf b/config/test-fixtures/variables.tf new file mode 100644 index 000000000..2fc0c6489 --- /dev/null +++ b/config/test-fixtures/variables.tf @@ -0,0 +1,7 @@ +variable "foo" {} +variable "bar" { + default = "" +} +variable "baz" { + default = "foo" +}