diff --git a/internal/stacks/stackconfig/config_test.go b/internal/stacks/stackconfig/config_test.go index f85d30c8d6..6af08c8be0 100644 --- a/internal/stacks/stackconfig/config_test.go +++ b/internal/stacks/stackconfig/config_test.go @@ -14,51 +14,6 @@ import ( "github.com/hashicorp/terraform/internal/tfdiags" ) -func TestLoadConfigDirDeprecated(t *testing.T) { - bundle, err := sourcebundle.OpenDir("testdata/basics-bundle") - if err != nil { - t.Fatal(err) - } - - rootAddr := sourceaddrs.MustParseSource("git::https://example.com/deprecated.git").(sourceaddrs.RemoteSource) - _, gotDiags := LoadConfigDir(rootAddr, bundle) - - wantDiags := tfdiags.Diagnostics{ - tfdiags.Sourceless(tfdiags.Warning, "Deprecated filename usage", "This configuration is using the deprecated .tfstack.hcl or .tfstack.json file extensions. This will not be supported in a future version of Terraform, please update your files to use the latest .tfcomponent.hcl or .tfcomponent.json file extensions."), - } - - count := len(wantDiags) - if len(gotDiags) > count { - count = len(gotDiags) - } - - for i := 0; i < count; i++ { - if i >= len(wantDiags) { - t.Errorf("unexpected diagnostic:\n%s", gotDiags[i]) - continue - } - - if i >= len(gotDiags) { - t.Errorf("missing diagnostic:\n%s", wantDiags[i]) - continue - } - - got, want := gotDiags[i], wantDiags[i] - - if got, want := got.Severity(), want.Severity(); got != want { - t.Errorf("diagnostics[%d] severity\ngot: %s\nwant: %s", i, got, want) - } - - if got, want := got.Description().Summary, want.Description().Summary; got != want { - t.Errorf("diagnostics[%d] summary\ngot: %s\nwant: %s", i, got, want) - } - - if got, want := got.Description().Detail, want.Description().Detail; got != want { - t.Errorf("diagnostics[%d] detail\ngot: %s\nwant: %s", i, got, want) - } - } -} - func TestLoadConfigDirErrors(t *testing.T) { bundle, err := sourcebundle.OpenDir("testdata/basics-bundle") if err != nil { diff --git a/internal/stacks/stackconfig/file.go b/internal/stacks/stackconfig/file.go index 7376acdfca..1aca35725b 100644 --- a/internal/stacks/stackconfig/file.go +++ b/internal/stacks/stackconfig/file.go @@ -155,34 +155,28 @@ func DecodeFileBody(body hcl.Body, fileAddr sourceaddrs.FinalSource) (*File, tfd return ret, diags } -// ParseFileSource parses the given source code as the content of a Stacks -// component file and then delegates the result to [DecodeFileBody] for -// analysis, returning that final result. -// -// For now, we support both the .tfstack. and .tfcomponent. file suffixes while -// the former is still in the process of being deprecated. It is not valid for -// a single stack to mix and match the two options. We are, therefore, accepting -// a "prior suffix" argument which lets us know which suffix was used by other -// files in this stack. +// ParseFileSource parses the given source code as the content of either a +// .tfcomponent.hcl or .tfcomponent.json file, and then delegates the result to +// [DecodeFileBody] for analysis, returning that final result. // // ParseFileSource chooses between native vs. JSON syntax based on the suffix // of the filename in the given source address, which must be either -// ".[tfstack|tfcomponent].hcl" or ".[tfstack|tfcomponent].json". -func ParseFileSource(src []byte, suffix string, fileAddr sourceaddrs.FinalSource) (*File, tfdiags.Diagnostics) { +// ".tfcomponent.hcl" or ".tfcomponent.json". +func ParseFileSource(src []byte, fileAddr sourceaddrs.FinalSource) (*File, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics filename := sourceaddrs.FinalSourceFilename(fileAddr) var body hcl.Body - switch { - case strings.HasSuffix(filename, fmt.Sprintf(".%s.hcl", suffix)): + switch validFilenameSuffix(filename) { + case ".tfcomponent.hcl": hclFile, hclDiags := hclsyntax.ParseConfig(src, fileAddr.String(), hcl.InitialPos) diags = diags.Append(hclDiags) if diags.HasErrors() { return nil, diags } body = hclFile.Body - case strings.HasSuffix(filename, fmt.Sprintf(".%s.json", suffix)): + case ".tfcomponent.json": hclFile, hclDiags := hcljson.Parse(src, fileAddr.String()) diags = diags.Append(hclDiags) if diags.HasErrors() { @@ -194,8 +188,8 @@ func ParseFileSource(src []byte, suffix string, fileAddr sourceaddrs.FinalSource tfdiags.Error, "Unsupported file type", fmt.Sprintf( - "Cannot load %s as a stack configuration file: filename must have either a .%s.hcl or .%s.json suffix.", - fileAddr, suffix, suffix, + "Cannot load %s as a stack configuration file: filename must have either a .tfcomponent.hcl or .tfcomponent.json suffix.", + fileAddr, ), )) return nil, diags @@ -209,23 +203,15 @@ func ParseFileSource(src []byte, suffix string, fileAddr sourceaddrs.FinalSource // validFilenameSuffix returns ".tfcomponent.hcl" or ".tfcomponent.json" if the // given filename ends with that suffix, and otherwise returns an empty // string to indicate that the suffix was invalid. -// -// We still support the deprecated .tfstack suffix for the time being. func validFilenameSuffix(filename string) string { const nativeSuffix = ".tfcomponent.hcl" const jsonSuffix = ".tfcomponent.json" - const deprecatedNativeSuffix = ".tfstack.hcl" - const deprecatedJsonSuffix = ".tfstack.json" switch { - case strings.HasSuffix(filename, deprecatedNativeSuffix): - return "tfstack" - case strings.HasSuffix(filename, deprecatedJsonSuffix): - return "tfstack" case strings.HasSuffix(filename, nativeSuffix): - return "tfcomponent" + return nativeSuffix case strings.HasSuffix(filename, jsonSuffix): - return "tfcomponent" + return jsonSuffix default: return "" } diff --git a/internal/stacks/stackconfig/stack.go b/internal/stacks/stackconfig/stack.go index 70286c7943..25bc471ff7 100644 --- a/internal/stacks/stackconfig/stack.go +++ b/internal/stacks/stackconfig/stack.go @@ -90,25 +90,12 @@ func LoadSingleStackConfig(sourceAddr sourceaddrs.FinalSource, sources *sourcebu Declarations: makeDeclarations(), } - var priorSuffix string for _, entry := range allEntries { - suffix := validFilenameSuffix(entry.Name()) - if suffix == "" { + if suffix := validFilenameSuffix(entry.Name()); suffix == "" { // not a file we're interested in, then continue } - if priorSuffix == "" { - priorSuffix = suffix - } else if priorSuffix != suffix { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid stack configuration directory", - "This stack configuration has mixed the deprecated .tfstack.hcl or .tfstack.json file extensions with the latest .tfcomponent.hcl or .tfcomponent.json file extensions. Update all files to the latest .tfcomponent file extension.", - )) - continue - } - asLocalSourcePath := "./" + filepath.Base(entry.Name()) relSource, err := sourceaddrs.ParseLocalSource(asLocalSourcePath) if err != nil { @@ -143,7 +130,7 @@ func LoadSingleStackConfig(sourceAddr sourceaddrs.FinalSource, sources *sourcebu )) } - file, moreDiags := ParseFileSource(src, priorSuffix, fileSourceAddr) + file, moreDiags := ParseFileSource(src, fileSourceAddr) diags = diags.Append(moreDiags) if moreDiags.HasErrors() { // We'll still try to analyze other files, so we can gather up @@ -176,13 +163,5 @@ func LoadSingleStackConfig(sourceAddr sourceaddrs.FinalSource, sources *sourcebu pc.ProviderAddr = providerAddr } - if priorSuffix == "tfstack" { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Deprecated filename usage", - Detail: "This configuration is using the deprecated .tfstack.hcl or .tfstack.json file extensions. This will not be supported in a future version of Terraform, please update your files to use the latest .tfcomponent.hcl or .tfcomponent.json file extensions.", - }) - } - return ret, diags } diff --git a/internal/stacks/stackconfig/testdata/basics-bundle/deprecated/component/main.tf b/internal/stacks/stackconfig/testdata/basics-bundle/deprecated/component/main.tf deleted file mode 100644 index 5ab385503e..0000000000 --- a/internal/stacks/stackconfig/testdata/basics-bundle/deprecated/component/main.tf +++ /dev/null @@ -1,17 +0,0 @@ -variable "name" { - type = string -} - -resource "null_resource" "example" { - triggers = { - name = var.name - } -} - -output "greeting" { - value = "Hello, ${var.name}!" -} - -output "resource_id" { - value = null_resource.example.id -} diff --git a/internal/stacks/stackconfig/testdata/basics-bundle/deprecated/main.tfstack.hcl b/internal/stacks/stackconfig/testdata/basics-bundle/deprecated/main.tfstack.hcl deleted file mode 100644 index 5fa4bda3e4..0000000000 --- a/internal/stacks/stackconfig/testdata/basics-bundle/deprecated/main.tfstack.hcl +++ /dev/null @@ -1,41 +0,0 @@ -required_providers { - null = { - source = "hashicorp/null" - version = "3.2.1" - } -} - -variable "name" { - type = string -} - -provider "null" "a" {} - -component "a" { - source = "./component" - - inputs = { - name = var.name - } - providers = { - null = var.provider - } -} - -removed { - from = component.b - - source = "./component" - providers = { - null = var.provider - } - - lifecycle { - destroy = true - } -} - -output "greeting" { - type = string - value = component.a.greeting -} diff --git a/internal/stacks/stackruntime/internal/stackeval/README.md b/internal/stacks/stackruntime/internal/stackeval/README.md index 60412bf39d..1d5e8f6bc8 100644 --- a/internal/stacks/stackruntime/internal/stackeval/README.md +++ b/internal/stacks/stackruntime/internal/stackeval/README.md @@ -39,9 +39,9 @@ There are various pairs of types in this package that represent a static object in the configuration and dynamic instances of that object respectively. For example, `InputVariableConfig` directly represents a `variable` block -from a `.tfstack.hcl` file, while `InputVariable` represents the possibly-many -dynamic instances of that object that can be caused by being within a stack -that was called using `for_each`. +from a `.tfcomponent.hcl` file, while `InputVariable` represents the +possibly-many dynamic instances of that object that can be caused by being +within a stack that was called using `for_each`. In general, the static types are responsible for "static validation"-type tasks, such as checking whether expressions refer to instances of other configuration