core: validate that depends_on addresses are for objects

Since the "References" function on graph nodes can't return errors, we
need to catch invalid depends_on references during the validation pass.

In this case, we're checking that the address is exact, rather than being
part of a traversal into an attribute of the object. In other words,
aws_instance.example is valid but aws_instance.example.id is not.
This commit is contained in:
Martin Atkins 2018-07-02 10:23:41 -07:00
parent 83538bbecb
commit e4efe92e83
2 changed files with 89 additions and 0 deletions

View File

@ -380,6 +380,19 @@ func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
diags = diags.Append(countDiags)
}
for _, traversal := range n.Config.DependsOn {
ref, refDiags := addrs.ParseRef(traversal)
diags = diags.Append(refDiags)
if len(ref.Remaining) != 0 {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid depends_on reference",
Detail: "References in depends_on must be to a whole object (resource, etc), not to an attribute of an object.",
Subject: ref.Remaining.SourceRange().Ptr(),
})
}
}
var warns []string
var errs []error

View File

@ -278,6 +278,82 @@ func TestEvalValidateResource_ignoreWarnings(t *testing.T) {
}
}
func TestEvalValidateResource_invalidDependsOn(t *testing.T) {
mp := simpleMockProvider()
mp.ValidateResourceFn = func(rt string, c *ResourceConfig) (ws []string, es []error) {
return
}
// We'll check a _valid_ config first, to make sure we're not failing
// for some other reason, and then make it invalid.
p := ResourceProvider(mp)
rc := &configs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_object",
Name: "foo",
Config: configs.SynthBody("", map[string]cty.Value{}),
DependsOn: []hcl.Traversal{
// Depending on path.module is pointless, since it is immediately
// available, but we allow all of the referencable addrs here
// for consistency: referencing them is harmless, and avoids the
// need for us to document a different subset of addresses that
// are valid in depends_on.
// For the sake of this test, it's a valid address we can use that
// doesn't require something else to exist in the configuration.
{
hcl.TraverseRoot{
Name: "path",
},
hcl.TraverseAttr{
Name: "module",
},
},
},
}
node := &EvalValidateResource{
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
},
Provider: &p,
Config: rc,
ProviderSchema: &mp.GetSchemaReturn,
}
ctx := &MockEvalContext{}
ctx.installSimpleEval()
_, err := node.Eval(ctx)
if err != nil {
t.Fatalf("error for supposedly-valid config: %s", err)
}
// No we'll make it invalid, but adding additional traversal steps
// on the end of what we're referencing. This is intended to catch the
// situation where the user tries to depend on e.g. a specific resource
// attribute, rather than the whole resource, like aws_instance.foo.id.
rc.DependsOn = append(rc.DependsOn, hcl.Traversal{
hcl.TraverseRoot{
Name: "path",
},
hcl.TraverseAttr{
Name: "module",
},
hcl.TraverseAttr{
Name: "extra",
},
})
_, err = node.Eval(ctx)
if err == nil {
t.Fatal("no error for invalid depends_on")
}
if got, want := err.Error(), "Invalid depends_on reference"; !strings.Contains(got, want) {
t.Fatalf("wrong error\ngot: %s\nwant: Message containing %q", got, want)
}
}
func TestEvalValidateProvisioner_valid(t *testing.T) {
mp := &MockResourceProvisioner{}
var p ResourceProvisioner = mp