mirror of
https://github.com/hashicorp/terraform.git
synced 2025-12-18 23:26:07 -05:00
fix: validate replace_triggered_by attribute references (#38010)
Some checks are pending
build / Determine intended Terraform version (push) Waiting to run
build / Determine Go toolchain version (push) Waiting to run
build / Generate release metadata (push) Blocked by required conditions
build / Build for freebsd_386 (push) Blocked by required conditions
build / Build for linux_386 (push) Blocked by required conditions
build / Build for openbsd_386 (push) Blocked by required conditions
build / Build for windows_386 (push) Blocked by required conditions
build / Build for darwin_amd64 (push) Blocked by required conditions
build / Build for freebsd_amd64 (push) Blocked by required conditions
build / Build for linux_amd64 (push) Blocked by required conditions
build / Build for openbsd_amd64 (push) Blocked by required conditions
build / Build for solaris_amd64 (push) Blocked by required conditions
build / Build for windows_amd64 (push) Blocked by required conditions
build / Build for freebsd_arm (push) Blocked by required conditions
build / Build for linux_arm (push) Blocked by required conditions
build / Build for darwin_arm64 (push) Blocked by required conditions
build / Build for linux_arm64 (push) Blocked by required conditions
build / Build for windows_arm64 (push) Blocked by required conditions
build / Build Docker image for linux_386 (push) Blocked by required conditions
build / Build Docker image for linux_amd64 (push) Blocked by required conditions
build / Build Docker image for linux_arm (push) Blocked by required conditions
build / Build Docker image for linux_arm64 (push) Blocked by required conditions
build / Build e2etest for linux_386 (push) Blocked by required conditions
build / Build e2etest for windows_386 (push) Blocked by required conditions
build / Build e2etest for darwin_amd64 (push) Blocked by required conditions
build / Build e2etest for linux_amd64 (push) Blocked by required conditions
build / Build e2etest for windows_amd64 (push) Blocked by required conditions
build / Build e2etest for linux_arm (push) Blocked by required conditions
build / Build e2etest for darwin_arm64 (push) Blocked by required conditions
build / Build e2etest for linux_arm64 (push) Blocked by required conditions
build / Run e2e test for linux_386 (push) Blocked by required conditions
build / Run e2e test for windows_386 (push) Blocked by required conditions
build / Run e2e test for darwin_amd64 (push) Blocked by required conditions
build / Run e2e test for linux_amd64 (push) Blocked by required conditions
build / Run e2e test for windows_amd64 (push) Blocked by required conditions
build / Run e2e test for linux_arm (push) Blocked by required conditions
build / Run e2e test for linux_arm64 (push) Blocked by required conditions
build / Run terraform-exec test for linux amd64 (push) Blocked by required conditions
Quick Checks / Unit Tests (push) Waiting to run
Quick Checks / Race Tests (push) Waiting to run
Quick Checks / End-to-end Tests (push) Waiting to run
Quick Checks / Code Consistency Checks (push) Waiting to run
Some checks are pending
build / Determine intended Terraform version (push) Waiting to run
build / Determine Go toolchain version (push) Waiting to run
build / Generate release metadata (push) Blocked by required conditions
build / Build for freebsd_386 (push) Blocked by required conditions
build / Build for linux_386 (push) Blocked by required conditions
build / Build for openbsd_386 (push) Blocked by required conditions
build / Build for windows_386 (push) Blocked by required conditions
build / Build for darwin_amd64 (push) Blocked by required conditions
build / Build for freebsd_amd64 (push) Blocked by required conditions
build / Build for linux_amd64 (push) Blocked by required conditions
build / Build for openbsd_amd64 (push) Blocked by required conditions
build / Build for solaris_amd64 (push) Blocked by required conditions
build / Build for windows_amd64 (push) Blocked by required conditions
build / Build for freebsd_arm (push) Blocked by required conditions
build / Build for linux_arm (push) Blocked by required conditions
build / Build for darwin_arm64 (push) Blocked by required conditions
build / Build for linux_arm64 (push) Blocked by required conditions
build / Build for windows_arm64 (push) Blocked by required conditions
build / Build Docker image for linux_386 (push) Blocked by required conditions
build / Build Docker image for linux_amd64 (push) Blocked by required conditions
build / Build Docker image for linux_arm (push) Blocked by required conditions
build / Build Docker image for linux_arm64 (push) Blocked by required conditions
build / Build e2etest for linux_386 (push) Blocked by required conditions
build / Build e2etest for windows_386 (push) Blocked by required conditions
build / Build e2etest for darwin_amd64 (push) Blocked by required conditions
build / Build e2etest for linux_amd64 (push) Blocked by required conditions
build / Build e2etest for windows_amd64 (push) Blocked by required conditions
build / Build e2etest for linux_arm (push) Blocked by required conditions
build / Build e2etest for darwin_arm64 (push) Blocked by required conditions
build / Build e2etest for linux_arm64 (push) Blocked by required conditions
build / Run e2e test for linux_386 (push) Blocked by required conditions
build / Run e2e test for windows_386 (push) Blocked by required conditions
build / Run e2e test for darwin_amd64 (push) Blocked by required conditions
build / Run e2e test for linux_amd64 (push) Blocked by required conditions
build / Run e2e test for windows_amd64 (push) Blocked by required conditions
build / Run e2e test for linux_arm (push) Blocked by required conditions
build / Run e2e test for linux_arm64 (push) Blocked by required conditions
build / Run terraform-exec test for linux amd64 (push) Blocked by required conditions
Quick Checks / Unit Tests (push) Waiting to run
Quick Checks / Race Tests (push) Waiting to run
Quick Checks / End-to-end Tests (push) Waiting to run
Quick Checks / Code Consistency Checks (push) Waiting to run
* fix: use schema-based validation for replace_triggered_by Fixes #36740 * fix: use change.ProviderAddr for schema lookup
This commit is contained in:
parent
f88d09544e
commit
c36c81431a
3 changed files with 85 additions and 0 deletions
5
.changes/v1.15/BUG FIXES-20251213-120000.yaml
Normal file
5
.changes/v1.15/BUG FIXES-20251213-120000.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
kind: BUG FIXES
|
||||
body: 'lifecycle: `replace_triggered_by` now reports an error when given an invalid attribute reference that does not exist in the target resource'
|
||||
time: 2025-12-13T12:00:00.000000Z
|
||||
custom:
|
||||
Issue: "36740"
|
||||
|
|
@ -4399,6 +4399,65 @@ resource "test_object" "b" {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContext2Plan_triggeredByInvalidAttribute(t *testing.T) {
|
||||
// This test verifies that referencing a non-existent attribute in
|
||||
// replace_triggered_by produces an error.
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
resource "test_object" "a" {
|
||||
test_string = "new"
|
||||
}
|
||||
resource "test_object" "b" {
|
||||
test_string = "value"
|
||||
lifecycle {
|
||||
replace_triggered_by = [ test_object.a.nonexistent_attribute ]
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
p := simpleMockProvider()
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
state := states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
mustResourceInstanceAddr("test_object.a"),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
AttrsJSON: []byte(`{"test_string":"old"}`),
|
||||
Status: states.ObjectReady,
|
||||
},
|
||||
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
||||
)
|
||||
s.SetResourceInstanceCurrent(
|
||||
mustResourceInstanceAddr("test_object.b"),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
AttrsJSON: []byte(`{"test_string":"value"}`),
|
||||
Status: states.ObjectReady,
|
||||
},
|
||||
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
||||
)
|
||||
})
|
||||
|
||||
_, diags := ctx.Plan(m, state, &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
})
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("expected errors for invalid attribute reference in replace_triggered_by")
|
||||
}
|
||||
|
||||
// Check that the error message is about the invalid attribute reference.
|
||||
// StaticValidateTraversal returns "Unsupported attribute" errors.
|
||||
errMsg := diags.Err().Error()
|
||||
if !strings.Contains(errMsg, "Unsupported attribute") && !strings.Contains(errMsg, "nonexistent_attribute") {
|
||||
t.Fatalf("unexpected error message: %s", errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Plan_dataSchemaChange(t *testing.T) {
|
||||
// We can't decode the prior state when a data source upgrades the schema
|
||||
// in an incompatible way. Since prior state for data sources is purely
|
||||
|
|
|
|||
|
|
@ -426,6 +426,27 @@ func (ctx *BuiltinEvalContext) EvaluateReplaceTriggeredBy(expr hcl.Expression, r
|
|||
return nil, false, diags
|
||||
}
|
||||
|
||||
// Validate the attribute reference against the target resource's schema.
|
||||
// We use schema-based validation rather than value-based validation because
|
||||
// resources may contain dynamically-typed attributes (DynamicPseudoType) whose
|
||||
// actual type can change between plans. Schema validation ensures we only
|
||||
// error on truly invalid attribute references.
|
||||
// We use change.ProviderAddr rather than resolving from config because
|
||||
// the provider configuration may not be local to the current module.
|
||||
providerSchema, err := ctx.ProviderSchema(change.ProviderAddr)
|
||||
if err == nil {
|
||||
schema := providerSchema.SchemaForResourceType(resCfg.Mode, resCfg.Type)
|
||||
if schema.Body != nil {
|
||||
moreDiags := schema.Body.StaticValidateTraversal(ref.Remaining)
|
||||
diags = diags.Append(moreDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, false, diags
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we couldn't get the schema, we skip validation and let the value
|
||||
// comparison below handle it. This is a graceful degradation for edge cases.
|
||||
|
||||
path, _ := traversalToPath(ref.Remaining)
|
||||
attrBefore, _ := path.Apply(change.Before)
|
||||
attrAfter, _ := path.Apply(change.After)
|
||||
|
|
|
|||
Loading…
Reference in a new issue