opentofu/terraform/eval_validate_selfref.go
Martin Atkins 168d84b3c4 core: Make resource type schema versions visible to callers
Previously we were fetching these from the provider but then immediately
discarding the version numbers because the schema API had nowhere to put
them.

To avoid a late-breaking change to the internal structure of
terraform.ProviderSchema (which is constructed directly all over the
tests) we're retaining the resource type schemas in a new map alongside
the existing one with the same keys, rather than just switching to
using the providers.Schema struct directly there.

The methods that return resource type schemas now return two arguments,
intentionally creating a little API friction here so each new caller can
be reminded to think about whether they need to do something with the
schema version, though it can be ignored by many callers.

Since this was a breaking change to the Schemas API anyway, this also
fixes another API wart where there was a separate method for fetching
managed vs. data resource types and thus every caller ended up having a
switch statement on "mode". Now we just accept mode as an argument and
do the switch statement within the single SchemaForResourceType method.
2018-11-27 15:53:54 -08:00

68 lines
2.0 KiB
Go

package terraform
import (
"fmt"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/lang"
"github.com/hashicorp/terraform/tfdiags"
)
// EvalValidateSelfRef is an EvalNode implementation that checks to ensure that
// expressions within a particular referencable block do not reference that
// same block.
type EvalValidateSelfRef struct {
Addr addrs.Referenceable
Config hcl.Body
ProviderSchema **ProviderSchema
}
func (n *EvalValidateSelfRef) Eval(ctx EvalContext) (interface{}, error) {
var diags tfdiags.Diagnostics
addr := n.Addr
addrStrs := make([]string, 0, 1)
addrStrs = append(addrStrs, addr.String())
switch tAddr := addr.(type) {
case addrs.ResourceInstance:
// A resource instance may not refer to its containing resource either.
addrStrs = append(addrStrs, tAddr.ContainingResource().String())
}
if n.ProviderSchema == nil || *n.ProviderSchema == nil {
return nil, fmt.Errorf("provider schema unavailable while validating %s for self-references; this is a bug in Terraform and should be reported", addr)
}
providerSchema := *n.ProviderSchema
var schema *configschema.Block
switch tAddr := addr.(type) {
case addrs.Resource:
schema, _ = providerSchema.SchemaForResourceAddr(tAddr)
case addrs.ResourceInstance:
schema, _ = providerSchema.SchemaForResourceAddr(tAddr.ContainingResource())
}
if schema == nil {
return nil, fmt.Errorf("no schema available for %s to validate for self-references; this is a bug in Terraform and should be reported", addr)
}
refs, _ := lang.ReferencesInBlock(n.Config, schema)
for _, ref := range refs {
for _, addrStr := range addrStrs {
if ref.Subject.String() == addrStr {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Self-referential block",
Detail: fmt.Sprintf("Configuration for %s may not refer to itself.", addrStr),
Subject: ref.SourceRange.ToHCL().Ptr(),
})
}
}
}
return nil, diags.NonFatalErr()
}