2024-02-08 03:48:59 -06:00
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
2023-05-02 10:33:06 -05:00
// SPDX-License-Identifier: MPL-2.0
2023-09-20 07:16:53 -05:00
package tofu
2018-12-20 14:28:23 -06:00
import (
"testing"
2019-09-09 17:58:44 -05:00
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
2023-03-23 10:07:31 -05:00
"github.com/zclconf/go-cty/cty"
2018-12-20 14:28:23 -06:00
2023-09-20 06:35:35 -05:00
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/lang"
"github.com/opentofu/opentofu/internal/providers"
2018-12-20 14:28:23 -06:00
)
func TestStaticValidateReferences ( t * testing . T ) {
tests := [ ] struct {
Ref string
2023-03-23 10:07:31 -05:00
Src addrs . Referenceable
2018-12-20 14:28:23 -06:00
WantErr string
} {
{
2023-03-23 10:07:31 -05:00
Ref : "aws_instance.no_count" ,
WantErr : ` ` ,
2018-12-20 14:28:23 -06:00
} ,
{
2023-03-23 10:07:31 -05:00
Ref : "aws_instance.count" ,
WantErr : ` ` ,
2018-12-20 14:28:23 -06:00
} ,
{
2023-03-23 10:07:31 -05:00
Ref : "aws_instance.count[0]" ,
WantErr : ` ` ,
2018-12-20 14:28:23 -06:00
} ,
{
2023-03-23 10:07:31 -05:00
Ref : "aws_instance.nonexist" ,
WantErr : ` Reference to undeclared resource: A managed resource "aws_instance" "nonexist" has not been declared in the root module. ` ,
2018-12-20 14:28:23 -06:00
} ,
2021-05-14 11:58:28 -05:00
{
2023-03-23 10:07:31 -05:00
Ref : "beep.boop" ,
WantErr : ` Reference to undeclared resource : A managed resource "beep" "boop" has not been declared in the root module .
2021-05-14 11:58:28 -05:00
Did you mean the data resource data . beep . boop ? ` ,
} ,
2018-12-20 14:28:23 -06:00
{
2023-03-23 10:07:31 -05:00
Ref : "aws_instance.no_count[0]" ,
WantErr : ` Unexpected resource instance key: Because aws_instance.no_count does not have "count" or "for_each" set, references to it must not include an index key. Remove the bracketed index to refer to the single instance of this resource. ` ,
2018-12-20 14:28:23 -06:00
} ,
{
2023-03-23 10:07:31 -05:00
Ref : "aws_instance.count.foo" ,
2018-12-20 14:28:23 -06:00
// In this case we return two errors that are somewhat redundant with
// one another, but we'll accept that because they both report the
// problem from different perspectives and so give the user more
// opportunity to understand what's going on here.
2023-03-23 10:07:31 -05:00
WantErr : ` 2 problems :
2018-12-20 14:28:23 -06:00
- Missing resource instance key : Because aws_instance . count has "count" set , its attributes must be accessed on specific instances .
For example , to correlate with indices of a referring resource , use :
aws_instance . count [ count . index ]
- Unsupported attribute : This object has no argument , nested block , or exported attribute named "foo" . ` ,
} ,
configs: Fix provider lookup local name mismatch
When a resource has no `provider` argument specified, its provider is
derived from the implied provider type based on the resource type. For
example, a `boop_instance` resource has an implied provider local name
of `boop`. Correspondingly, its provider configuration is specified with
a `provider "boop"` block.
However, users can use the `required_providers` configuration to give a
different local name to a given provider than its defined type. For
example, a provider may be published at `foobar/beep`, but provide
resources such as `boop_instance`. The most convenient way to use this
provider is with a `required_providers` map:
terraform {
required_providers {
boop = {
source = "foobar/beep"
}
}
}
Once that local name is defined, it is used for provider configuration
(a `provider "boop"` block, not `provider "beep"`). It should also be
used when looking up a resource's provider configuration or provider.
This commit fixes a bug with this edge case, where previously we were
looking up the local provider configuration block using the resource's
assigned provider type. Instead, if no provider argument is specified,
we should be using the implied provider type, as that is what binds the
resource to the local provider configuration.
2020-11-10 14:12:45 -06:00
{
2023-03-23 10:07:31 -05:00
Ref : "boop_instance.yep" ,
WantErr : ` ` ,
configs: Fix provider lookup local name mismatch
When a resource has no `provider` argument specified, its provider is
derived from the implied provider type based on the resource type. For
example, a `boop_instance` resource has an implied provider local name
of `boop`. Correspondingly, its provider configuration is specified with
a `provider "boop"` block.
However, users can use the `required_providers` configuration to give a
different local name to a given provider than its defined type. For
example, a provider may be published at `foobar/beep`, but provide
resources such as `boop_instance`. The most convenient way to use this
provider is with a `required_providers` map:
terraform {
required_providers {
boop = {
source = "foobar/beep"
}
}
}
Once that local name is defined, it is used for provider configuration
(a `provider "boop"` block, not `provider "beep"`). It should also be
used when looking up a resource's provider configuration or provider.
This commit fixes a bug with this edge case, where previously we were
looking up the local provider configuration block using the resource's
assigned provider type. Instead, if no provider argument is specified,
we should be using the implied provider type, as that is what binds the
resource to the local provider configuration.
2020-11-10 14:12:45 -06:00
} ,
{
2023-03-23 10:07:31 -05:00
Ref : "boop_whatever.nope" ,
2023-10-03 02:49:38 -05:00
WantErr : ` Invalid resource type: A managed resource type "boop_whatever" is not supported by provider "registry.opentofu.org/foobar/beep". ` ,
2023-03-23 10:07:31 -05:00
} ,
{
Ref : "data.boop_data.boop_nested" ,
WantErr : ` Reference to scoped resource: The referenced data resource "boop_data" "boop_nested" is not available from this context. ` ,
} ,
{
Ref : "data.boop_data.boop_nested" ,
WantErr : ` ` ,
Src : addrs . Check { Name : "foo" } ,
configs: Fix provider lookup local name mismatch
When a resource has no `provider` argument specified, its provider is
derived from the implied provider type based on the resource type. For
example, a `boop_instance` resource has an implied provider local name
of `boop`. Correspondingly, its provider configuration is specified with
a `provider "boop"` block.
However, users can use the `required_providers` configuration to give a
different local name to a given provider than its defined type. For
example, a provider may be published at `foobar/beep`, but provide
resources such as `boop_instance`. The most convenient way to use this
provider is with a `required_providers` map:
terraform {
required_providers {
boop = {
source = "foobar/beep"
}
}
}
Once that local name is defined, it is used for provider configuration
(a `provider "boop"` block, not `provider "beep"`). It should also be
used when looking up a resource's provider configuration or provider.
This commit fixes a bug with this edge case, where previously we were
looking up the local provider configuration block using the resource's
assigned provider type. Instead, if no provider argument is specified,
we should be using the implied provider type, as that is what binds the
resource to the local provider configuration.
2020-11-10 14:12:45 -06:00
} ,
2018-12-20 14:28:23 -06:00
}
cfg := testModule ( t , "static-validate-refs" )
evaluator := & Evaluator {
Config : cfg ,
2023-07-06 09:35:33 -05:00
Plugins : schemaOnlyProvidersForTesting ( map [ addrs . Provider ] providers . ProviderSchema {
2021-08-31 19:53:03 -05:00
addrs . NewDefaultProvider ( "aws" ) : {
2023-07-05 16:31:39 -05:00
ResourceTypes : map [ string ] providers . Schema {
"aws_instance" : {
Block : & configschema . Block { } ,
} ,
2018-12-20 14:28:23 -06:00
} ,
2021-08-31 19:53:03 -05:00
} ,
addrs . MustParseProviderSourceString ( "foobar/beep" ) : {
2023-07-05 16:31:39 -05:00
ResourceTypes : map [ string ] providers . Schema {
2021-08-31 19:53:03 -05:00
// intentional mismatch between resource type prefix and provider type
2023-07-05 16:31:39 -05:00
"boop_instance" : {
Block : & configschema . Block { } ,
} ,
configs: Fix provider lookup local name mismatch
When a resource has no `provider` argument specified, its provider is
derived from the implied provider type based on the resource type. For
example, a `boop_instance` resource has an implied provider local name
of `boop`. Correspondingly, its provider configuration is specified with
a `provider "boop"` block.
However, users can use the `required_providers` configuration to give a
different local name to a given provider than its defined type. For
example, a provider may be published at `foobar/beep`, but provide
resources such as `boop_instance`. The most convenient way to use this
provider is with a `required_providers` map:
terraform {
required_providers {
boop = {
source = "foobar/beep"
}
}
}
Once that local name is defined, it is used for provider configuration
(a `provider "boop"` block, not `provider "beep"`). It should also be
used when looking up a resource's provider configuration or provider.
This commit fixes a bug with this edge case, where previously we were
looking up the local provider configuration block using the resource's
assigned provider type. Instead, if no provider argument is specified,
we should be using the implied provider type, as that is what binds the
resource to the local provider configuration.
2020-11-10 14:12:45 -06:00
} ,
2023-07-05 16:31:39 -05:00
DataSources : map [ string ] providers . Schema {
2023-03-23 10:07:31 -05:00
"boop_data" : {
2023-07-05 16:31:39 -05:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"id" : {
Type : cty . String ,
Optional : true ,
} ,
2023-03-23 10:07:31 -05:00
} ,
} ,
} ,
} ,
2018-12-20 14:28:23 -06:00
} ,
2024-04-10 07:04:08 -05:00
} , t ) ,
2018-12-20 14:28:23 -06:00
}
for _ , test := range tests {
t . Run ( test . Ref , func ( t * testing . T ) {
traversal , hclDiags := hclsyntax . ParseTraversalAbs ( [ ] byte ( test . Ref ) , "" , hcl . Pos { Line : 1 , Column : 1 } )
if hclDiags . HasErrors ( ) {
t . Fatal ( hclDiags . Error ( ) )
}
2023-06-28 02:47:24 -05:00
refs , diags := lang . References ( addrs . ParseRef , [ ] hcl . Traversal { traversal } )
2018-12-20 14:28:23 -06:00
if diags . HasErrors ( ) {
t . Fatal ( diags . Err ( ) )
}
data := & evaluationStateData {
Evaluator : evaluator ,
}
2023-03-23 10:07:31 -05:00
diags = data . StaticValidateReferences ( refs , nil , test . Src )
2018-12-20 14:28:23 -06:00
if diags . HasErrors ( ) {
if test . WantErr == "" {
t . Fatalf ( "Unexpected diagnostics: %s" , diags . Err ( ) )
}
gotErr := diags . Err ( ) . Error ( )
if gotErr != test . WantErr {
t . Fatalf ( "Wrong diagnostics\ngot: %s\nwant: %s" , gotErr , test . WantErr )
}
return
}
if test . WantErr != "" {
t . Fatalf ( "Expected diagnostics, but got none\nwant: %s" , test . WantErr )
}
} )
}
}