mirror of
https://github.com/hashicorp/terraform.git
synced 2026-06-09 08:58:34 -04:00
Merge pull request #37986 from hashicorp/jbardin/planned-private-for-random
Some checks failed
build / Determine intended Terraform version (push) Has been cancelled
build / Determine Go toolchain version (push) Has been cancelled
Quick Checks / Unit Tests (push) Has been cancelled
Quick Checks / Race Tests (push) Has been cancelled
Quick Checks / End-to-end Tests (push) Has been cancelled
Quick Checks / Code Consistency Checks (push) Has been cancelled
build / Generate release metadata (push) Has been cancelled
build / Build for freebsd_386 (push) Has been cancelled
build / Build for linux_386 (push) Has been cancelled
build / Build for openbsd_386 (push) Has been cancelled
build / Build for windows_386 (push) Has been cancelled
build / Build for darwin_amd64 (push) Has been cancelled
build / Build for freebsd_amd64 (push) Has been cancelled
build / Build for linux_amd64 (push) Has been cancelled
build / Build for openbsd_amd64 (push) Has been cancelled
build / Build for solaris_amd64 (push) Has been cancelled
build / Build for windows_amd64 (push) Has been cancelled
build / Build for freebsd_arm (push) Has been cancelled
build / Build for linux_arm (push) Has been cancelled
build / Build for darwin_arm64 (push) Has been cancelled
build / Build for linux_arm64 (push) Has been cancelled
build / Build for windows_arm64 (push) Has been cancelled
build / Build Docker image for linux_386 (push) Has been cancelled
build / Build Docker image for linux_amd64 (push) Has been cancelled
build / Build Docker image for linux_arm (push) Has been cancelled
build / Build Docker image for linux_arm64 (push) Has been cancelled
build / Build e2etest for linux_386 (push) Has been cancelled
build / Build e2etest for windows_386 (push) Has been cancelled
build / Build e2etest for darwin_amd64 (push) Has been cancelled
build / Build e2etest for linux_amd64 (push) Has been cancelled
build / Build e2etest for windows_amd64 (push) Has been cancelled
build / Build e2etest for linux_arm (push) Has been cancelled
build / Build e2etest for darwin_arm64 (push) Has been cancelled
build / Build e2etest for linux_arm64 (push) Has been cancelled
build / Run e2e test for linux_386 (push) Has been cancelled
build / Run e2e test for windows_386 (push) Has been cancelled
build / Run e2e test for darwin_amd64 (push) Has been cancelled
build / Run e2e test for linux_amd64 (push) Has been cancelled
build / Run e2e test for windows_amd64 (push) Has been cancelled
build / Run e2e test for linux_arm (push) Has been cancelled
build / Run e2e test for linux_arm64 (push) Has been cancelled
build / Run terraform-exec test for linux amd64 (push) Has been cancelled
Some checks failed
build / Determine intended Terraform version (push) Has been cancelled
build / Determine Go toolchain version (push) Has been cancelled
Quick Checks / Unit Tests (push) Has been cancelled
Quick Checks / Race Tests (push) Has been cancelled
Quick Checks / End-to-end Tests (push) Has been cancelled
Quick Checks / Code Consistency Checks (push) Has been cancelled
build / Generate release metadata (push) Has been cancelled
build / Build for freebsd_386 (push) Has been cancelled
build / Build for linux_386 (push) Has been cancelled
build / Build for openbsd_386 (push) Has been cancelled
build / Build for windows_386 (push) Has been cancelled
build / Build for darwin_amd64 (push) Has been cancelled
build / Build for freebsd_amd64 (push) Has been cancelled
build / Build for linux_amd64 (push) Has been cancelled
build / Build for openbsd_amd64 (push) Has been cancelled
build / Build for solaris_amd64 (push) Has been cancelled
build / Build for windows_amd64 (push) Has been cancelled
build / Build for freebsd_arm (push) Has been cancelled
build / Build for linux_arm (push) Has been cancelled
build / Build for darwin_arm64 (push) Has been cancelled
build / Build for linux_arm64 (push) Has been cancelled
build / Build for windows_arm64 (push) Has been cancelled
build / Build Docker image for linux_386 (push) Has been cancelled
build / Build Docker image for linux_amd64 (push) Has been cancelled
build / Build Docker image for linux_arm (push) Has been cancelled
build / Build Docker image for linux_arm64 (push) Has been cancelled
build / Build e2etest for linux_386 (push) Has been cancelled
build / Build e2etest for windows_386 (push) Has been cancelled
build / Build e2etest for darwin_amd64 (push) Has been cancelled
build / Build e2etest for linux_amd64 (push) Has been cancelled
build / Build e2etest for windows_amd64 (push) Has been cancelled
build / Build e2etest for linux_arm (push) Has been cancelled
build / Build e2etest for darwin_arm64 (push) Has been cancelled
build / Build e2etest for linux_arm64 (push) Has been cancelled
build / Run e2e test for linux_386 (push) Has been cancelled
build / Run e2e test for windows_386 (push) Has been cancelled
build / Run e2e test for darwin_amd64 (push) Has been cancelled
build / Run e2e test for linux_amd64 (push) Has been cancelled
build / Run e2e test for windows_amd64 (push) Has been cancelled
build / Run e2e test for linux_arm (push) Has been cancelled
build / Run e2e test for linux_arm64 (push) Has been cancelled
build / Run terraform-exec test for linux amd64 (push) Has been cancelled
Resource pre-planned private data
This commit is contained in:
commit
73c225dff4
10 changed files with 125 additions and 15 deletions
5
.changes/v1.16/NEW FEATURES-20251217-113349.yaml
Normal file
5
.changes/v1.16/NEW FEATURES-20251217-113349.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
kind: NEW FEATURES
|
||||
body: Store PlannedPrivate data for providers
|
||||
time: 2025-12-17T11:33:49.911997-05:00
|
||||
custom:
|
||||
Issue: "37986"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright IBM Corp. 2014, 2026
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Terraform Plugin RPC protocol version 6.10
|
||||
// Terraform Plugin RPC protocol version 6.11
|
||||
//
|
||||
// This file defines version 6.10 of the RPC protocol. To implement a plugin
|
||||
// This file defines version 6.11 of the RPC protocol. To implement a plugin
|
||||
// against this protocol, copy this definition into your own codebase and
|
||||
// use protoc to generate stubs for your target language.
|
||||
//
|
||||
|
|
@ -312,6 +312,11 @@ message ClientCapabilities {
|
|||
// The write_only_attributes_allowed capability signals that the client
|
||||
// is able to handle write_only attributes for managed resources.
|
||||
bool write_only_attributes_allowed = 2;
|
||||
|
||||
// store_planned_private indicates that the client will store the private data
|
||||
// returned with an initial plan, and send it back to the provider as
|
||||
// PlannedPrivate data in a subsequent plan request.
|
||||
bool store_planned_private = 3;
|
||||
}
|
||||
|
||||
// Deferred is a message that indicates that change is deferred for a reason.
|
||||
|
|
@ -643,6 +648,7 @@ message PlanResourceChange {
|
|||
DynamicValue provider_meta = 6;
|
||||
ClientCapabilities client_capabilities = 7;
|
||||
ResourceIdentityData prior_identity = 8;
|
||||
bytes planned_private = 9;
|
||||
}
|
||||
|
||||
message Response {
|
||||
|
|
|
|||
|
|
@ -673,6 +673,7 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
|
|||
ProposedNewState: &proto6.DynamicValue{Msgpack: propMP},
|
||||
PriorPrivate: r.PriorPrivate,
|
||||
ClientCapabilities: clientCapabilitiesToProto(r.ClientCapabilities),
|
||||
PlannedPrivate: r.PlannedPrivate,
|
||||
}
|
||||
|
||||
if metaSchema.Body != nil {
|
||||
|
|
@ -2071,6 +2072,7 @@ func clientCapabilitiesToProto(c providers.ClientCapabilities) *proto6.ClientCap
|
|||
return &proto6.ClientCapabilities{
|
||||
DeferralAllowed: c.DeferralAllowed,
|
||||
WriteOnlyAttributesAllowed: c.WriteOnlyAttributesAllowed,
|
||||
StorePlannedPrivate: c.StorePlannedPrivate,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -307,6 +307,11 @@ type ClientCapabilities struct {
|
|||
// The write_only_attributes_allowed capability signals that the client
|
||||
// is able to handle write_only attributes for managed resources.
|
||||
WriteOnlyAttributesAllowed bool
|
||||
|
||||
// StorePlannedPrivate indicates that the client is will store private data
|
||||
// returned from PlanResourceChange, and return it with the final
|
||||
// PlanResourceChange call.
|
||||
StorePlannedPrivate bool
|
||||
}
|
||||
|
||||
type ValidateProviderConfigRequest struct {
|
||||
|
|
@ -556,6 +561,11 @@ type PlanResourceChangeRequest struct {
|
|||
// provider during the last apply.
|
||||
PriorPrivate []byte
|
||||
|
||||
// PlannedPrivate is the private data stored from the the last plan.
|
||||
// PlannedPrivate will only be supplied in the plan immediately preceding an
|
||||
// ApplyResourceChange call.
|
||||
PlannedPrivate []byte
|
||||
|
||||
// ProviderMeta is the configuration for the provider_meta block for the
|
||||
// module and provider this resource belongs to. Its use is defined by
|
||||
// each provider, and it should not be used without coordination with
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -4892,3 +4893,63 @@ func TestContext2Apply_outputWithTypeContraint(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_storedPrivatePlanData(t *testing.T) {
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
resource "test_resource" "foo" {
|
||||
}
|
||||
|
||||
resource "test_resource" "bar" {
|
||||
value = test_resource.foo.computed
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
p := testProvider("test")
|
||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_resource": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
"computed": {Type: cty.String, Computed: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// make sure we can correctly re-plan a value which was stored in the
|
||||
// PlannedPrivate data from our initial plan
|
||||
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
|
||||
planned := req.ProposedNewState.AsValueMap()
|
||||
if req.PlannedPrivate != nil {
|
||||
// fetch the originally planned random string
|
||||
planned["computed"] = cty.StringVal(string(req.PlannedPrivate))
|
||||
} else {
|
||||
// this is our first plan, so generate a new computed value
|
||||
s := fmt.Sprintf("%d", rand.Int())
|
||||
planned["computed"] = cty.StringVal(s)
|
||||
resp.PlannedPrivate = []byte(s)
|
||||
}
|
||||
|
||||
planned["id"] = cty.UnknownVal(cty.String)
|
||||
resp.PlannedState = cty.ObjectVal(planned)
|
||||
return resp
|
||||
}
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
|
||||
tfdiags.AssertNoErrors(t, diags)
|
||||
|
||||
// we don't need to try and determine what the correct random value was, if
|
||||
// the planing was incorrect apply would fail with "Provider produced
|
||||
// inconsistent final plan"
|
||||
_, diags = ctx.Apply(plan, m, nil)
|
||||
tfdiags.AssertNoErrors(t, diags)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1748,6 +1748,7 @@ func TestContext2Plan_blockNestingGroup(t *testing.T) {
|
|||
ClientCapabilities: providers.ClientCapabilities{
|
||||
DeferralAllowed: false,
|
||||
WriteOnlyAttributesAllowed: true,
|
||||
StorePlannedPrivate: true,
|
||||
},
|
||||
}
|
||||
if !cmp.Equal(got, want, valueTrans) {
|
||||
|
|
|
|||
|
|
@ -660,6 +660,7 @@ func (ctx *BuiltinEvalContext) ClientCapabilities() providers.ClientCapabilities
|
|||
return providers.ClientCapabilities{
|
||||
DeferralAllowed: ctx.Deferrals().DeferralAllowed(),
|
||||
WriteOnlyAttributesAllowed: true,
|
||||
StorePlannedPrivate: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -835,10 +835,12 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
if n.preDestroyRefresh {
|
||||
checkRuleSeverity = tfdiags.Warning
|
||||
}
|
||||
|
||||
var plannedPrivate []byte
|
||||
if plannedChange != nil {
|
||||
// If we already planned the action, we stick to that plan
|
||||
createBeforeDestroy = plannedChange.Action == plans.CreateThenDelete
|
||||
|
||||
plannedPrivate = plannedChange.Private
|
||||
}
|
||||
|
||||
// Evaluate the configuration
|
||||
|
|
@ -991,6 +993,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
ProviderMeta: metaConfigVal,
|
||||
ClientCapabilities: ctx.ClientCapabilities(),
|
||||
PriorIdentity: priorIdentity,
|
||||
PlannedPrivate: plannedPrivate,
|
||||
})
|
||||
// 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.
|
||||
|
|
@ -1012,7 +1015,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
}
|
||||
|
||||
plannedNewVal := resp.PlannedState
|
||||
plannedPrivate := resp.PlannedPrivate
|
||||
plannedPrivate = resp.PlannedPrivate
|
||||
plannedIdentity := resp.PlannedIdentity
|
||||
|
||||
// These checks are only relevant if the provider is not deferring the
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright IBM Corp. 2014, 2026
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Terraform Plugin RPC protocol version 6.10
|
||||
// Terraform Plugin RPC protocol version 6.11
|
||||
//
|
||||
// This file defines version 6.10 of the RPC protocol. To implement a plugin
|
||||
// This file defines version 6.11 of the RPC protocol. To implement a plugin
|
||||
// against this protocol, copy this definition into your own codebase and
|
||||
// use protoc to generate stubs for your target language.
|
||||
//
|
||||
|
|
@ -1040,8 +1040,12 @@ type ClientCapabilities struct {
|
|||
// The write_only_attributes_allowed capability signals that the client
|
||||
// is able to handle write_only attributes for managed resources.
|
||||
WriteOnlyAttributesAllowed bool `protobuf:"varint,2,opt,name=write_only_attributes_allowed,json=writeOnlyAttributesAllowed,proto3" json:"write_only_attributes_allowed,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
// store_planned_private indicates that the client will store the private data
|
||||
// returned with an initial plan, and send it back to the provider as
|
||||
// PlannedPrivate data in a subsequent plan request.
|
||||
StorePlannedPrivate bool `protobuf:"varint,3,opt,name=store_planned_private,json=storePlannedPrivate,proto3" json:"store_planned_private,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientCapabilities) Reset() {
|
||||
|
|
@ -1088,6 +1092,13 @@ func (x *ClientCapabilities) GetWriteOnlyAttributesAllowed() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (x *ClientCapabilities) GetStorePlannedPrivate() bool {
|
||||
if x != nil {
|
||||
return x.StorePlannedPrivate
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Deferred is a message that indicates that change is deferred for a reason.
|
||||
type Deferred struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
|
|
@ -4998,6 +5009,7 @@ type PlanResourceChange_Request struct {
|
|||
ProviderMeta *DynamicValue `protobuf:"bytes,6,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"`
|
||||
ClientCapabilities *ClientCapabilities `protobuf:"bytes,7,opt,name=client_capabilities,json=clientCapabilities,proto3" json:"client_capabilities,omitempty"`
|
||||
PriorIdentity *ResourceIdentityData `protobuf:"bytes,8,opt,name=prior_identity,json=priorIdentity,proto3" json:"prior_identity,omitempty"`
|
||||
PlannedPrivate []byte `protobuf:"bytes,9,opt,name=planned_private,json=plannedPrivate,proto3" json:"planned_private,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
|
@ -5088,6 +5100,13 @@ func (x *PlanResourceChange_Request) GetPriorIdentity() *ResourceIdentityData {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *PlanResourceChange_Request) GetPlannedPrivate() []byte {
|
||||
if x != nil {
|
||||
return x.PlannedPrivate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PlanResourceChange_Response struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
PlannedState *DynamicValue `protobuf:"bytes,1,opt,name=planned_state,json=plannedState,proto3" json:"planned_state,omitempty"`
|
||||
|
|
@ -8220,10 +8239,11 @@ const file_tfplugin6_proto_rawDesc = "" +
|
|||
"\fplan_destroy\x18\x01 \x01(\bR\vplanDestroy\x12?\n" +
|
||||
"\x1cget_provider_schema_optional\x18\x02 \x01(\bR\x19getProviderSchemaOptional\x12.\n" +
|
||||
"\x13move_resource_state\x18\x03 \x01(\bR\x11moveResourceState\x128\n" +
|
||||
"\x18generate_resource_config\x18\x04 \x01(\bR\x16generateResourceConfig\"\x82\x01\n" +
|
||||
"\x18generate_resource_config\x18\x04 \x01(\bR\x16generateResourceConfig\"\xb6\x01\n" +
|
||||
"\x12ClientCapabilities\x12)\n" +
|
||||
"\x10deferral_allowed\x18\x01 \x01(\bR\x0fdeferralAllowed\x12A\n" +
|
||||
"\x1dwrite_only_attributes_allowed\x18\x02 \x01(\bR\x1awriteOnlyAttributesAllowed\"\xa2\x01\n" +
|
||||
"\x1dwrite_only_attributes_allowed\x18\x02 \x01(\bR\x1awriteOnlyAttributesAllowed\x122\n" +
|
||||
"\x15store_planned_private\x18\x03 \x01(\bR\x13storePlannedPrivate\"\xa2\x01\n" +
|
||||
"\bDeferred\x122\n" +
|
||||
"\x06reason\x18\x01 \x01(\x0e2\x1a.tfplugin6.Deferred.ReasonR\x06reason\"b\n" +
|
||||
"\x06Reason\x12\v\n" +
|
||||
|
|
@ -8361,8 +8381,8 @@ const file_tfplugin6_proto_rawDesc = "" +
|
|||
"\vdiagnostics\x18\x02 \x03(\v2\x15.tfplugin6.DiagnosticR\vdiagnostics\x12\x18\n" +
|
||||
"\aprivate\x18\x03 \x01(\fR\aprivate\x12/\n" +
|
||||
"\bdeferred\x18\x04 \x01(\v2\x13.tfplugin6.DeferredR\bdeferred\x12B\n" +
|
||||
"\fnew_identity\x18\x05 \x01(\v2\x1f.tfplugin6.ResourceIdentityDataR\vnewIdentity\"\x87\a\n" +
|
||||
"\x12PlanResourceChange\x1a\xd3\x03\n" +
|
||||
"\fnew_identity\x18\x05 \x01(\v2\x1f.tfplugin6.ResourceIdentityDataR\vnewIdentity\"\xb0\a\n" +
|
||||
"\x12PlanResourceChange\x1a\xfc\x03\n" +
|
||||
"\aRequest\x12\x1b\n" +
|
||||
"\ttype_name\x18\x01 \x01(\tR\btypeName\x128\n" +
|
||||
"\vprior_state\x18\x02 \x01(\v2\x17.tfplugin6.DynamicValueR\n" +
|
||||
|
|
@ -8372,7 +8392,8 @@ const file_tfplugin6_proto_rawDesc = "" +
|
|||
"\rprior_private\x18\x05 \x01(\fR\fpriorPrivate\x12<\n" +
|
||||
"\rprovider_meta\x18\x06 \x01(\v2\x17.tfplugin6.DynamicValueR\fproviderMeta\x12N\n" +
|
||||
"\x13client_capabilities\x18\a \x01(\v2\x1d.tfplugin6.ClientCapabilitiesR\x12clientCapabilities\x12F\n" +
|
||||
"\x0eprior_identity\x18\b \x01(\v2\x1f.tfplugin6.ResourceIdentityDataR\rpriorIdentity\x1a\x9a\x03\n" +
|
||||
"\x0eprior_identity\x18\b \x01(\v2\x1f.tfplugin6.ResourceIdentityDataR\rpriorIdentity\x12'\n" +
|
||||
"\x0fplanned_private\x18\t \x01(\fR\x0eplannedPrivate\x1a\x9a\x03\n" +
|
||||
"\bResponse\x12<\n" +
|
||||
"\rplanned_state\x18\x01 \x01(\v2\x17.tfplugin6.DynamicValueR\fplannedState\x12C\n" +
|
||||
"\x10requires_replace\x18\x02 \x03(\v2\x18.tfplugin6.AttributePathR\x0frequiresReplace\x12'\n" +
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright IBM Corp. 2014, 2026
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Terraform Plugin RPC protocol version 6.10
|
||||
// Terraform Plugin RPC protocol version 6.11
|
||||
//
|
||||
// This file defines version 6.10 of the RPC protocol. To implement a plugin
|
||||
// This file defines version 6.11 of the RPC protocol. To implement a plugin
|
||||
// against this protocol, copy this definition into your own codebase and
|
||||
// use protoc to generate stubs for your target language.
|
||||
//
|
||||
|
|
|
|||
Loading…
Reference in a new issue