From 3b42e0a1d1b9b37447e14e3af714061e9752b01c Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Fri, 3 Jan 2025 09:26:32 +0100 Subject: [PATCH 01/19] releases: trigger the build action only after the unit tests (#36254) --- .github/workflows/build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f34833589156..6e7c47a35b8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,13 +7,17 @@ name: build on: workflow_dispatch: - push: + workflow_run: + workflows: ["Quick Checks"] + types: + - completed branches: - main - 'v[0-9]+.[0-9]+' - releng/** - tsccr-auto-pinning/** - dependabot/** + push: tags: - 'v[0-9]+.[0-9]+.[0-9]+*' @@ -27,6 +31,7 @@ permissions: jobs: get-product-version: name: "Determine intended Terraform version" + if: ${{ github.event.action != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest outputs: product-version: ${{ steps.get-product-version.outputs.product-version }} From a4e1e60a011716282569082986b3ca627d8a91cc Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Fri, 3 Jan 2025 11:28:19 +0100 Subject: [PATCH 02/19] logging: add trace statements when changes are ignored (#36259) * logging: add trace statements when changes are ignored * copywrite headers --- internal/backend/local/backend_apply.go | 3 +- internal/command/views/json/diagnostic.go | 134 +---------------- internal/terraform/eval_context_builtin.go | 2 +- .../node_resource_abstract_instance.go | 28 +++- internal/terraform/node_resource_validate.go | 2 +- internal/terraform/reduce_plan_test.go | 3 +- internal/tfdiags/format.go | 136 ++++++++++++++++++ 7 files changed, 167 insertions(+), 141 deletions(-) create mode 100644 internal/tfdiags/format.go diff --git a/internal/backend/local/backend_apply.go b/internal/backend/local/backend_apply.go index 4b9765d1b348..cb8bd0268180 100644 --- a/internal/backend/local/backend_apply.go +++ b/internal/backend/local/backend_apply.go @@ -16,7 +16,6 @@ import ( "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/backend/backendrun" "github.com/hashicorp/terraform/internal/command/views" - viewsjson "github.com/hashicorp/terraform/internal/command/views/json" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/logging" "github.com/hashicorp/terraform/internal/plans" @@ -348,7 +347,7 @@ func (b *Local) opApply( diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Can't change variable when applying a saved plan", - Detail: fmt.Sprintf("The variable %s cannot be set using the -var and -var-file options when applying a saved plan file, because a saved plan includes the variable values that were set when it was created. The saved plan specifies %s as the value whereas during apply the value %s was %s. To declare an ephemeral variable which is not saved in the plan file, use ephemeral = true.", varName, viewsjson.CompactValueStr(parsedVar.Value), viewsjson.CompactValueStr(plannedVar), parsedVar.SourceType.DiagnosticLabel()), + Detail: fmt.Sprintf("The variable %s cannot be set using the -var and -var-file options when applying a saved plan file, because a saved plan includes the variable values that were set when it was created. The saved plan specifies %s as the value whereas during apply the value %s was %s. To declare an ephemeral variable which is not saved in the plan file, use ephemeral = true.", varName, tfdiags.CompactValueStr(parsedVar.Value), tfdiags.CompactValueStr(plannedVar), parsedVar.SourceType.DiagnosticLabel()), Subject: rng, }) } diff --git a/internal/command/views/json/diagnostic.go b/internal/command/views/json/diagnostic.go index 4d4032814a08..daef06c1b186 100644 --- a/internal/command/views/json/diagnostic.go +++ b/internal/command/views/json/diagnostic.go @@ -5,7 +5,6 @@ package json import ( "bufio" - "bytes" "fmt" "sort" "strings" @@ -14,9 +13,10 @@ import ( "github.com/hashicorp/hcl/v2/hcled" "github.com/hashicorp/hcl/v2/hclparse" "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/tfdiags" - "github.com/zclconf/go-cty/cty" ) // These severities map to the tfdiags.Severity values, plus an explicit @@ -289,7 +289,7 @@ func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnost continue } - traversalStr := traversalStr(traversal) + traversalStr := tfdiags.TraversalStr(traversal) if _, exists := seen[traversalStr]; exists { continue Traversals // don't show duplicates when the same variable is referenced multiple times } @@ -382,7 +382,7 @@ func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnost value.Statement = "will be known only after apply" } default: - value.Statement = fmt.Sprintf("is %s", compactValueStr(val)) + value.Statement = fmt.Sprintf("is %s", tfdiags.CompactValueStr(val)) } values = append(values, value) seen[traversalStr] = struct{}{} @@ -437,129 +437,3 @@ func parseRange(src []byte, rng hcl.Range) (*hcl.File, int) { return file, offset } - -// CompactValueStr produces a compact, single-line summary of a given value -// that is suitable for display in the UI. -// -// For primitives it returns a full representation, while for more complex -// types it instead summarizes the type, size, etc to produce something -// that is hopefully still somewhat useful but not as verbose as a rendering -// of the entire data structure. -func CompactValueStr(val cty.Value) string { - return compactValueStr(val) -} - -func compactValueStr(val cty.Value) string { - // This is a specialized subset of value rendering tailored to producing - // helpful but concise messages in diagnostics. It is not comprehensive - // nor intended to be used for other purposes. - - val, valMarks := val.Unmark() - for mark := range valMarks { - switch mark { - case marks.Sensitive: - // We check this in here just to make sure, but note that the caller - // of compactValueStr ought to have already checked this and skipped - // calling into compactValueStr anyway, so this shouldn't actually - // be reachable. - return "(sensitive value)" - case marks.Ephemeral: - // A non-sensitive ephemeral value is fine to show in the UI. Values - // that are both ephemeral and sensitive should have both markings - // and should therefore get caught by the marks.Sensitive case - // above. - continue - default: - // We don't know about any other marks, so we'll be conservative. - // This shouldn't actuallyr eachable since the caller should've - // checked this and skipped calling compactValueStr anyway. - return "value with unrecognized marks (this is a bug in Terraform)" - } - } - - // WARNING: We've only checked that the value isn't sensitive _shallowly_ - // here, and so we must never show any element values from complex types - // in here. However, it's fine to show map keys and attribute names because - // those are never sensitive in isolation: the entire value would be - // sensitive in that case. - - ty := val.Type() - switch { - case val.IsNull(): - return "null" - case !val.IsKnown(): - // Should never happen here because we should filter before we get - // in here, but we'll do something reasonable rather than panic. - return "(not yet known)" - case ty == cty.Bool: - if val.True() { - return "true" - } - return "false" - case ty == cty.Number: - bf := val.AsBigFloat() - return bf.Text('g', 10) - case ty == cty.String: - // Go string syntax is not exactly the same as HCL native string syntax, - // but we'll accept the minor edge-cases where this is different here - // for now, just to get something reasonable here. - return fmt.Sprintf("%q", val.AsString()) - case ty.IsCollectionType() || ty.IsTupleType(): - l := val.LengthInt() - switch l { - case 0: - return "empty " + ty.FriendlyName() - case 1: - return ty.FriendlyName() + " with 1 element" - default: - return fmt.Sprintf("%s with %d elements", ty.FriendlyName(), l) - } - case ty.IsObjectType(): - atys := ty.AttributeTypes() - l := len(atys) - switch l { - case 0: - return "object with no attributes" - case 1: - var name string - for k := range atys { - name = k - } - return fmt.Sprintf("object with 1 attribute %q", name) - default: - return fmt.Sprintf("object with %d attributes", l) - } - default: - return ty.FriendlyName() - } -} - -// traversalStr produces a representation of an HCL traversal that is compact, -// resembles HCL native syntax, and is suitable for display in the UI. -func traversalStr(traversal hcl.Traversal) string { - // This is a specialized subset of traversal rendering tailored to - // producing helpful contextual messages in diagnostics. It is not - // comprehensive nor intended to be used for other purposes. - - var buf bytes.Buffer - for _, step := range traversal { - switch tStep := step.(type) { - case hcl.TraverseRoot: - buf.WriteString(tStep.Name) - case hcl.TraverseAttr: - buf.WriteByte('.') - buf.WriteString(tStep.Name) - case hcl.TraverseIndex: - buf.WriteByte('[') - if keyTy := tStep.Key.Type(); keyTy.IsPrimitiveType() { - buf.WriteString(compactValueStr(tStep.Key)) - } else { - // We'll just use a placeholder for more complex values, - // since otherwise our result could grow ridiculously long. - buf.WriteString("...") - } - buf.WriteByte(']') - } - } - return buf.String() -} diff --git a/internal/terraform/eval_context_builtin.go b/internal/terraform/eval_context_builtin.go index ec7226aac0a6..0a7610a78f49 100644 --- a/internal/terraform/eval_context_builtin.go +++ b/internal/terraform/eval_context_builtin.go @@ -407,7 +407,7 @@ func (ctx *BuiltinEvalContext) EvaluateReplaceTriggeredBy(expr hcl.Expression, r return nil, false, diags } - path := traversalToPath(ref.Remaining) + path, _ := traversalToPath(ref.Remaining) attrBefore, _ := path.Apply(change.Before) attrAfter, _ := path.Apply(change.After) diff --git a/internal/terraform/node_resource_abstract_instance.go b/internal/terraform/node_resource_abstract_instance.go index f41428104faf..e7f223e8df81 100644 --- a/internal/terraform/node_resource_abstract_instance.go +++ b/internal/terraform/node_resource_abstract_instance.go @@ -1252,7 +1252,7 @@ func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value, sch return config, nil } - ignoreChanges := traversalsToPaths(n.Config.Managed.IgnoreChanges) + ignoreChanges, keys := traversalsToPaths(n.Config.Managed.IgnoreChanges) ignoreAll := n.Config.Managed.IgnoreAllChanges if len(ignoreChanges) == 0 && !ignoreAll { @@ -1260,6 +1260,8 @@ func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value, sch } if ignoreAll { + log.Printf("[TRACE] processIgnoreChanges: Ignoring all changes for %s", n.Addr) + // Legacy providers need up to clean up their invalid plans and ensure // no changes are passed though, but that also means making an invalid // config with computed values. In that case we just don't supply a @@ -1282,6 +1284,7 @@ func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value, sch return ret, nil } + log.Printf("[TRACE] processIgnoreChanges: Ignoring changes for %s at [%s]", n.Addr, strings.Join(keys, ", ")) if prior.IsNull() || config.IsNull() { // Ignore changes doesn't apply when we're creating for the first time. @@ -1296,36 +1299,49 @@ func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value, sch // Convert the hcl.Traversal values we get form the configuration to the // cty.Path values we need to operate on the cty.Values -func traversalsToPaths(traversals []hcl.Traversal) []cty.Path { +func traversalsToPaths(traversals []hcl.Traversal) ([]cty.Path, []string) { paths := make([]cty.Path, len(traversals)) + keys := make([]string, len(traversals)) for i, traversal := range traversals { - path := traversalToPath(traversal) + path, key := traversalToPath(traversal) paths[i] = path + keys[i] = key } - return paths + return paths, keys } -func traversalToPath(traversal hcl.Traversal) cty.Path { +func traversalToPath(traversal hcl.Traversal) (cty.Path, string) { path := make(cty.Path, len(traversal)) + var key strings.Builder for si, step := range traversal { switch ts := step.(type) { case hcl.TraverseRoot: path[si] = cty.GetAttrStep{ Name: ts.Name, } + key.WriteString(ts.Name) case hcl.TraverseAttr: path[si] = cty.GetAttrStep{ Name: ts.Name, } + key.WriteString(".") + key.WriteString(ts.Name) case hcl.TraverseIndex: path[si] = cty.IndexStep{ Key: ts.Key, } + if ts.Key.Type().IsPrimitiveType() { + key.WriteString("[") + key.WriteString(tfdiags.CompactValueStr(ts.Key)) + key.WriteString("]") + } else { + key.WriteString("[...]") + } default: panic(fmt.Sprintf("unsupported traversal step %#v", step)) } } - return path + return path, key.String() } func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChangesPath []cty.Path) (cty.Value, tfdiags.Diagnostics) { diff --git a/internal/terraform/node_resource_validate.go b/internal/terraform/node_resource_validate.go index 7325eaaf4e8d..42d03a9fcff3 100644 --- a/internal/terraform/node_resource_validate.go +++ b/internal/terraform/node_resource_validate.go @@ -366,7 +366,7 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag // use that to check whether the Attribute is Computed and // non-Optional. if !diags.HasErrors() { - path := traversalToPath(traversal) + path, _ := traversalToPath(traversal) attrSchema := schema.AttributeByPath(path) diff --git a/internal/terraform/reduce_plan_test.go b/internal/terraform/reduce_plan_test.go index 2148b1954b9c..142782ec97c2 100644 --- a/internal/terraform/reduce_plan_test.go +++ b/internal/terraform/reduce_plan_test.go @@ -433,7 +433,8 @@ func TestProcessIgnoreChangesIndividual(t *testing.T) { ignore[i] = trav } - ret, diags := processIgnoreChangesIndividual(test.Old, test.New, traversalsToPaths(ignore)) + paths, _ := traversalsToPaths(ignore) + ret, diags := processIgnoreChangesIndividual(test.Old, test.New, paths) if diags.HasErrors() { t.Fatal(diags.Err()) } diff --git a/internal/tfdiags/format.go b/internal/tfdiags/format.go new file mode 100644 index 000000000000..c7e6bdc97cc3 --- /dev/null +++ b/internal/tfdiags/format.go @@ -0,0 +1,136 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package tfdiags + +import ( + "bytes" + "fmt" + + "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/terraform/internal/lang/marks" +) + +// CompactValueStr produces a compact, single-line summary of a given value +// that is suitable for display in the UI. +// +// For primitives it returns a full representation, while for more complex +// types it instead summarizes the type, size, etc to produce something +// that is hopefully still somewhat useful but not as verbose as a rendering +// of the entire data structure. +func CompactValueStr(val cty.Value) string { + // This is a specialized subset of value rendering tailored to producing + // helpful but concise messages in diagnostics. It is not comprehensive + // nor intended to be used for other purposes. + + val, valMarks := val.Unmark() + for mark := range valMarks { + switch mark { + case marks.Sensitive: + // We check this in here just to make sure, but note that the caller + // of compactValueStr ought to have already checked this and skipped + // calling into compactValueStr anyway, so this shouldn't actually + // be reachable. + return "(sensitive value)" + case marks.Ephemeral: + // A non-sensitive ephemeral value is fine to show in the UI. Values + // that are both ephemeral and sensitive should have both markings + // and should therefore get caught by the marks.Sensitive case + // above. + continue + default: + // We don't know about any other marks, so we'll be conservative. + // This shouldn't actuallyr eachable since the caller should've + // checked this and skipped calling compactValueStr anyway. + return "value with unrecognized marks (this is a bug in Terraform)" + } + } + + // WARNING: We've only checked that the value isn't sensitive _shallowly_ + // here, and so we must never show any element values from complex types + // in here. However, it's fine to show map keys and attribute names because + // those are never sensitive in isolation: the entire value would be + // sensitive in that case. + + ty := val.Type() + switch { + case val.IsNull(): + return "null" + case !val.IsKnown(): + // Should never happen here because we should filter before we get + // in here, but we'll do something reasonable rather than panic. + return "(not yet known)" + case ty == cty.Bool: + if val.True() { + return "true" + } + return "false" + case ty == cty.Number: + bf := val.AsBigFloat() + return bf.Text('g', 10) + case ty == cty.String: + // Go string syntax is not exactly the same as HCL native string syntax, + // but we'll accept the minor edge-cases where this is different here + // for now, just to get something reasonable here. + return fmt.Sprintf("%q", val.AsString()) + case ty.IsCollectionType() || ty.IsTupleType(): + l := val.LengthInt() + switch l { + case 0: + return "empty " + ty.FriendlyName() + case 1: + return ty.FriendlyName() + " with 1 element" + default: + return fmt.Sprintf("%s with %d elements", ty.FriendlyName(), l) + } + case ty.IsObjectType(): + atys := ty.AttributeTypes() + l := len(atys) + switch l { + case 0: + return "object with no attributes" + case 1: + var name string + for k := range atys { + name = k + } + return fmt.Sprintf("object with 1 attribute %q", name) + default: + return fmt.Sprintf("object with %d attributes", l) + } + default: + return ty.FriendlyName() + } +} + +// TraversalStr produces a representation of an HCL traversal that is compact, +// resembles HCL native syntax, and is suitable for display in the UI. +func TraversalStr(traversal hcl.Traversal) string { + // This is a specialized subset of traversal rendering tailored to + // producing helpful contextual messages in diagnostics. It is not + // comprehensive nor intended to be used for other purposes. + + var buf bytes.Buffer + for _, step := range traversal { + switch tStep := step.(type) { + case hcl.TraverseRoot: + buf.WriteString(tStep.Name) + case hcl.TraverseAttr: + buf.WriteByte('.') + buf.WriteString(tStep.Name) + case hcl.TraverseIndex: + buf.WriteByte('[') + if keyTy := tStep.Key.Type(); keyTy.IsPrimitiveType() { + buf.WriteString(CompactValueStr(tStep.Key)) + } else { + // We'll just use a placeholder for more complex values, + // since otherwise our result could grow ridiculously long. + buf.WriteString("...") + } + buf.WriteByte(']') + } + } + return buf.String() +} From fa27595fe344f11753f7cdb2ed4a3912296e67bd Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Mon, 6 Jan 2025 12:53:45 +0100 Subject: [PATCH 03/19] Update go-cty to 1.16.0 (#36262) * Update go-cty to 1.16.0 * syndeps --- go.mod | 2 +- go.sum | 4 ++-- internal/backend/remote-state/azure/go.mod | 2 +- internal/backend/remote-state/azure/go.sum | 4 ++-- internal/backend/remote-state/consul/go.mod | 2 +- internal/backend/remote-state/consul/go.sum | 4 ++-- internal/backend/remote-state/cos/go.mod | 2 +- internal/backend/remote-state/cos/go.sum | 4 ++-- internal/backend/remote-state/gcs/go.mod | 2 +- internal/backend/remote-state/gcs/go.sum | 4 ++-- internal/backend/remote-state/kubernetes/go.mod | 2 +- internal/backend/remote-state/kubernetes/go.sum | 4 ++-- internal/backend/remote-state/oss/go.mod | 2 +- internal/backend/remote-state/oss/go.sum | 4 ++-- internal/backend/remote-state/pg/go.mod | 2 +- internal/backend/remote-state/pg/go.sum | 4 ++-- internal/backend/remote-state/s3/go.mod | 2 +- internal/backend/remote-state/s3/go.sum | 4 ++-- internal/legacy/go.mod | 2 +- internal/legacy/go.sum | 4 ++-- 20 files changed, 30 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 9ec3fb8ba69e..c22f4c7f5faa 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/spf13/afero v1.9.5 github.com/xanzy/ssh-agent v0.3.3 github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 - github.com/zclconf/go-cty v1.15.1 + github.com/zclconf/go-cty v1.16.0 github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 github.com/zclconf/go-cty-yaml v1.1.0 go.opentelemetry.io/contrib/exporters/autoexport v0.45.0 diff --git a/go.sum b/go.sum index 0b78ca405213..d45696b2d3e3 100644 --- a/go.sum +++ b/go.sum @@ -1023,8 +1023,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0= diff --git a/internal/backend/remote-state/azure/go.mod b/internal/backend/remote-state/azure/go.mod index 62575dfae700..2abd8e9133d2 100644 --- a/internal/backend/remote-state/azure/go.mod +++ b/internal/backend/remote-state/azure/go.mod @@ -50,7 +50,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/stretchr/testify v1.8.4 // indirect - github.com/zclconf/go-cty v1.15.1 // indirect + github.com/zclconf/go-cty v1.16.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.29.0 // indirect diff --git a/internal/backend/remote-state/azure/go.sum b/internal/backend/remote-state/azure/go.sum index 1513ecb5cee8..5daaf4cc65b9 100644 --- a/internal/backend/remote-state/azure/go.sum +++ b/internal/backend/remote-state/azure/go.sum @@ -304,8 +304,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/backend/remote-state/consul/go.mod b/internal/backend/remote-state/consul/go.mod index 4360b2f79d4b..434d0ac38681 100644 --- a/internal/backend/remote-state/consul/go.mod +++ b/internal/backend/remote-state/consul/go.mod @@ -6,7 +6,7 @@ require ( github.com/hashicorp/consul/api v1.13.0 github.com/hashicorp/consul/sdk v0.8.0 github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000 - github.com/zclconf/go-cty v1.15.1 + github.com/zclconf/go-cty v1.16.0 ) require ( diff --git a/internal/backend/remote-state/consul/go.sum b/internal/backend/remote-state/consul/go.sum index 8e99c85a4385..2bab69642f49 100644 --- a/internal/backend/remote-state/consul/go.sum +++ b/internal/backend/remote-state/consul/go.sum @@ -302,8 +302,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/backend/remote-state/cos/go.mod b/internal/backend/remote-state/cos/go.mod index 71f42d47e32f..a4a51bff0956 100644 --- a/internal/backend/remote-state/cos/go.mod +++ b/internal/backend/remote-state/cos/go.mod @@ -32,7 +32,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mozillazg/go-httpheader v0.3.0 // indirect github.com/spf13/afero v1.9.5 // indirect - github.com/zclconf/go-cty v1.15.1 // indirect + github.com/zclconf/go-cty v1.16.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.10.0 // indirect diff --git a/internal/backend/remote-state/cos/go.sum b/internal/backend/remote-state/cos/go.sum index 5a7badeb36c5..dce6281724c7 100644 --- a/internal/backend/remote-state/cos/go.sum +++ b/internal/backend/remote-state/cos/go.sum @@ -243,8 +243,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/backend/remote-state/gcs/go.mod b/internal/backend/remote-state/gcs/go.mod index 844b9b5abfa8..211c4bffb519 100644 --- a/internal/backend/remote-state/gcs/go.mod +++ b/internal/backend/remote-state/gcs/go.mod @@ -7,7 +7,7 @@ require ( cloud.google.com/go/storage v1.30.1 github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000 github.com/mitchellh/go-homedir v1.1.0 - github.com/zclconf/go-cty v1.15.1 + github.com/zclconf/go-cty v1.16.0 golang.org/x/oauth2 v0.23.0 google.golang.org/api v0.126.0 google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d diff --git a/internal/backend/remote-state/gcs/go.sum b/internal/backend/remote-state/gcs/go.sum index a1a0450aac6b..03722522230d 100644 --- a/internal/backend/remote-state/gcs/go.sum +++ b/internal/backend/remote-state/gcs/go.sum @@ -240,8 +240,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/backend/remote-state/kubernetes/go.mod b/internal/backend/remote-state/kubernetes/go.mod index a8d7405d9c26..0e43ee2c7483 100644 --- a/internal/backend/remote-state/kubernetes/go.mod +++ b/internal/backend/remote-state/kubernetes/go.mod @@ -5,7 +5,7 @@ go 1.23.3 require ( github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000 github.com/mitchellh/go-homedir v1.1.0 - github.com/zclconf/go-cty v1.15.1 + github.com/zclconf/go-cty v1.16.0 k8s.io/api v0.25.5 k8s.io/apimachinery v0.25.5 k8s.io/client-go v0.25.5 diff --git a/internal/backend/remote-state/kubernetes/go.sum b/internal/backend/remote-state/kubernetes/go.sum index eff25bd9b50a..7794284e37f2 100644 --- a/internal/backend/remote-state/kubernetes/go.sum +++ b/internal/backend/remote-state/kubernetes/go.sum @@ -307,8 +307,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/backend/remote-state/oss/go.mod b/internal/backend/remote-state/oss/go.mod index 6382f6934240..4b0c1074d9bc 100644 --- a/internal/backend/remote-state/oss/go.mod +++ b/internal/backend/remote-state/oss/go.mod @@ -39,7 +39,7 @@ require ( github.com/satori/go.uuid v1.2.0 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/stretchr/testify v1.8.4 // indirect - github.com/zclconf/go-cty v1.15.1 // indirect + github.com/zclconf/go-cty v1.16.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.10.0 // indirect diff --git a/internal/backend/remote-state/oss/go.sum b/internal/backend/remote-state/oss/go.sum index ce4c7352f597..fa0a02b91763 100644 --- a/internal/backend/remote-state/oss/go.sum +++ b/internal/backend/remote-state/oss/go.sum @@ -259,8 +259,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/backend/remote-state/pg/go.mod b/internal/backend/remote-state/pg/go.mod index d543ebb24f40..94beecdce112 100644 --- a/internal/backend/remote-state/pg/go.mod +++ b/internal/backend/remote-state/pg/go.mod @@ -7,7 +7,7 @@ require ( github.com/hashicorp/hcl/v2 v2.23.0 github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000 github.com/lib/pq v1.10.3 - github.com/zclconf/go-cty v1.15.1 + github.com/zclconf/go-cty v1.16.0 ) require ( diff --git a/internal/backend/remote-state/pg/go.sum b/internal/backend/remote-state/pg/go.sum index 99cb45ea603d..e30a0914f48d 100644 --- a/internal/backend/remote-state/pg/go.sum +++ b/internal/backend/remote-state/pg/go.sum @@ -217,8 +217,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/backend/remote-state/s3/go.mod b/internal/backend/remote-state/s3/go.mod index 3702cb7114f7..b7b6b20c49e6 100644 --- a/internal/backend/remote-state/s3/go.mod +++ b/internal/backend/remote-state/s3/go.mod @@ -15,7 +15,7 @@ require ( github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/hcl/v2 v2.23.0 github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000 - github.com/zclconf/go-cty v1.15.1 + github.com/zclconf/go-cty v1.16.0 ) require ( diff --git a/internal/backend/remote-state/s3/go.sum b/internal/backend/remote-state/s3/go.sum index b5f89ffaa101..54b3f1e2d18f 100644 --- a/internal/backend/remote-state/s3/go.sum +++ b/internal/backend/remote-state/s3/go.sum @@ -287,8 +287,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/legacy/go.mod b/internal/legacy/go.mod index 47bdb4fff347..7d3634151515 100644 --- a/internal/legacy/go.mod +++ b/internal/legacy/go.mod @@ -11,7 +11,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/reflectwalk v1.0.2 - github.com/zclconf/go-cty v1.15.1 + github.com/zclconf/go-cty v1.16.0 ) require ( diff --git a/internal/legacy/go.sum b/internal/legacy/go.sum index 4650a5e0ea53..104ad0e19c92 100644 --- a/internal/legacy/go.sum +++ b/internal/legacy/go.sum @@ -219,8 +219,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.15.1 h1:RgQYm4j2EvoBRXOPxhUvxPzRrGDo1eCOhHXuGfrj5S0= -github.com/zclconf/go-cty v1.15.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= From 908828be8a14e5efc97c7d5dfb4beabff17b85a7 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 6 Jan 2025 15:25:24 +0000 Subject: [PATCH 04/19] cmd/modules: Ensure modules are sorted by reference key (#36268) --- internal/command/views/modules.go | 5 +++++ internal/moduleref/record.go | 16 ++++++++++++++-- internal/moduleref/resolver.go | 2 +- internal/moduleref/resolver_test.go | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/internal/command/views/modules.go b/internal/command/views/modules.go index 5ce57cad2d46..8fa3cd8a19da 100644 --- a/internal/command/views/modules.go +++ b/internal/command/views/modules.go @@ -6,6 +6,7 @@ package views import ( encJson "encoding/json" "fmt" + "sort" "github.com/hashicorp/terraform/internal/command/arguments" "github.com/hashicorp/terraform/internal/moduleref" @@ -44,6 +45,10 @@ func (v *ModulesHuman) Display(manifest moduleref.Manifest) int { return 0 } printRoot := treeprint.New() + + // ensure output is deterministic + sort.Sort(manifest.Records) + populateTreeNode(printRoot, &moduleref.Record{ Children: manifest.Records, }) diff --git a/internal/moduleref/record.go b/internal/moduleref/record.go index fb4f918226c4..cd19ef0d6196 100644 --- a/internal/moduleref/record.go +++ b/internal/moduleref/record.go @@ -17,14 +17,14 @@ type Record struct { Source addrs.ModuleSource Version *version.Version VersionConstraints version.Constraints - Children []*Record + Children Records } // ModuleRecordManifest is the view implementation of module entries declared // in configuration type Manifest struct { FormatVersion string - Records []*Record + Records Records } func (m *Manifest) addModuleEntry(entry *Record) { @@ -34,3 +34,15 @@ func (m *Manifest) addModuleEntry(entry *Record) { func (r *Record) addChild(child *Record) { r.Children = append(r.Children, child) } + +type Records []*Record + +func (r Records) Len() int { + return len(r) +} +func (r Records) Less(i, j int) bool { + return r[i].Key < r[j].Key +} +func (r Records) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} diff --git a/internal/moduleref/resolver.go b/internal/moduleref/resolver.go index f85ddba4a158..d66ce6d5661e 100644 --- a/internal/moduleref/resolver.go +++ b/internal/moduleref/resolver.go @@ -36,7 +36,7 @@ func NewResolver(internalManifest modsdir.Manifest) *Resolver { internalManifest: internalManifestCopy, manifest: &Manifest{ FormatVersion: FormatVersion, - Records: []*Record{}, + Records: Records{}, }, } } diff --git a/internal/moduleref/resolver_test.go b/internal/moduleref/resolver_test.go index eaa9f0a92574..1075252e01c2 100644 --- a/internal/moduleref/resolver_test.go +++ b/internal/moduleref/resolver_test.go @@ -185,7 +185,7 @@ func TestResolver_ResolveNestedChildren(t *testing.T) { } } -func countAndListSources(records []*Record) (count int, sources []string) { +func countAndListSources(records Records) (count int, sources []string) { for _, record := range records { sources = append(sources, record.Source.String()) count++ From 0ff1b77afa6807d94fd8001c8583d645c3181d16 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Mon, 6 Jan 2025 16:10:14 +0100 Subject: [PATCH 05/19] standardize client capabilities --- internal/grpcwrap/provider.go | 1 + .../internal/stackeval/client_capabilities.go | 15 ++++ .../internal/stackeval/provider_instance.go | 9 +- .../stackeval/provider_instance_test.go | 10 +-- internal/terraform/eval_context.go | 4 + internal/terraform/eval_context_builtin.go | 7 ++ internal/terraform/eval_context_mock.go | 7 ++ .../node_resource_abstract_instance.go | 83 ++++++++----------- internal/terraform/node_resource_import.go | 9 +- .../terraform/node_resource_plan_instance.go | 9 +- internal/terraform/node_resource_validate.go | 8 +- 11 files changed, 81 insertions(+), 81 deletions(-) create mode 100644 internal/stacks/stackruntime/internal/stackeval/client_capabilities.go diff --git a/internal/grpcwrap/provider.go b/internal/grpcwrap/provider.go index 705e7b1ed1f3..f8644381ea7c 100644 --- a/internal/grpcwrap/provider.go +++ b/internal/grpcwrap/provider.go @@ -122,6 +122,7 @@ func (p *provider) ValidateResourceTypeConfig(_ context.Context, req *tfplugin5. TypeName: req.TypeName, Config: configVal, ClientCapabilities: providers.ClientCapabilities{ + DeferralAllowed: true, WriteOnlyAttributesAllowed: true, }, }) diff --git a/internal/stacks/stackruntime/internal/stackeval/client_capabilities.go b/internal/stacks/stackruntime/internal/stackeval/client_capabilities.go new file mode 100644 index 000000000000..15202fbb6a5f --- /dev/null +++ b/internal/stacks/stackruntime/internal/stackeval/client_capabilities.go @@ -0,0 +1,15 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package stackeval + +import "github.com/hashicorp/terraform/internal/providers" + +// ClientCapabilities returns the client capabilities sent to the providers +// for each request. They define what this terraform instance is capable of. +func ClientCapabilities() providers.ClientCapabilities { + return providers.ClientCapabilities{ + DeferralAllowed: true, + WriteOnlyAttributesAllowed: true, + } +} diff --git a/internal/stacks/stackruntime/internal/stackeval/provider_instance.go b/internal/stacks/stackruntime/internal/stackeval/provider_instance.go index 2ddc8e434fd7..7c8e0204f96d 100644 --- a/internal/stacks/stackruntime/internal/stackeval/provider_instance.go +++ b/internal/stacks/stackruntime/internal/stackeval/provider_instance.go @@ -256,12 +256,9 @@ func (p *ProviderInstance) CheckClient(ctx context.Context, phase EvalPhase) (pr } resp := client.ConfigureProvider(providers.ConfigureProviderRequest{ - TerraformVersion: version.SemVer.String(), - Config: unmarkedArgs, - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: true, - WriteOnlyAttributesAllowed: true, - }, + TerraformVersion: version.SemVer.String(), + Config: unmarkedArgs, + ClientCapabilities: ClientCapabilities(), }) diags = diags.Append(resp.Diagnostics) if resp.Diagnostics.HasErrors() { diff --git a/internal/stacks/stackruntime/internal/stackeval/provider_instance_test.go b/internal/stacks/stackruntime/internal/stackeval/provider_instance_test.go index 1339b39916ba..6ca58c691d60 100644 --- a/internal/stacks/stackruntime/internal/stackeval/provider_instance_test.go +++ b/internal/stacks/stackruntime/internal/stackeval/provider_instance_test.go @@ -383,10 +383,7 @@ func TestProviderInstanceCheckClient(t *testing.T) { Config: cty.ObjectVal(map[string]cty.Value{ "test": cty.StringVal("yep"), }), - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: true, - WriteOnlyAttributesAllowed: true, - }, + ClientCapabilities: ClientCapabilities(), } if diff := cmp.Diff(want, got, ctydebug.CmpOptions); diff != "" { t.Errorf("wrong request\n%s", diff) @@ -426,10 +423,7 @@ func TestProviderInstanceCheckClient(t *testing.T) { Config: cty.ObjectVal(map[string]cty.Value{ "test": cty.StringVal("yep"), }), - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: true, - WriteOnlyAttributesAllowed: true, - }, + ClientCapabilities: ClientCapabilities(), } if diff := cmp.Diff(want, got, ctydebug.CmpOptions); diff != "" { t.Errorf("wrong request\n%s", diff) diff --git a/internal/terraform/eval_context.go b/internal/terraform/eval_context.go index 30ed37e928ac..bde1626ca3db 100644 --- a/internal/terraform/eval_context.go +++ b/internal/terraform/eval_context.go @@ -191,6 +191,10 @@ type EvalContext interface { // perform a planned action due to an upstream dependency being deferred. Deferrals() *deferring.Deferred + // ClientCapabilities returns the client capabilities sent to the providers + // for each request. They define what this terraform instance is capable of. + ClientCapabilities() providers.ClientCapabilities + // MoveResults returns a map describing the results of handling any // resource instance move statements prior to the graph walk, so that // the graph walk can then record that information appropriately in other diff --git a/internal/terraform/eval_context_builtin.go b/internal/terraform/eval_context_builtin.go index 0a7610a78f49..c9950f608d1b 100644 --- a/internal/terraform/eval_context_builtin.go +++ b/internal/terraform/eval_context_builtin.go @@ -613,3 +613,10 @@ func (ctx *BuiltinEvalContext) Forget() bool { func (ctx *BuiltinEvalContext) EphemeralResources() *ephemeral.Resources { return ctx.EphemeralResourcesValue } + +func (ctx *BuiltinEvalContext) ClientCapabilities() providers.ClientCapabilities { + return providers.ClientCapabilities{ + DeferralAllowed: ctx.Deferrals().DeferralAllowed(), + WriteOnlyAttributesAllowed: true, + } +} diff --git a/internal/terraform/eval_context_mock.go b/internal/terraform/eval_context_mock.go index 47daaceadbff..bd2edf2031b7 100644 --- a/internal/terraform/eval_context_mock.go +++ b/internal/terraform/eval_context_mock.go @@ -424,3 +424,10 @@ func (c *MockEvalContext) Forget() bool { c.ForgetCalled = true return c.ForgetValues } + +func (ctx *MockEvalContext) ClientCapabilities() providers.ClientCapabilities { + return providers.ClientCapabilities{ + DeferralAllowed: ctx.Deferrals().DeferralAllowed(), + WriteOnlyAttributesAllowed: true, + } +} diff --git a/internal/terraform/node_resource_abstract_instance.go b/internal/terraform/node_resource_abstract_instance.go index e7f223e8df81..6d721cc85311 100644 --- a/internal/terraform/node_resource_abstract_instance.go +++ b/internal/terraform/node_resource_abstract_instance.go @@ -432,16 +432,13 @@ func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState // Allow the provider to check the destroy plan, and insert any // necessary private data. resp = provider.PlanResourceChange(providers.PlanResourceChangeRequest{ - TypeName: n.Addr.Resource.Resource.Type, - Config: nullVal, - PriorState: unmarkedPriorVal, - ProposedNewState: nullVal, - PriorPrivate: currentState.Private, - ProviderMeta: metaConfigVal, - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: deferralAllowed, - WriteOnlyAttributesAllowed: true, - }, + TypeName: n.Addr.Resource.Resource.Type, + Config: nullVal, + PriorState: unmarkedPriorVal, + ProposedNewState: nullVal, + PriorPrivate: currentState.Private, + ProviderMeta: metaConfigVal, + ClientCapabilities: ctx.ClientCapabilities(), }) deferred = resp.Deferred @@ -635,14 +632,11 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state } } else { resp = provider.ReadResource(providers.ReadResourceRequest{ - TypeName: n.Addr.Resource.Resource.Type, - PriorState: priorVal, - Private: state.Private, - ProviderMeta: metaConfigVal, - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: deferralAllowed, - WriteOnlyAttributesAllowed: true, - }, + TypeName: n.Addr.Resource.Resource.Type, + PriorState: priorVal, + Private: state.Private, + ProviderMeta: metaConfigVal, + ClientCapabilities: ctx.ClientCapabilities(), }) // If we don't support deferrals, but the provider reports a deferral and does not @@ -863,11 +857,9 @@ func (n *NodeAbstractResourceInstance) plan( unmarkedConfigVal, _ := origConfigVal.UnmarkDeep() validateResp := provider.ValidateResourceConfig( providers.ValidateResourceConfigRequest{ - TypeName: n.Addr.Resource.Resource.Type, - Config: unmarkedConfigVal, - ClientCapabilities: providers.ClientCapabilities{ - WriteOnlyAttributesAllowed: true, - }, + TypeName: n.Addr.Resource.Resource.Type, + Config: unmarkedConfigVal, + ClientCapabilities: ctx.ClientCapabilities(), }, ) diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String())) @@ -926,16 +918,13 @@ func (n *NodeAbstractResourceInstance) plan( } } else { resp = provider.PlanResourceChange(providers.PlanResourceChangeRequest{ - TypeName: n.Addr.Resource.Resource.Type, - Config: unmarkedConfigVal, - PriorState: unmarkedPriorVal, - ProposedNewState: proposedNewVal, - PriorPrivate: priorPrivate, - ProviderMeta: metaConfigVal, - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: deferralAllowed, - WriteOnlyAttributesAllowed: true, - }, + TypeName: n.Addr.Resource.Resource.Type, + Config: unmarkedConfigVal, + PriorState: unmarkedPriorVal, + ProposedNewState: proposedNewVal, + PriorPrivate: priorPrivate, + ProviderMeta: metaConfigVal, + ClientCapabilities: ctx.ClientCapabilities(), }) // If we don't support deferrals, but the provider reports a deferral and does not // emit any error level diagnostics, we should emit an error. @@ -1100,16 +1089,13 @@ func (n *NodeAbstractResourceInstance) plan( } } else { resp = provider.PlanResourceChange(providers.PlanResourceChangeRequest{ - TypeName: n.Addr.Resource.Resource.Type, - Config: unmarkedConfigVal, - PriorState: nullPriorVal, - ProposedNewState: proposedNewVal, - PriorPrivate: plannedPrivate, - ProviderMeta: metaConfigVal, - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: deferralAllowed, - WriteOnlyAttributesAllowed: true, - }, + TypeName: n.Addr.Resource.Resource.Type, + Config: unmarkedConfigVal, + PriorState: nullPriorVal, + ProposedNewState: proposedNewVal, + PriorPrivate: plannedPrivate, + ProviderMeta: metaConfigVal, + ClientCapabilities: ctx.ClientCapabilities(), }) // If we don't support deferrals, but the provider reports a deferral and does not @@ -1567,13 +1553,10 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal } } else { resp = provider.ReadDataSource(providers.ReadDataSourceRequest{ - TypeName: n.Addr.ContainingResource().Resource.Type, - Config: configVal, - ProviderMeta: metaConfigVal, - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: deferralAllowed, - WriteOnlyAttributesAllowed: true, - }, + TypeName: n.Addr.ContainingResource().Resource.Type, + Config: configVal, + ProviderMeta: metaConfigVal, + ClientCapabilities: ctx.ClientCapabilities(), }) // If we don't support deferrals, but the provider reports a deferral and does not diff --git a/internal/terraform/node_resource_import.go b/internal/terraform/node_resource_import.go index 5aefb9f28925..fe4339cff705 100644 --- a/internal/terraform/node_resource_import.go +++ b/internal/terraform/node_resource_import.go @@ -103,12 +103,9 @@ func (n *graphNodeImportState) Execute(ctx EvalContext, op walkOperation) (diags } resp := provider.ImportResourceState(providers.ImportResourceStateRequest{ - TypeName: n.Addr.Resource.Resource.Type, - ID: n.ID, - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: false, - WriteOnlyAttributesAllowed: true, - }, + TypeName: n.Addr.Resource.Resource.Type, + ID: n.ID, + ClientCapabilities: ctx.ClientCapabilities(), }) diags = diags.Append(resp.Diagnostics) if diags.HasErrors() { diff --git a/internal/terraform/node_resource_plan_instance.go b/internal/terraform/node_resource_plan_instance.go index 0415b4694dd0..a7513cca646e 100644 --- a/internal/terraform/node_resource_plan_instance.go +++ b/internal/terraform/node_resource_plan_instance.go @@ -649,12 +649,9 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs. } } else { resp = provider.ImportResourceState(providers.ImportResourceStateRequest{ - TypeName: addr.Resource.Resource.Type, - ID: importId, - ClientCapabilities: providers.ClientCapabilities{ - DeferralAllowed: deferralAllowed, - WriteOnlyAttributesAllowed: true, - }, + TypeName: addr.Resource.Resource.Type, + ID: importId, + ClientCapabilities: ctx.ClientCapabilities(), }) } // If we don't support deferrals, but the provider reports a deferral and does not diff --git a/internal/terraform/node_resource_validate.go b/internal/terraform/node_resource_validate.go index 42d03a9fcff3..b102d61d8903 100644 --- a/internal/terraform/node_resource_validate.go +++ b/internal/terraform/node_resource_validate.go @@ -390,11 +390,9 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag // Use unmarked value for validate request unmarkedConfigVal, _ := configVal.UnmarkDeep() req := providers.ValidateResourceConfigRequest{ - TypeName: n.Config.Type, - Config: unmarkedConfigVal, - ClientCapabilities: providers.ClientCapabilities{ - WriteOnlyAttributesAllowed: true, - }, + TypeName: n.Config.Type, + Config: unmarkedConfigVal, + ClientCapabilities: ctx.ClientCapabilities(), } resp := provider.ValidateResourceConfig(req) From c3fb92c1ce2000615a12556307f4a3f64f3a4c61 Mon Sep 17 00:00:00 2001 From: Daniel Banck Date: Mon, 6 Jan 2025 16:51:28 +0100 Subject: [PATCH 06/19] Fix `terraform console` crash for ephemeral values (#36267) We now check if a value has an ephemeral mark before trying to format it. The check prevents us from passing a marked value to go-cty's AsString function, which leads to a crash. --- internal/repl/format.go | 3 +++ internal/repl/format_test.go | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/internal/repl/format.go b/internal/repl/format.go index b64c2d5bc90b..ebadd18873f3 100644 --- a/internal/repl/format.go +++ b/internal/repl/format.go @@ -23,6 +23,9 @@ func FormatValue(v cty.Value, indent int) string { if v.HasMark(marks.Sensitive) { return "(sensitive value)" } + if v.HasMark(marks.Ephemeral) { + return "(ephemeral value)" + } if v.IsNull() { ty := v.Type() switch { diff --git a/internal/repl/format_test.go b/internal/repl/format_test.go index e4b86fa91fce..f55f58d7cd6a 100644 --- a/internal/repl/format_test.go +++ b/internal/repl/format_test.go @@ -177,6 +177,10 @@ EOT_`, cty.StringVal("a sensitive value").Mark(marks.Sensitive), "(sensitive value)", }, + { + cty.StringVal("an ephemeral value").Mark(marks.Ephemeral), + "(ephemeral value)", + }, } for _, test := range tests { From 0e6a3ac994ce3cf46935718fa8569efd556d6c9c Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Thu, 28 Nov 2024 16:54:16 +0100 Subject: [PATCH 07/19] ephemeral: render write-only attributes in plan UI --- .../computed/renderers/write_only.go | 33 ++++ .../command/jsonformat/differ/attribute.go | 20 +- internal/command/jsonformat/differ/block.go | 7 +- .../command/jsonformat/differ/differ_test.go | 68 +++---- internal/command/jsonformat/differ/object.go | 2 +- internal/command/jsonformat/plan_test.go | 178 ++++++++++++++++++ 6 files changed, 269 insertions(+), 39 deletions(-) create mode 100644 internal/command/jsonformat/computed/renderers/write_only.go diff --git a/internal/command/jsonformat/computed/renderers/write_only.go b/internal/command/jsonformat/computed/renderers/write_only.go new file mode 100644 index 000000000000..e3f3cd95fd0e --- /dev/null +++ b/internal/command/jsonformat/computed/renderers/write_only.go @@ -0,0 +1,33 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package renderers + +import ( + "fmt" + + "github.com/hashicorp/terraform/internal/command/jsonformat/computed" +) + +var _ computed.DiffRenderer = (*writeOnlyRenderer)(nil) + +func WriteOnly(sensitive bool) computed.DiffRenderer { + return &writeOnlyRenderer{ + sensitive, + } +} + +type writeOnlyRenderer struct { + sensitive bool +} + +func (renderer writeOnlyRenderer) RenderHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string { + if renderer.sensitive { + return fmt.Sprintf("(sensitive, write-only attribute)%s%s", nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts)) + } + return fmt.Sprintf("(write-only attribute)%s%s", nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts)) +} + +func (renderer writeOnlyRenderer) WarningsHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) []string { + return []string{} +} diff --git a/internal/command/jsonformat/differ/attribute.go b/internal/command/jsonformat/differ/attribute.go index 698c3a494c3b..fb73e4a65ad3 100644 --- a/internal/command/jsonformat/differ/attribute.go +++ b/internal/command/jsonformat/differ/attribute.go @@ -4,7 +4,9 @@ package differ import ( + "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" "github.com/hashicorp/terraform/internal/command/jsonformat/structured" + "github.com/hashicorp/terraform/internal/plans" "github.com/zclconf/go-cty/cty" ctyjson "github.com/zclconf/go-cty/cty/json" @@ -13,10 +15,14 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonprovider" ) -func ComputeDiffForAttribute(change structured.Change, attribute *jsonprovider.Attribute) computed.Diff { +func ComputeDiffForAttribute(change structured.Change, attribute *jsonprovider.Attribute, blockAction plans.Action) computed.Diff { if attribute.AttributeNestedType != nil { return computeDiffForNestedAttribute(change, attribute.AttributeNestedType) } + if attribute.WriteOnly { + return computeDiffForWriteOnlyAttribute(change, blockAction) + } + return ComputeDiffForType(change, unmarshalAttribute(attribute)) } @@ -43,6 +49,18 @@ func computeDiffForNestedAttribute(change structured.Change, nested *jsonprovide } } +func computeDiffForWriteOnlyAttribute(change structured.Change, blockAction plans.Action) computed.Diff { + renderer := renderers.WriteOnly(change.IsBeforeSensitive() || change.IsAfterSensitive()) + replacePathMatches := change.ReplacePaths.Matches() + // Write-only diffs should always copy the behavior of the block they are in, except for updates + // since we don't want them to be always highlighted. + if blockAction == plans.Update { + return computed.NewDiff(renderer, plans.NoOp, replacePathMatches) + } + return computed.NewDiff(renderer, blockAction, replacePathMatches) + +} + func ComputeDiffForType(change structured.Change, ctype cty.Type) computed.Diff { if !change.NonLegacySchema { // Empty strings in blocks should be considered null, because the legacy diff --git a/internal/command/jsonformat/differ/block.go b/internal/command/jsonformat/differ/block.go index f8c855c6f8f3..b508009d8b4e 100644 --- a/internal/command/jsonformat/differ/block.go +++ b/internal/command/jsonformat/differ/block.go @@ -28,6 +28,7 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co current := change.GetDefaultActionForIteration() blockValue := change.AsMap() + blockAction := change.CalculateAction() attributes := make(map[string]computed.Diff) for key, attr := range block.Attributes { @@ -42,9 +43,9 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co childValue.BeforeExplicit = false childValue.AfterExplicit = false - childChange := ComputeDiffForAttribute(childValue, attr) - if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil { - // Don't record nil values at all in blocks. + childChange := ComputeDiffForAttribute(childValue, attr, blockAction) + if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil && !attr.WriteOnly { + // Don't record nil values at all in blocks except if they are write-only. continue } diff --git a/internal/command/jsonformat/differ/differ_test.go b/internal/command/jsonformat/differ/differ_test.go index 9a2d09940e06..5707baade6db 100644 --- a/internal/command/jsonformat/differ/differ_test.go +++ b/internal/command/jsonformat/differ/differ_test.go @@ -779,17 +779,17 @@ func TestValue_ObjectAttributes(t *testing.T) { } if tc.validateObject != nil { - tc.validateObject(t, ComputeDiffForAttribute(tc.input, attribute)) + tc.validateObject(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) return } if tc.validateSingleDiff != nil { - tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute)) + tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) return } validate := renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace) - validate(t, ComputeDiffForAttribute(tc.input, attribute)) + validate(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) }) t.Run("map", func(t *testing.T) { @@ -803,7 +803,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -811,14 +811,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) t.Run("list", func(t *testing.T) { @@ -829,7 +829,7 @@ func TestValue_ObjectAttributes(t *testing.T) { input := wrapChangeInSlice(tc.input) if tc.validateList != nil { - tc.validateList(t, ComputeDiffForAttribute(input, attribute)) + tc.validateList(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -837,7 +837,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -845,14 +845,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) t.Run("set", func(t *testing.T) { @@ -869,7 +869,7 @@ func TestValue_ObjectAttributes(t *testing.T) { ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateObject)) return ret }(), collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -877,7 +877,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -885,14 +885,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) }) @@ -914,17 +914,17 @@ func TestValue_ObjectAttributes(t *testing.T) { } if tc.validateNestedObject != nil { - tc.validateNestedObject(t, ComputeDiffForAttribute(tc.input, attribute)) + tc.validateNestedObject(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) return } if tc.validateSingleDiff != nil { - tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute)) + tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) return } validate := renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace) - validate(t, ComputeDiffForAttribute(tc.input, attribute)) + validate(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) }) t.Run("map", func(t *testing.T) { @@ -949,7 +949,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -957,14 +957,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) t.Run("list", func(t *testing.T) { @@ -989,7 +989,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -997,14 +997,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) t.Run("set", func(t *testing.T) { @@ -1032,7 +1032,7 @@ func TestValue_ObjectAttributes(t *testing.T) { ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateNestedObject)) return ret }(), collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -1040,7 +1040,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } @@ -1048,14 +1048,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) }) } @@ -1840,7 +1840,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { t.Run("direct", func(t *testing.T) { tc.validateDiff(t, ComputeDiffForAttribute(tc.input, &jsonprovider.Attribute{ AttributeType: unmarshalType(t, tc.attribute), - })) + }, plans.Update)) }) t.Run("map", func(t *testing.T) { @@ -1852,7 +1852,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) t.Run("list", func(t *testing.T) { @@ -1863,14 +1863,14 @@ func TestValue_PrimitiveAttributes(t *testing.T) { if tc.validateSliceDiffs != nil { validate := renderers.ValidateList(tc.validateSliceDiffs, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) t.Run("set", func(t *testing.T) { @@ -1881,14 +1881,14 @@ func TestValue_PrimitiveAttributes(t *testing.T) { if tc.validateSliceDiffs != nil { validate := renderers.ValidateSet(tc.validateSetDiffs, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute)) + validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) }) }) } @@ -2260,7 +2260,7 @@ func TestValue_CollectionAttributes(t *testing.T) { } t.Run(name, func(t *testing.T) { - tc.validateDiff(t, ComputeDiffForAttribute(tc.input, tc.attribute)) + tc.validateDiff(t, ComputeDiffForAttribute(tc.input, tc.attribute, plans.Update)) }) } } diff --git a/internal/command/jsonformat/differ/object.go b/internal/command/jsonformat/differ/object.go index 5698a35a7e36..1bb3d4698d9c 100644 --- a/internal/command/jsonformat/differ/object.go +++ b/internal/command/jsonformat/differ/object.go @@ -23,7 +23,7 @@ func computeAttributeDiffAsObject(change structured.Change, attributes map[strin func computeAttributeDiffAsNestedObject(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff { attributeDiffs, action := processObject(change, attributes, func(value structured.Change, attribute *jsonprovider.Attribute) computed.Diff { - return ComputeDiffForAttribute(value, attribute) + return ComputeDiffForAttribute(value, attribute, change.CalculateAction()) }) return computed.NewDiff(renderers.NestedObject(attributeDiffs), action, change.ReplacePaths.Matches()) } diff --git a/internal/command/jsonformat/plan_test.go b/internal/command/jsonformat/plan_test.go index 335a982fe0ab..5b5cd1055581 100644 --- a/internal/command/jsonformat/plan_test.go +++ b/internal/command/jsonformat/plan_test.go @@ -7452,6 +7452,184 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { runTestCases(t, testCases) } +func TestResourceChange_writeOnlyAttributes(t *testing.T) { + testCases := map[string]testCase{ + "create": { + Action: plans.Create, + Mode: addrs.ManagedResourceMode, + Before: cty.NullVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "write_only": cty.String, + "conn_info": cty.Object(map[string]cty.Type{ + "user": cty.String, + "write_only_set_password": cty.String, + }), + })), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "write_only": cty.NullVal(cty.String), + "conn_info": cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "write_only_set_password": cty.NullVal(cty.String), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "write_only": {Type: cty.String, WriteOnly: true}, + "conn_info": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "write_only_set_password": {Type: cty.String, Optional: true, Sensitive: true, WriteOnly: true}, + }, + }, + }, + }, + }, + ExpectedOutput: ` # test_instance.example will be created + + resource "test_instance" "example" { + + conn_info = { + + user = "not-secret" + + write_only_set_password = (sensitive, write-only attribute) + } + + id = "i-02ae66f368e8518a9" + + write_only = (write-only attribute) + }`, + }, + "update attribute": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "write_only": cty.NullVal(cty.String), + "conn_info": cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "write_only_set_password": cty.NullVal(cty.String), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a10"), + "write_only": cty.NullVal(cty.String), + "conn_info": cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "write_only_set_password": cty.NullVal(cty.String), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "write_only": {Type: cty.String, WriteOnly: true}, + "conn_info": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "write_only_set_password": {Type: cty.String, Optional: true, Sensitive: true, WriteOnly: true}, + }, + }, + }, + }, + }, + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ id = "i-02ae66f368e8518a9" -> "i-02ae66f368e8518a10" + # (2 unchanged attributes hidden) + }`, + }, + "update - delete block": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "write_only": cty.NullVal(cty.String), + "conn_info": cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "write_only_set_password": cty.NullVal(cty.String), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "write_only": cty.NullVal(cty.String), + "conn_info": cty.NullVal(cty.Object(map[string]cty.Type{ + "user": cty.String, + "write_only_set_password": cty.String, + })), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "write_only": {Type: cty.String, WriteOnly: true}, + "conn_info": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "write_only_set_password": {Type: cty.String, Optional: true, Sensitive: true, WriteOnly: true}, + }, + }, + }, + }, + }, + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + - conn_info = { + - user = "not-secret" -> null + - write_only_set_password = (sensitive, write-only attribute) -> null + } -> null + id = "i-02ae66f368e8518a9" + # (1 unchanged attribute hidden) + }`, + }, + "delete": { + Action: plans.Delete, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "write_only": cty.NullVal(cty.String), + "conn_info": cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "write_only_set_password": cty.NullVal(cty.String), + }), + }), + After: cty.NullVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "write_only": cty.String, + "conn_info": cty.Object(map[string]cty.Type{ + "user": cty.String, + "write_only_set_password": cty.String, + }), + })), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "write_only": {Type: cty.String, WriteOnly: true}, + "conn_info": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "write_only_set_password": {Type: cty.String, Optional: true, Sensitive: true, WriteOnly: true}, + }, + }, + }, + }, + }, + ExpectedOutput: ` # test_instance.example will be destroyed + - resource "test_instance" "example" { + - conn_info = { + - user = "not-secret" -> null + - write_only_set_password = (sensitive, write-only attribute) -> null + } -> null + - id = "i-02ae66f368e8518a9" -> null + - write_only = (write-only attribute) -> null + }`, + }, + } + runTestCases(t, testCases) +} + func TestResourceChange_moved(t *testing.T) { prevRunAddr := addrs.Resource{ Mode: addrs.ManagedResourceMode, From 672cd816d5c62ce96102be2950c9ec479e3b4f4a Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Mon, 6 Jan 2025 15:04:25 +0100 Subject: [PATCH 08/19] don't precompute actions --- .../command/jsonformat/differ/attribute.go | 10 ++- internal/command/jsonformat/differ/block.go | 13 +++- .../command/jsonformat/differ/differ_test.go | 68 +++++++++---------- internal/command/jsonformat/differ/object.go | 39 +++++++++-- 4 files changed, 83 insertions(+), 47 deletions(-) diff --git a/internal/command/jsonformat/differ/attribute.go b/internal/command/jsonformat/differ/attribute.go index fb73e4a65ad3..763f00333e73 100644 --- a/internal/command/jsonformat/differ/attribute.go +++ b/internal/command/jsonformat/differ/attribute.go @@ -4,24 +4,22 @@ package differ import ( + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" + "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/hashicorp/terraform/internal/plans" - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonprovider" ) -func ComputeDiffForAttribute(change structured.Change, attribute *jsonprovider.Attribute, blockAction plans.Action) computed.Diff { +func ComputeDiffForAttribute(change structured.Change, attribute *jsonprovider.Attribute) computed.Diff { if attribute.AttributeNestedType != nil { return computeDiffForNestedAttribute(change, attribute.AttributeNestedType) } - if attribute.WriteOnly { - return computeDiffForWriteOnlyAttribute(change, blockAction) - } return ComputeDiffForType(change, unmarshalAttribute(attribute)) } diff --git a/internal/command/jsonformat/differ/block.go b/internal/command/jsonformat/differ/block.go index b508009d8b4e..3c5a60099644 100644 --- a/internal/command/jsonformat/differ/block.go +++ b/internal/command/jsonformat/differ/block.go @@ -28,10 +28,13 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co current := change.GetDefaultActionForIteration() blockValue := change.AsMap() - blockAction := change.CalculateAction() attributes := make(map[string]computed.Diff) for key, attr := range block.Attributes { + if attr.WriteOnly { + continue + } + childValue := blockValue.GetChild(key) if !childValue.RelevantAttributes.MatchesPartial() { @@ -43,7 +46,7 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co childValue.BeforeExplicit = false childValue.AfterExplicit = false - childChange := ComputeDiffForAttribute(childValue, attr, blockAction) + childChange := ComputeDiffForAttribute(childValue, attr) if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil && !attr.WriteOnly { // Don't record nil values at all in blocks except if they are write-only. continue @@ -115,6 +118,12 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co } } + for name, attr := range block.Attributes { + if attr.WriteOnly { + attributes[name] = computeDiffForWriteOnlyAttribute(change, current) + } + } + return computed.NewDiff(renderers.Block(attributes, blocks), current, change.ReplacePaths.Matches()) } diff --git a/internal/command/jsonformat/differ/differ_test.go b/internal/command/jsonformat/differ/differ_test.go index 5707baade6db..9a2d09940e06 100644 --- a/internal/command/jsonformat/differ/differ_test.go +++ b/internal/command/jsonformat/differ/differ_test.go @@ -779,17 +779,17 @@ func TestValue_ObjectAttributes(t *testing.T) { } if tc.validateObject != nil { - tc.validateObject(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) + tc.validateObject(t, ComputeDiffForAttribute(tc.input, attribute)) return } if tc.validateSingleDiff != nil { - tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) + tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute)) return } validate := renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace) - validate(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(tc.input, attribute)) }) t.Run("map", func(t *testing.T) { @@ -803,7 +803,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -811,14 +811,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("list", func(t *testing.T) { @@ -829,7 +829,7 @@ func TestValue_ObjectAttributes(t *testing.T) { input := wrapChangeInSlice(tc.input) if tc.validateList != nil { - tc.validateList(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + tc.validateList(t, ComputeDiffForAttribute(input, attribute)) return } @@ -837,7 +837,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -845,14 +845,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("set", func(t *testing.T) { @@ -869,7 +869,7 @@ func TestValue_ObjectAttributes(t *testing.T) { ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateObject)) return ret }(), collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -877,7 +877,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -885,14 +885,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) }) @@ -914,17 +914,17 @@ func TestValue_ObjectAttributes(t *testing.T) { } if tc.validateNestedObject != nil { - tc.validateNestedObject(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) + tc.validateNestedObject(t, ComputeDiffForAttribute(tc.input, attribute)) return } if tc.validateSingleDiff != nil { - tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) + tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute)) return } validate := renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace) - validate(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(tc.input, attribute)) }) t.Run("map", func(t *testing.T) { @@ -949,7 +949,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -957,14 +957,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("list", func(t *testing.T) { @@ -989,7 +989,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -997,14 +997,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("set", func(t *testing.T) { @@ -1032,7 +1032,7 @@ func TestValue_ObjectAttributes(t *testing.T) { ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateNestedObject)) return ret }(), collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -1040,7 +1040,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -1048,14 +1048,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) }) } @@ -1840,7 +1840,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { t.Run("direct", func(t *testing.T) { tc.validateDiff(t, ComputeDiffForAttribute(tc.input, &jsonprovider.Attribute{ AttributeType: unmarshalType(t, tc.attribute), - }, plans.Update)) + })) }) t.Run("map", func(t *testing.T) { @@ -1852,7 +1852,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("list", func(t *testing.T) { @@ -1863,14 +1863,14 @@ func TestValue_PrimitiveAttributes(t *testing.T) { if tc.validateSliceDiffs != nil { validate := renderers.ValidateList(tc.validateSliceDiffs, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("set", func(t *testing.T) { @@ -1881,14 +1881,14 @@ func TestValue_PrimitiveAttributes(t *testing.T) { if tc.validateSliceDiffs != nil { validate := renderers.ValidateSet(tc.validateSetDiffs, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, ComputeDiffForAttribute(input, attribute, plans.Update)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) }) } @@ -2260,7 +2260,7 @@ func TestValue_CollectionAttributes(t *testing.T) { } t.Run(name, func(t *testing.T) { - tc.validateDiff(t, ComputeDiffForAttribute(tc.input, tc.attribute, plans.Update)) + tc.validateDiff(t, ComputeDiffForAttribute(tc.input, tc.attribute)) }) } } diff --git a/internal/command/jsonformat/differ/object.go b/internal/command/jsonformat/differ/object.go index 1bb3d4698d9c..6ca44c59e0a9 100644 --- a/internal/command/jsonformat/differ/object.go +++ b/internal/command/jsonformat/differ/object.go @@ -15,15 +15,29 @@ import ( ) func computeAttributeDiffAsObject(change structured.Change, attributes map[string]cty.Type) computed.Diff { - attributeDiffs, action := processObject(change, attributes, func(value structured.Change, ctype cty.Type) computed.Diff { + attributeDiffs, action := processObject(change, attributes, nil, func(value structured.Change, ctype cty.Type, _ plans.Action) computed.Diff { return ComputeDiffForType(value, ctype) }) return computed.NewDiff(renderers.Object(attributeDiffs), action, change.ReplacePaths.Matches()) } func computeAttributeDiffAsNestedObject(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff { - attributeDiffs, action := processObject(change, attributes, func(value structured.Change, attribute *jsonprovider.Attribute) computed.Diff { - return ComputeDiffForAttribute(value, attribute, change.CalculateAction()) + + otherAttributes := make(map[string]*jsonprovider.Attribute) + writeOnlyAttributes := make(map[string]*jsonprovider.Attribute) + for key, attr := range attributes { + if attr.WriteOnly { + writeOnlyAttributes[key] = attr + } else { + otherAttributes[key] = attr + } + } + + attributeDiffs, action := processObject(change, otherAttributes, writeOnlyAttributes, func(value structured.Change, attribute *jsonprovider.Attribute, currentAction plans.Action) computed.Diff { + if attribute.WriteOnly { + return computeDiffForWriteOnlyAttribute(value, currentAction) + } + return ComputeDiffForAttribute(value, attribute) }) return computed.NewDiff(renderers.NestedObject(attributeDiffs), action, change.ReplacePaths.Matches()) } @@ -39,7 +53,7 @@ func computeAttributeDiffAsNestedObject(change structured.Change, attributes map // Also, as it generic we cannot make this function a method on Change as you // can't create generic methods on structs. Instead, we make this a generic // function that receives the value as an argument. -func processObject[T any](v structured.Change, attributes map[string]T, computeDiff func(structured.Change, T) computed.Diff) (map[string]computed.Diff, plans.Action) { +func processObject[T any](v structured.Change, attributes map[string]T, writeOnlyAttributes map[string]T, computeDiff func(structured.Change, T, plans.Action) computed.Diff) (map[string]computed.Diff, plans.Action) { attributeDiffs := make(map[string]computed.Diff) mapValue := v.AsMap() @@ -56,7 +70,7 @@ func processObject[T any](v structured.Change, attributes map[string]T, computeD attributeValue.BeforeExplicit = false attributeValue.AfterExplicit = false - attributeDiff := computeDiff(attributeValue, attribute) + attributeDiff := computeDiff(attributeValue, attribute, currentAction) if attributeDiff.Action == plans.NoOp && attributeValue.Before == nil && attributeValue.After == nil { // We skip attributes of objects that are null both before and // after. We don't even count these as unchanged attributes. @@ -65,6 +79,21 @@ func processObject[T any](v structured.Change, attributes map[string]T, computeD attributeDiffs[key] = attributeDiff currentAction = collections.CompareActions(currentAction, attributeDiff.Action) } + for key, attribute := range writeOnlyAttributes { + attributeValue := mapValue.GetChild(key) + + if !attributeValue.RelevantAttributes.MatchesPartial() { + // Mark non-relevant attributes as unchanged. + attributeValue = attributeValue.AsNoOp() + } + + // We always assume changes to object are implicit. + attributeValue.BeforeExplicit = false + attributeValue.AfterExplicit = false + + attributeDiff := computeDiff(attributeValue, attribute, currentAction) + attributeDiffs[key] = attributeDiff + } return attributeDiffs, currentAction } From 276662f7546b3096f760fe4d6f53b692e7073ce5 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Mon, 6 Jan 2025 15:51:26 +0100 Subject: [PATCH 09/19] ephemeral: add test case for block rendering --- internal/command/jsonformat/differ/block.go | 2 +- internal/command/jsonformat/plan_test.go | 93 +++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/internal/command/jsonformat/differ/block.go b/internal/command/jsonformat/differ/block.go index 3c5a60099644..edafc7f4fb43 100644 --- a/internal/command/jsonformat/differ/block.go +++ b/internal/command/jsonformat/differ/block.go @@ -47,7 +47,7 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co childValue.AfterExplicit = false childChange := ComputeDiffForAttribute(childValue, attr) - if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil && !attr.WriteOnly { + if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil { // Don't record nil values at all in blocks except if they are write-only. continue } diff --git a/internal/command/jsonformat/plan_test.go b/internal/command/jsonformat/plan_test.go index 5b5cd1055581..0d3ba3a490f6 100644 --- a/internal/command/jsonformat/plan_test.go +++ b/internal/command/jsonformat/plan_test.go @@ -7464,6 +7464,10 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { "user": cty.String, "write_only_set_password": cty.String, }), + "block": cty.List(cty.Object(map[string]cty.Type{ + "user": cty.String, + "block_set_password": cty.String, + })), })), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), @@ -7472,6 +7476,10 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { "user": cty.StringVal("not-secret"), "write_only_set_password": cty.NullVal(cty.String), }), + "block": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("this-is-not-secret"), + "block_set_password": cty.NullVal(cty.String), + })}), }), Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -7487,6 +7495,17 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { }, }, }, + BlockTypes: map[string]*configschema.NestedBlock{ + "block": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "block_set_password": {Type: cty.String, Optional: true, Sensitive: true, WriteOnly: true}, + }, + }, + }, + }, }, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { @@ -7496,6 +7515,11 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { } + id = "i-02ae66f368e8518a9" + write_only = (write-only attribute) + + + block { + + block_set_password = (write-only attribute) + + user = "this-is-not-secret" + } }`, }, "update attribute": { @@ -7508,6 +7532,10 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { "user": cty.StringVal("not-secret"), "write_only_set_password": cty.NullVal(cty.String), }), + "block": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-so-secret"), + "block_set_password": cty.NullVal(cty.String), + })}), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a10"), @@ -7516,6 +7544,10 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { "user": cty.StringVal("not-secret"), "write_only_set_password": cty.NullVal(cty.String), }), + "block": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-so-secret"), + "block_set_password": cty.NullVal(cty.String), + })}), }), Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -7531,11 +7563,24 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { }, }, }, + BlockTypes: map[string]*configschema.NestedBlock{ + "block": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "block_set_password": {Type: cty.String, Optional: true, Sensitive: true, WriteOnly: true}, + }, + }, + }, + }, }, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> "i-02ae66f368e8518a10" # (2 unchanged attributes hidden) + + # (1 unchanged block hidden) }`, }, "update - delete block": { @@ -7548,6 +7593,10 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { "user": cty.StringVal("not-secret"), "write_only_set_password": cty.NullVal(cty.String), }), + "block": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "block_set_password": cty.NullVal(cty.String), + })}), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), @@ -7556,6 +7605,10 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { "user": cty.String, "write_only_set_password": cty.String, })), + "block": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "user": cty.String, + "block_set_password": cty.String, + }))), }), Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -7571,6 +7624,17 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { }, }, }, + BlockTypes: map[string]*configschema.NestedBlock{ + "block": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "block_set_password": {Type: cty.String, Optional: true, Sensitive: true, WriteOnly: true}, + }, + }, + }, + }, }, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { @@ -7580,6 +7644,11 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { } -> null id = "i-02ae66f368e8518a9" # (1 unchanged attribute hidden) + + - block { + - block_set_password = (write-only attribute) -> null + - user = "not-secret" -> null + } }`, }, "delete": { @@ -7592,6 +7661,10 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { "user": cty.StringVal("not-secret"), "write_only_set_password": cty.NullVal(cty.String), }), + "block": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "block_set_password": cty.NullVal(cty.String), + })}), }), After: cty.NullVal(cty.Object(map[string]cty.Type{ "id": cty.String, @@ -7600,6 +7673,10 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { "user": cty.String, "write_only_set_password": cty.String, }), + "block": cty.List(cty.Object(map[string]cty.Type{ + "user": cty.String, + "block_set_password": cty.String, + })), })), Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -7615,6 +7692,17 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { }, }, }, + BlockTypes: map[string]*configschema.NestedBlock{ + "block": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "block_set_password": {Type: cty.String, Optional: true, Sensitive: true, WriteOnly: true}, + }, + }, + }, + }, }, ExpectedOutput: ` # test_instance.example will be destroyed - resource "test_instance" "example" { @@ -7624,6 +7712,11 @@ func TestResourceChange_writeOnlyAttributes(t *testing.T) { } -> null - id = "i-02ae66f368e8518a9" -> null - write_only = (write-only attribute) -> null + + - block { + - block_set_password = (write-only attribute) -> null + - user = "not-secret" -> null + } }`, }, } From 300745c416012294b16670256ebd48d0f75995e6 Mon Sep 17 00:00:00 2001 From: Samsondeen <40821565+dsa0x@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:11:23 +0100 Subject: [PATCH 10/19] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3965837a488a..d62a72bf60a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.11.0 (Unreleased) +## 1.11.0-alpha20250107 (January 7, 2025) ENHANCEMENTS: From e4c7657304b35f8a1ac08d9b885eadf186676130 Mon Sep 17 00:00:00 2001 From: Samsondeen <40821565+dsa0x@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:13:13 +0100 Subject: [PATCH 11/19] update version --- version/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/VERSION b/version/VERSION index 1f724bf455d7..7393ec3919e0 100644 --- a/version/VERSION +++ b/version/VERSION @@ -1 +1 @@ -1.11.0-dev +1.11.0-alpha20250107 \ No newline at end of file From c1c433b762d399038f2fbce1fe9e6e9606e74bc7 Mon Sep 17 00:00:00 2001 From: Samsondeen <40821565+dsa0x@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:13:25 +0100 Subject: [PATCH 12/19] update version --- version/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/VERSION b/version/VERSION index 7393ec3919e0..2047616ad8d3 100644 --- a/version/VERSION +++ b/version/VERSION @@ -1 +1 @@ -1.11.0-alpha20250107 \ No newline at end of file +1.11.0-alpha20250107 From 2a0ee07d035b1bd88e7e2877267b22f85bd7a6eb Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Thu, 2 Jan 2025 13:14:50 +0100 Subject: [PATCH 13/19] setup changie --- .changes/1.11.0.md | 0 .changes/experiments.md | 11 ++ .changes/previous-releases.md | 16 ++ .changes/unreleased/.gitkeep | 0 .../ENHANCEMENTS-20250102-130808.yaml | 5 + .../ENHANCEMENTS-20250102-131022.yaml | 5 + .changie.yaml | 33 ++++ .copywrite.hcl | 1 + .github/pull_request_template.md | 20 +-- CHANGELOG.md | 7 +- scripts/changelog.sh | 141 ++++++++++++------ 11 files changed, 180 insertions(+), 59 deletions(-) create mode 100644 .changes/1.11.0.md create mode 100644 .changes/experiments.md create mode 100644 .changes/previous-releases.md create mode 100644 .changes/unreleased/.gitkeep create mode 100644 .changes/unreleased/ENHANCEMENTS-20250102-130808.yaml create mode 100644 .changes/unreleased/ENHANCEMENTS-20250102-131022.yaml create mode 100644 .changie.yaml diff --git a/.changes/1.11.0.md b/.changes/1.11.0.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.changes/experiments.md b/.changes/experiments.md new file mode 100644 index 000000000000..8cbffbf8241e --- /dev/null +++ b/.changes/experiments.md @@ -0,0 +1,11 @@ +EXPERIMENTS: + +Experiments are only enabled in alpha releases of Terraform CLI. The following features are not yet available in stable releases. + +- `terraform test` accepts a new option `-junit-xml=FILENAME`. If specified, and if the test configuration is valid enough to begin executing, then Terraform writes a JUnit XML test result report to the given filename, describing similar information as included in the normal test output. ([#34291](https://github.com/hashicorp/terraform/issues/34291)) +- The new command `terraform rpcapi` exposes some Terraform Core functionality through an RPC interface compatible with [`go-plugin`](https://github.com/hashicorp/go-plugin). The exact RPC API exposed here is currently subject to change at any time, because it's here primarily as a vehicle to support the [Terraform Stacks](https://www.hashicorp.com/blog/terraform-stacks-explained) private preview and so will be broken if necessary to respond to feedback from private preview participants, or possibly for other reasons. Do not use this mechanism yet outside of Terraform Stacks private preview. +- The experimental "deferred actions" feature, enabled by passing the `-allow-deferral` option to `terraform plan`, permits `count` and `for_each` arguments in `module`, `resource`, and `data` blocks to have unknown values and allows providers to react more flexibly to unknown values. This experiment is under active development, and so it's not yet useful to participate in this experiment + +## Previous Releases + +For information on prior major and minor releases, refer to their changelogs: diff --git a/.changes/previous-releases.md b/.changes/previous-releases.md new file mode 100644 index 000000000000..fe4cea59b42d --- /dev/null +++ b/.changes/previous-releases.md @@ -0,0 +1,16 @@ +- [v1.10](https://github.com/hashicorp/terraform/blob/v1.10/CHANGELOG.md) +- [v1.9](https://github.com/hashicorp/terraform/blob/v1.9/CHANGELOG.md) +- [v1.8](https://github.com/hashicorp/terraform/blob/v1.8/CHANGELOG.md) +- [v1.7](https://github.com/hashicorp/terraform/blob/v1.7/CHANGELOG.md) +- [v1.6](https://github.com/hashicorp/terraform/blob/v1.6/CHANGELOG.md) +- [v1.5](https://github.com/hashicorp/terraform/blob/v1.5/CHANGELOG.md) +- [v1.4](https://github.com/hashicorp/terraform/blob/v1.4/CHANGELOG.md) +- [v1.3](https://github.com/hashicorp/terraform/blob/v1.3/CHANGELOG.md) +- [v1.2](https://github.com/hashicorp/terraform/blob/v1.2/CHANGELOG.md) +- [v1.1](https://github.com/hashicorp/terraform/blob/v1.1/CHANGELOG.md) +- [v1.0](https://github.com/hashicorp/terraform/blob/v1.0/CHANGELOG.md) +- [v0.15](https://github.com/hashicorp/terraform/blob/v0.15/CHANGELOG.md) +- [v0.14](https://github.com/hashicorp/terraform/blob/v0.14/CHANGELOG.md) +- [v0.13](https://github.com/hashicorp/terraform/blob/v0.13/CHANGELOG.md) +- [v0.12](https://github.com/hashicorp/terraform/blob/v0.12/CHANGELOG.md) +- [v0.11 and earlier](https://github.com/hashicorp/terraform/blob/v0.11/CHANGELOG.md) diff --git a/.changes/unreleased/.gitkeep b/.changes/unreleased/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.changes/unreleased/ENHANCEMENTS-20250102-130808.yaml b/.changes/unreleased/ENHANCEMENTS-20250102-130808.yaml new file mode 100644 index 000000000000..767c4fb1a7ae --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20250102-130808.yaml @@ -0,0 +1,5 @@ +kind: ENHANCEMENTS +body: "`init`: Provider installation will utilise credentials configured in a `.netrc` file for the download and shasum URLs returned by provider registries." +time: 2025-01-02T13:00:22.419624+01:00 +custom: + Issue: "35843" diff --git a/.changes/unreleased/ENHANCEMENTS-20250102-131022.yaml b/.changes/unreleased/ENHANCEMENTS-20250102-131022.yaml new file mode 100644 index 000000000000..0f45f2fc959c --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20250102-131022.yaml @@ -0,0 +1,5 @@ +kind: ENHANCEMENTS +body: "New command `modules -json`: Displays a full list of all installed modules in a working directory, including whether each module is currently referenced by the working directory's configuration." +time: 2025-01-02T13:10:22.419624+01:00 +custom: + Issue: "35884" diff --git a/.changie.yaml b/.changie.yaml new file mode 100644 index 000000000000..098dc6fbae32 --- /dev/null +++ b/.changie.yaml @@ -0,0 +1,33 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + + +changesDir: .changes +unreleasedDir: unreleased +versionFooterPath: version_footer.tpl.md +changelogPath: CHANGELOG.md +versionExt: md +versionFormat: '## {{.Version}} ({{.Time.Format "January 2, 2006"}})' +kindFormat: "{{.Kind}}:" +changeFormat: "* {{.Body}} {{- if .Custom.Issue }} ([#{{.Custom.Issue}}](https://github.com/hashicorp/terraform/issues/{{.Custom.Issue}})){{- end}}" +custom: + - key: Issue + label: Issue/PR Number + type: int + minInt: 1 +kinds: + - label: NEW FEATURES + - label: ENHANCEMENTS + - label: BUG FIXES + - label: NOTES + - label: UPGRADE NOTES + - label: BREAKING CHANGES +newlines: + afterChangelogHeader: 0 + beforeKind: 1 + afterKind: 1 + afterChange: 1 + afterVersion: 1 + beforeChangelogVersion: 0 + endOfVersion: 2 +envPrefix: CHANGIE_ diff --git a/.copywrite.hcl b/.copywrite.hcl index e4355ec6f15f..b369a36093d3 100644 --- a/.copywrite.hcl +++ b/.copywrite.hcl @@ -13,6 +13,7 @@ project { "**/*.pb.go", "**/*_string.go", "**/mock*.go", + ".changes/unreleased/**", # these directories have their own copywrite config "docs/plugin-protocol/**", "internal/tfplugin*/**" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 024e699a8129..6b4dd468082c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -33,24 +33,14 @@ label to enable the backport bot. 1.11.x -## Draft CHANGELOG entry +## CHANGELOG entry -### NEW FEATURES | UPGRADE NOTES | ENHANCEMENTS | BUG FIXES | EXPERIMENTS - - - -- +- [ ] This change is user-facing and I added a changelog entry. +- [ ] This change is not user-facing. diff --git a/CHANGELOG.md b/CHANGELOG.md index d62a72bf60a9..f8fedf6d8c8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ ## 1.11.0-alpha20250107 (January 7, 2025) + ENHANCEMENTS: -- `init`: Provider installation will utilise credentials configured in a `.netrc` file for the download and shasum URLs returned by provider registries. ([https://github.com/hashicorp/terraform/pull/35843](35843)) -- New command `modules -json`: Displays a full list of all installed modules in a working directory, including whether each module is currently referenced by the working directory's configuration. ([#35884](https://github.com/hashicorp/terraform/pull/35884), [#36062](https://github.com/hashicorp/terraform/pull/36062)) +* `init`: Provider installation will utilise credentials configured in a `.netrc` file for the download and shasum URLs returned by provider registries. ([#35843](https://github.com/hashicorp/terraform/issues/35843)) + +* New command `modules -json`: Displays a full list of all installed modules in a working directory, including whether each module is currently referenced by the working directory's configuration. ([#35884](https://github.com/hashicorp/terraform/issues/35884)) + EXPERIMENTS: diff --git a/scripts/changelog.sh b/scripts/changelog.sh index f5c23c9d2381..dd0dbf228308 100755 --- a/scripts/changelog.sh +++ b/scripts/changelog.sh @@ -5,6 +5,8 @@ set -uo pipefail +CHANGIE_VERSION="${CHANGIE_VERSION:-1.21.0}" + function usage { cat <<-'EOF' Usage: ./changelog.sh [] @@ -13,59 +15,114 @@ Description: This script will update CHANGELOG.md with the given version and date. Commands: - prepare - prepare updates the first line in the CHANGELOG.md file with the - given version and date. - - ./changelog.sh prepare 1.0.0 "November 1, 2021" - - cleanup - cleanup prepends a new section to the CHANGELOG.md file with the given - version and (Unreleased) as the date. If the released version contains a - pre-release tag, the next version will replace the top line instead of - inserting a new section. + generate + generate will create a new section in the CHANGELOG.md file for the given release + type. The release type should be one of "dev", "alpha", "release", or "patch". + `dev`: will update the changelog with the latest unreleased changes. + `alpha`: will generate a new section with an alpha version for today. + `release`: will make the initial minor release for this branch. + `patch`: will generate a new patch release + + + nextminor + Run this to get a new release branch for the next minor version. EOF } -function prepare { - VERSION="${1:-}" - DATE="${2:-}" - - if [[ -z "$VERSION" || -z "$DATE" ]]; then - echo "missing at least one of [, ] arguments" - usage - exit 1 - fi - - sed -i '' -e "1s/.*/## $VERSION ($DATE)/" CHANGELOG.md +function generate { + RELEASE_TYPE="${1:-}" + + if [[ -z "$RELEASE_TYPE" ]]; then + echo "missing argument" + usage + exit 1 + fi + + case "$RELEASE_TYPE" in + + dev) + LATEST_VERSION=$(npx -y changie@$CHANGIE_VERSION latest -r --skip-prereleases) + COMPLETE_VERSION="$LATEST_VERSION-dev" + + + npx -y changie@$CHANGIE_VERSION merge -u "## $LATEST_VERSION (Unreleased)" + + # If we have no changes yet, the changelog is empty now, so we need to add a header + if [[ ! -s CHANGELOG.md ]]; then + echo "## $LATEST_VERSION (Unreleased)" > CHANGELOG.md + echo "" >> CHANGELOG.md + fi + ;; + + alpha) + PRERELEASE_VERSION=$(date +"alpha%Y%m%d") + LATEST_VERSION=$(npx -y changie@$CHANGIE_VERSION latest -r --skip-prereleases) + HUMAN_DATE=$(date +"%B %d, %Y") # Date in Janurary 1st, 2022 format + COMPLETE_VERSION="$LATEST_VERSION-$PRERELEASE_VERSION" + + npx -y changie@$CHANGIE_VERSION merge -u "## $COMPLETE_VERSION ($HUMAN_DATE)" + ;; + patch) + COMPLETE_VERSION=$(npx -y changie@$CHANGIE_VERSION next patch) + COMPLETE_VERSION=${COMPLETE_VERSION:1} # remove the v prefix + npx -y changie@$CHANGIE_VERSION batch patch + npx -y changie@$CHANGIE_VERSION merge + ;; + + release) + # This is the first release of the branch, releasing the new minor version + COMPLETE_VERSION=$(npx -y changie@$CHANGIE_VERSION latest -r --skip-prereleases) + # We currently keep a file that looks like this release to ensure the alphas and dev versions are generated correctly + rm ./.changes/$COMPLETE_VERSION.md + + npx -y changie@$CHANGIE_VERSION batch $COMPLETE_VERSION + npx -y changie@$CHANGIE_VERSION merge + ;; + + *) + echo "invalid argument" + usage + exit 1 + + ;; + esac + + # Set version/VERSION to the to be released version + echo "$COMPLETE_VERSION" > version/VERSION + + # Add footer to the changelog + cat ./.changes/experiments.md >> CHANGELOG.md + echo "" >> CHANGELOG.md + cat ./.changes/previous-releases.md >> CHANGELOG.md } -function cleanup { - RELEASED_VERSION="${1:-}" - NEXT_VERSION="${2:-}" - - if [[ -z "$RELEASED_VERSION" || -z "$NEXT_VERSION" ]]; then - echo "missing at least one of [, ] arguments" - usage - exit 1 - fi - - if [[ "$RELEASED_VERSION" == *-* ]]; then - # then we have a pre-release version, so we should replace the top line - sed -i '' -e "1s/.*/## $NEXT_VERSION (Unreleased)/" CHANGELOG.md - else - sed -i '' -e "1s/^/## $NEXT_VERSION (Unreleased)\n\n/" CHANGELOG.md - fi +function nextminor { + LATEST_VERSION=$(npx -y changie@$CHANGIE_VERSION latest -r --skip-prereleases) + LATEST_VERSION=${LATEST_VERSION%.*} # Remove the patch version + CURRENT_FILE_CONTENT=$(cat ./.changes/previous-releases.md) + # Prepend the latest version to the previous releases + echo "- [v$LATEST_VERSION](https://github.com/hashicorp/terraform/blob/v$LATEST_VERSION/CHANGELOG.md)" > ./.changes/previous-releases.md + echo "$CURRENT_FILE_CONTENT" >> ./.changes/previous-releases.md + + NEXT_VERSION=$(npx -y changie@$CHANGIE_VERSION next minor) + # Remove all existing per-release changelogs + rm ./.changes/*.*.*.md + # Remove all unreleased changes + rm ./.changes/unreleased/*.yaml + # Create a new empty version file for the next minor version + touch ./.changes/$NEXT_VERSION.md + + generate "dev" } function main { case "$1" in - prepare) - prepare "${@:2}" + generate) + generate "${@:2}" ;; - cleanup) - cleanup "${@:2}" + nextminor) + nextminor "${@:2}" ;; *) From bd8cb41befa36456e88722c8335c5aa0807c85af Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Mon, 6 Jan 2025 17:46:36 +0100 Subject: [PATCH 14/19] handle rc releases --- scripts/changelog.sh | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/scripts/changelog.sh b/scripts/changelog.sh index dd0dbf228308..2754fd425355 100755 --- a/scripts/changelog.sh +++ b/scripts/changelog.sh @@ -17,9 +17,10 @@ Description: Commands: generate generate will create a new section in the CHANGELOG.md file for the given release - type. The release type should be one of "dev", "alpha", "release", or "patch". + type. The release type should be one of "dev", "alpha", "rc", "release", or "patch". `dev`: will update the changelog with the latest unreleased changes. `alpha`: will generate a new section with an alpha version for today. + `rc`: will generate a new rc release. `release`: will make the initial minor release for this branch. `patch`: will generate a new patch release @@ -62,6 +63,18 @@ function generate { npx -y changie@$CHANGIE_VERSION merge -u "## $COMPLETE_VERSION ($HUMAN_DATE)" ;; + + rc) + LATEST_VERSION=$(npx -y changie@$CHANGIE_VERSION latest -r --skip-prereleases) + # We need to check if this is the first RC of the version + RC_NUMBER=$(git tag -l "v$LATEST_VERSION-rc*" | wc -l) + RC_NUMBER=$((RC_NUMBER + 1)) + HUMAN_DATE=$(date +"%B %d, %Y") # Date in Janurary 1st, 2022 format + COMPLETE_VERSION="$LATEST_VERSION-rc$RC_NUMBER" + + npx -y changie@$CHANGIE_VERSION merge -u "## $COMPLETE_VERSION ($HUMAN_DATE)" + ;; + patch) COMPLETE_VERSION=$(npx -y changie@$CHANGIE_VERSION next patch) COMPLETE_VERSION=${COMPLETE_VERSION:1} # remove the v prefix From 4415ddd735518aa40d4142cdd9e1ad8415098d5e Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Mon, 6 Jan 2025 17:50:11 +0100 Subject: [PATCH 15/19] handle beta releases --- scripts/changelog.sh | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/changelog.sh b/scripts/changelog.sh index 2754fd425355..28cc4b1ea6b0 100755 --- a/scripts/changelog.sh +++ b/scripts/changelog.sh @@ -45,7 +45,6 @@ function generate { LATEST_VERSION=$(npx -y changie@$CHANGIE_VERSION latest -r --skip-prereleases) COMPLETE_VERSION="$LATEST_VERSION-dev" - npx -y changie@$CHANGIE_VERSION merge -u "## $LATEST_VERSION (Unreleased)" # If we have no changes yet, the changelog is empty now, so we need to add a header @@ -61,6 +60,17 @@ function generate { HUMAN_DATE=$(date +"%B %d, %Y") # Date in Janurary 1st, 2022 format COMPLETE_VERSION="$LATEST_VERSION-$PRERELEASE_VERSION" + npx -y changie@$CHANGIE_VERSION merge -u "## $COMPLETE_VERSION ($HUMAN_DATE)" + ;; + + beta) + LATEST_VERSION=$(npx -y changie@$CHANGIE_VERSION latest -r --skip-prereleases) + # We need to check if this is the first RC of the version + BETA_NUMBER=$(git tag -l "v$LATEST_VERSION-beta*" | wc -l) + BETA_NUMBER=$((BETA_NUMBER + 1)) + HUMAN_DATE=$(date +"%B %d, %Y") # Date in Janurary 1st, 2022 format + COMPLETE_VERSION="$LATEST_VERSION-beta$BETA_NUMBER" + npx -y changie@$CHANGIE_VERSION merge -u "## $COMPLETE_VERSION ($HUMAN_DATE)" ;; @@ -74,14 +84,14 @@ function generate { npx -y changie@$CHANGIE_VERSION merge -u "## $COMPLETE_VERSION ($HUMAN_DATE)" ;; - + patch) COMPLETE_VERSION=$(npx -y changie@$CHANGIE_VERSION next patch) COMPLETE_VERSION=${COMPLETE_VERSION:1} # remove the v prefix npx -y changie@$CHANGIE_VERSION batch patch npx -y changie@$CHANGIE_VERSION merge ;; - + release) # This is the first release of the branch, releasing the new minor version COMPLETE_VERSION=$(npx -y changie@$CHANGIE_VERSION latest -r --skip-prereleases) From d0563661fc163a433f135424e814733cd5d56cef Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Tue, 7 Jan 2025 11:17:18 +0100 Subject: [PATCH 16/19] removed merging maintainer comment --- .github/workflows/merged-pr.yml | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .github/workflows/merged-pr.yml diff --git a/.github/workflows/merged-pr.yml b/.github/workflows/merged-pr.yml deleted file mode 100644 index 17a386730734..000000000000 --- a/.github/workflows/merged-pr.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Merged Pull Request -permissions: - pull-requests: write - -# only trigger on pull request closed events -on: - pull_request_target: - types: [ closed ] - -jobs: - merge_job: - # this job will only run if the PR has been merged - if: github.event.pull_request.merged == true - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: "Reminder for the merging maintainer: if this is a user-visible change, please update the changelog on the appropriate release branch." - }) From 4a90c38520b48d86c155f8bbaad9cc6ff50f518e Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Tue, 7 Jan 2025 11:46:04 +0100 Subject: [PATCH 17/19] add changelog entry to contributing guide --- .github/CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8a9d4db0c245..ac800c506d9c 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -121,10 +121,12 @@ If an an unmaintained area of code interests you and you'd like to become a main 1. You are welcome to submit a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/) for commentary or review before it is fully completed. It's also a good idea to include specific questions or items you'd like feedback on. 2. Once you believe your pull request is ready to be merged you can create your pull request. -3. When time permits Terraform's core team members will look over your contribution and either merge, or provide comments letting you know if there is anything left to do. It may take some time for us to respond. We may also have questions that we need answered about the code, either because something doesn't make sense to us or because we want to understand your thought process. We kindly ask that you do not target specific team members. -4. If we have requested changes, you can either make those changes or, if you disagree with the suggested changes, we can have a conversation about our reasoning and agree on a path forward. This may be a multi-step process. Our view is that pull requests are a chance to collaborate, and we welcome conversations about how to do things better. It is the contributor's responsibility to address any changes requested. While reviewers are happy to give guidance, it is unsustainable for us to perform the coding work necessary to get a PR into a mergeable state. -5. Once all outstanding comments and checklist items have been addressed, your contribution will be merged! Merged PRs may or may not be included in the next release based on changes the Terraform teams deems as breaking or not. The core team takes care of updating the [CHANGELOG.md](https://github.com/hashicorp/terraform/blob/main/CHANGELOG.md) as they merge. -6. In some cases, we might decide that a PR should be closed without merging. We'll make sure to provide clear reasoning when this happens. Following the recommended process above is one of the ways to ensure you don't spend time on a PR we can't or won't merge. +3. If your change is user-facing, add a short description in a changelog entry. +You can use `npx changie new` to create a new changelog entry or manually create a new file in the .changes/unreleasd directory. +4. When time permits Terraform's core team members will look over your contribution and either merge, or provide comments letting you know if there is anything left to do. It may take some time for us to respond. We may also have questions that we need answered about the code, either because something doesn't make sense to us or because we want to understand your thought process. We kindly ask that you do not target specific team members. +5. If we have requested changes, you can either make those changes or, if you disagree with the suggested changes, we can have a conversation about our reasoning and agree on a path forward. This may be a multi-step process. Our view is that pull requests are a chance to collaborate, and we welcome conversations about how to do things better. It is the contributor's responsibility to address any changes requested. While reviewers are happy to give guidance, it is unsustainable for us to perform the coding work necessary to get a PR into a mergeable state. +6. Once all outstanding comments and checklist items have been addressed, your contribution will be merged! Merged PRs may or may not be included in the next release based on changes the Terraform teams deems as breaking or not. The core team takes care of updating the [CHANGELOG.md](https://github.com/hashicorp/terraform/blob/main/CHANGELOG.md) as they merge. +7. In some cases, we might decide that a PR should be closed without merging. We'll make sure to provide clear reasoning when this happens. Following the recommended process above is one of the ways to ensure you don't spend time on a PR we can't or won't merge. #### Getting Your Pull Requests Merged Faster From fda43872f4fbf9d63249ab6aa9f9949c598172d1 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Tue, 7 Jan 2025 12:35:53 +0100 Subject: [PATCH 18/19] fix typo Co-authored-by: Liam Cervante --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ac800c506d9c..e8f99a0f9b15 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -122,7 +122,7 @@ If an an unmaintained area of code interests you and you'd like to become a main 1. You are welcome to submit a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/) for commentary or review before it is fully completed. It's also a good idea to include specific questions or items you'd like feedback on. 2. Once you believe your pull request is ready to be merged you can create your pull request. 3. If your change is user-facing, add a short description in a changelog entry. -You can use `npx changie new` to create a new changelog entry or manually create a new file in the .changes/unreleasd directory. +You can use `npx changie new` to create a new changelog entry or manually create a new file in the .changes/unreleased directory. 4. When time permits Terraform's core team members will look over your contribution and either merge, or provide comments letting you know if there is anything left to do. It may take some time for us to respond. We may also have questions that we need answered about the code, either because something doesn't make sense to us or because we want to understand your thought process. We kindly ask that you do not target specific team members. 5. If we have requested changes, you can either make those changes or, if you disagree with the suggested changes, we can have a conversation about our reasoning and agree on a path forward. This may be a multi-step process. Our view is that pull requests are a chance to collaborate, and we welcome conversations about how to do things better. It is the contributor's responsibility to address any changes requested. While reviewers are happy to give guidance, it is unsustainable for us to perform the coding work necessary to get a PR into a mergeable state. 6. Once all outstanding comments and checklist items have been addressed, your contribution will be merged! Merged PRs may or may not be included in the next release based on changes the Terraform teams deems as breaking or not. The core team takes care of updating the [CHANGELOG.md](https://github.com/hashicorp/terraform/blob/main/CHANGELOG.md) as they merge. From 850fc9785480cc6b371fcae88e7d537d613ce658 Mon Sep 17 00:00:00 2001 From: hc-github-team-tf-core Date: Tue, 7 Jan 2025 13:13:55 +0000 Subject: [PATCH 19/19] Cleanup after 1.11.0-alpha20250107 release --- CHANGELOG.md | 2 +- version/VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8fedf6d8c8b..7a5317676242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.11.0-alpha20250107 (January 7, 2025) +## 1.11.0 (Unreleased) ENHANCEMENTS: diff --git a/version/VERSION b/version/VERSION index 2047616ad8d3..1f724bf455d7 100644 --- a/version/VERSION +++ b/version/VERSION @@ -1 +1 @@ -1.11.0-alpha20250107 +1.11.0-dev