config: validate depends_on with module values

This commit is contained in:
Mitchell Hashimoto 2016-11-12 08:21:27 -08:00
parent 0b87ef82c3
commit 576b61a21d
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
4 changed files with 107 additions and 23 deletions

View File

@ -508,25 +508,8 @@ func (c *Config) Validate() error {
} }
r.RawCount.init() r.RawCount.init()
// Verify depends on points to resources that all exist // Validate DependsOn
for _, d := range r.DependsOn { errs = append(errs, c.validateDependsOn(n, r.DependsOn, resources, modules)...)
// Check if we contain interpolations
rc, err := NewRawConfig(map[string]interface{}{
"value": d,
})
if err == nil && len(rc.Variables) > 0 {
errs = append(errs, fmt.Errorf(
"%s: depends on value cannot contain interpolations: %s",
n, d))
continue
}
if _, ok := resources[d]; !ok {
errs = append(errs, fmt.Errorf(
"%s: resource depends on non-existent resource '%s'",
n, d))
}
}
// Verify provider points to a provider that is configured // Verify provider points to a provider that is configured
if r.Provider != "" { if r.Provider != "" {
@ -801,6 +784,48 @@ func (c *Config) validateVarContextFn(
} }
} }
func (c *Config) validateDependsOn(
n string,
v []string,
resources map[string]*Resource,
modules map[string]*Module) []error {
// Verify depends on points to resources that all exist
var errs []error
for _, d := range v {
// Check if we contain interpolations
rc, err := NewRawConfig(map[string]interface{}{
"value": d,
})
if err == nil && len(rc.Variables) > 0 {
errs = append(errs, fmt.Errorf(
"%s: depends on value cannot contain interpolations: %s",
n, d))
continue
}
// If it is a module, verify it is a module
if strings.HasPrefix(d, "module.") {
name := d[len("module."):]
if _, ok := modules[name]; !ok {
errs = append(errs, fmt.Errorf(
"%s: resource depends on non-existent module '%s'",
n, name))
}
continue
}
// Check resources
if _, ok := resources[d]; !ok {
errs = append(errs, fmt.Errorf(
"%s: resource depends on non-existent resource '%s'",
n, d))
}
}
return errs
}
func (m *Module) mergerName() string { func (m *Module) mergerName() string {
return m.Id() return m.Id()
} }

View File

@ -2,6 +2,7 @@ package config
import ( import (
"flag" "flag"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
@ -130,11 +131,55 @@ func TestConfig_emptyCollections(t *testing.T) {
} }
} }
func TestConfigValidate(t *testing.T) { // This table test is the preferred way to test validation of configuration.
c := testConfig(t, "validate-good") // There are dozens of functions below which do not follow this that are
if err := c.Validate(); err != nil { // there mostly historically. They should be converted at some point.
func TestConfigValidate_table(t *testing.T) {
cases := []struct {
Name string
Fixture string
Err bool
ErrString string
}{
{
"basic good",
"validate-good",
false,
"",
},
{
"depends on module",
"validate-depends-on-module",
false,
"",
},
{
"depends on non-existent module",
"validate-depends-on-bad-module",
true,
"non-existent module 'foo'",
},
}
for i, tc := range cases {
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
c := testConfig(t, tc.Fixture)
err := c.Validate()
if (err != nil) != tc.Err {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
if err != nil {
if tc.ErrString != "" && !strings.Contains(err.Error(), tc.ErrString) {
t.Fatalf("expected err to contain: %s\n\ngot: %s", tc.ErrString, err)
}
return
}
})
}
} }
func TestConfigValidate_badDependsOn(t *testing.T) { func TestConfigValidate_badDependsOn(t *testing.T) {

View File

@ -0,0 +1,7 @@
module "child" {
source = "./child"
}
resource aws_instance "web" {
depends_on = ["module.foo"]
}

View File

@ -0,0 +1,7 @@
module "child" {
source = "./child"
}
resource aws_instance "web" {
depends_on = ["module.child"]
}