From 745aebeda1030485b8b08ed861dea93e0cbf73be Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 11 May 2021 10:03:04 -0700 Subject: [PATCH] core: "Did you mean" hints for unsupported resource types When we detect that a module is trying to declare a resource type that doesn't exist in its corresponding provider, we have enough information to give some hints as to what might be wrong. We give preference to the possibility of mixing up a managed resource type with a data resource type, but if not that then we'll use our usual levenshtein-distance-based similar name suggestion function. We currently don't typically test exact error message text, so and this is just a cosmetic change to the message output, so there are now new tests or test modifications here. --- terraform/node_resource_validate.go | 31 +++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/terraform/node_resource_validate.go b/terraform/node_resource_validate.go index 071f8e9b2..13d5997ea 100644 --- a/terraform/node_resource_validate.go +++ b/terraform/node_resource_validate.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/internal/didyoumean" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/provisioners" "github.com/hashicorp/terraform/tfdiags" @@ -345,10 +346,23 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag case addrs.ManagedResourceMode: schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type) if schema == nil { + var suggestion string + if dSchema, _ := providerSchema.SchemaForResourceType(addrs.DataResourceMode, n.Config.Type); dSchema != nil { + suggestion = fmt.Sprintf("\n\nDid you intend to use the data source %q? If so, declare this using a \"data\" block instead of a \"resource\" block.", n.Config.Type) + } else if len(providerSchema.ResourceTypes) > 0 { + suggestions := make([]string, 0, len(providerSchema.ResourceTypes)) + for name := range providerSchema.ResourceTypes { + suggestions = append(suggestions, name) + } + if suggestion = didyoumean.NameSuggestion(n.Config.Type, suggestions); suggestion != "" { + suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) + } + } + diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid resource type", - Detail: fmt.Sprintf("The provider %s does not support resource type %q.", n.Config.ProviderConfigAddr(), n.Config.Type), + Detail: fmt.Sprintf("The provider %s does not support resource type %q.%s", n.Provider().ForDisplay(), n.Config.Type, suggestion), Subject: &n.Config.TypeRange, }) return diags @@ -386,10 +400,23 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag case addrs.DataResourceMode: schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type) if schema == nil { + var suggestion string + if dSchema, _ := providerSchema.SchemaForResourceType(addrs.ManagedResourceMode, n.Config.Type); dSchema != nil { + suggestion = fmt.Sprintf("\n\nDid you intend to use the managed resource type %q? If so, declare this using a \"resource\" block instead of a \"data\" block.", n.Config.Type) + } else if len(providerSchema.DataSources) > 0 { + suggestions := make([]string, 0, len(providerSchema.DataSources)) + for name := range providerSchema.DataSources { + suggestions = append(suggestions, name) + } + if suggestion = didyoumean.NameSuggestion(n.Config.Type, suggestions); suggestion != "" { + suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) + } + } + diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid data source", - Detail: fmt.Sprintf("The provider %s does not support data source %q.", n.Config.ProviderConfigAddr(), n.Config.Type), + Detail: fmt.Sprintf("The provider %s does not support data source %q.%s", n.Provider().ForDisplay(), n.Config.Type, suggestion), Subject: &n.Config.TypeRange, }) return diags