stacks: adding deferred action to datasource reads

TF-13959
Mark DeCrane 2 years ago committed by Daniel Schmidt
parent 765ec6e923
commit e6f4ffa8a3
No known key found for this signature in database
GPG Key ID: 377C3A4D62FBBBE2

@ -721,6 +721,7 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p
Config: &proto.DynamicValue{
Msgpack: config,
},
DeferralAllowed: r.DeferralAllowed,
}
if metaSchema.Block != nil {
@ -745,6 +746,7 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p
return resp
}
resp.State = state
resp.Deferred = convert.ProtoToDeferred(protoResp.Deferred)
return resp
}

@ -710,6 +710,7 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p
Config: &proto6.DynamicValue{
Msgpack: config,
},
DeferralAllowed: r.DeferralAllowed,
}
if metaSchema.Block != nil {
@ -734,6 +735,7 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p
return resp
}
resp.State = state
resp.Deferred = convert.ProtoToDeferred(protoResp.Deferred)
return resp
}

@ -508,6 +508,10 @@ type ReadDataSourceRequest struct {
// each provider, and it should not be used without coordination with
// HashiCorp. It is considered experimental and subject to change.
ProviderMeta cty.Value
// DeferralAllowed signals that the provider is allowed to defer the
// changes. If set the caller needs to handle the deferred response.
DeferralAllowed bool
}
type ReadDataSourceResponse struct {
@ -516,6 +520,10 @@ type ReadDataSourceResponse struct {
// Diagnostics contains any warnings or errors from the method call.
Diagnostics tfdiags.Diagnostics
// Deferred if present signals that the provider was not able to fully
// complete this operation and a susequent run is required.
Deferred *Deferred
}
type CallFunctionRequest struct {

@ -933,7 +933,7 @@ resource "test" "c" {
targetResourceThatDependsOnDeferredResourceTest = deferredActionsTest{
configs: map[string]string{
"main.tf": `
variable "resource_count" {
variable "resource_count" {
type = number
}
@ -1581,11 +1581,61 @@ output "a" {
},
},
}
readDataSourceTest = deferredActionsTest{
configs: map[string]string{
"main.tf": `
data "test" "a" {
name = "deferred_read"
}
resource "test" "b" {
name = data.test.a.name
}
output "a" {
value = data.test.a
}
output "b" {
value = test.b
}
`,
},
stages: []deferredActionsTestStage{
{
inputs: map[string]cty.Value{},
wantPlanned: map[string]cty.Value{
"deferred_read": cty.ObjectVal(map[string]cty.Value{
"name": cty.StringVal("deferred_read"),
"upstream_names": cty.NullVal(cty.Set(cty.String)),
"output": cty.UnknownVal(cty.String),
}),
},
wantActions: map[string]plans.Action{},
wantApplied: map[string]cty.Value{
// The all resources will be deferred, so shouldn't
// have any action at this stage.
},
wantOutputs: map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"name": cty.StringVal("deferred_read"),
}),
"b": cty.NullVal(cty.DynamicPseudoType),
},
wantDeferred: map[string]providers.DeferredReason{
"data.test.a": providers.DeferredReasonProviderConfigUnknown,
"test.b": providers.DeferredReasonDeferredPrereq,
},
complete: false,
},
},
}
)
func TestContextApply_deferredActions(t *testing.T) {
tests := map[string]deferredActionsTest{
"resource_for_each": resourceForEachTest,
"resource_in_module_for_each": resourceInModuleForEachTest,
"resource_count": resourceCountTest,
@ -1599,6 +1649,7 @@ func TestContextApply_deferredActions(t *testing.T) {
"custom_conditions": customConditionsTest,
"custom_conditions_with_orphans": customConditionsWithOrphansTest,
"resource_read": resourceReadTest,
"data_read": readDataSourceTest,
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
@ -1784,6 +1835,18 @@ func (provider *deferredActionsProvider) Provider() providers.Interface {
},
},
},
DataSources: map[string]providers.Schema{
"test": {
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"name": {
Type: cty.String,
Required: true,
},
},
},
},
},
},
ReadResourceFn: func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
if key := req.PriorState.GetAttr("name"); key.IsKnown() && key.AsString() == "deferred_read" {
@ -1799,6 +1862,19 @@ func (provider *deferredActionsProvider) Provider() providers.Interface {
NewState: req.PriorState,
}
},
ReadDataSourceFn: func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
if key := req.Config.GetAttr("name"); key.IsKnown() && key.AsString() == "deferred_read" {
return providers.ReadDataSourceResponse{
State: req.Config,
Deferred: &providers.Deferred{
Reason: providers.DeferredReasonProviderConfigUnknown,
},
}
}
return providers.ReadDataSourceResponse{
State: req.Config,
}
},
PlanResourceChangeFn: func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
if req.ProposedNewState.IsNull() {
// Then we're deleting a concrete instance.

@ -1594,10 +1594,23 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal
}
} else {
resp = provider.ReadDataSource(providers.ReadDataSourceRequest{
TypeName: n.Addr.ContainingResource().Resource.Type,
Config: configVal,
ProviderMeta: metaConfigVal,
TypeName: n.Addr.ContainingResource().Resource.Type,
Config: configVal,
ProviderMeta: metaConfigVal,
DeferralAllowed: ctx.Deferrals().DeferralAllowed(),
})
if resp.Deferred != nil {
deffered := ctx.Deferrals()
deffered.ReportResourceInstanceDeferred(n.Addr, resp.Deferred.Reason, &plans.ResourceInstanceChange{
Addr: n.Addr,
Change: plans.Change{
Action: plans.Read,
Before: cty.DynamicVal,
After: resp.State,
},
})
}
}
diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
if diags.HasErrors() {

Loading…
Cancel
Save