Skip to content

Commit

Permalink
Return current state when requesting deferral. (#2570)
Browse files Browse the repository at this point in the history
* Return unknown value for "object" when asking for a plan deferral
* Return current state when requesting deferral of a read.
* Return presuo-typed unknown value from deferred import
* Bump Terraform version in deferred actions tests to the latest alpha release.
  • Loading branch information
alexsomesan authored Aug 14, 2024
1 parent 6531356 commit bfd8d84
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 33 deletions.
3 changes: 3 additions & 0 deletions .changelog/2570.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
deferred actions: return unknown value instead of nil when requesting deferral of a planned resource change.
```
2 changes: 1 addition & 1 deletion .github/workflows/acceptance_test_dfa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
inputs:
terraformVersion:
description: Terraform version
default: 1.9.0-alpha20240516
default: 1.10.0-alpha20240807

jobs:
acceptance_tests:
Expand Down
9 changes: 9 additions & 0 deletions manifest/provider/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,16 @@ func (s *RawProviderServer) ImportResourceState(ctx context.Context, req *tfprot

cp := req.ClientCapabilities
if cp != nil && cp.DeferralAllowed && s.clientConfigUnknown {
v := tftypes.NewValue(tftypes.DynamicPseudoType, tftypes.UnknownValue)
dv, err := tfprotov5.NewDynamicValue(v.Type(), v)
if err != nil {
return resp, err
}
// if client support it, request deferral when client configuration not fully known
resp.ImportedResources = append(resp.ImportedResources, &tfprotov5.ImportedResource{
TypeName: req.TypeName,
State: &dv,
})
resp.Deferred = &tfprotov5.Deferred{
Reason: tfprotov5.DeferredReasonProviderConfigUnknown,
}
Expand Down
71 changes: 39 additions & 32 deletions manifest/provider/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,49 @@ func isImportedFlagFromPrivate(p []byte) (f bool, d []*tfprotov5.Diagnostic) {
func (s *RawProviderServer) PlanResourceChange(ctx context.Context, req *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) {
resp := &tfprotov5.PlanResourceChangeResponse{}

rt, err := GetResourceType(req.TypeName)
if err != nil {
resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{
Severity: tfprotov5.DiagnosticSeverityError,
Summary: "Failed to determine planned resource type",
Detail: err.Error(),
})
return resp, nil
}
// Decode proposed resource state
proposedState, err := req.ProposedNewState.Unmarshal(rt)
if err != nil {
resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{
Severity: tfprotov5.DiagnosticSeverityError,
Summary: "Failed to unmarshal planned resource state",
Detail: err.Error(),
})
return resp, nil
}
s.logger.Trace("[PlanResourceChange]", "[ProposedState]", dump(proposedState))

proposedVal := make(map[string]tftypes.Value)
err = proposedState.As(&proposedVal)
if err != nil {
resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{
Severity: tfprotov5.DiagnosticSeverityError,
Summary: "Failed to extract planned resource state from tftypes.Value",
Detail: err.Error(),
})
return resp, nil
}

canDeferr := req.ClientCapabilities != nil && req.ClientCapabilities.DeferralAllowed

if canDeferr && s.clientConfigUnknown {
// if client supports it, request deferral when client configuration not fully known
proposedVal["object"] = tftypes.NewValue(tftypes.DynamicPseudoType, tftypes.UnknownValue)
newPlannedState := tftypes.NewValue(proposedState.Type(), proposedVal)
ps, err := tfprotov5.NewDynamicValue(newPlannedState.Type(), newPlannedState)
if err != nil {
return resp, err
}
resp.PlannedState = &ps
resp.Deferred = &tfprotov5.Deferred{
Reason: tfprotov5.DeferredReasonProviderConfigUnknown,
}
Expand Down Expand Up @@ -164,38 +203,6 @@ func (s *RawProviderServer) PlanResourceChange(ctx context.Context, req *tfproto
return resp, nil
}

rt, err := GetResourceType(req.TypeName)
if err != nil {
resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{
Severity: tfprotov5.DiagnosticSeverityError,
Summary: "Failed to determine planned resource type",
Detail: err.Error(),
})
return resp, nil
}
// Decode proposed resource state
proposedState, err := req.ProposedNewState.Unmarshal(rt)
if err != nil {
resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{
Severity: tfprotov5.DiagnosticSeverityError,
Summary: "Failed to unmarshal planned resource state",
Detail: err.Error(),
})
return resp, nil
}
s.logger.Trace("[PlanResourceChange]", "[ProposedState]", dump(proposedState))

proposedVal := make(map[string]tftypes.Value)
err = proposedState.As(&proposedVal)
if err != nil {
resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{
Severity: tfprotov5.DiagnosticSeverityError,
Summary: "Failed to extract planned resource state from tftypes.Value",
Detail: err.Error(),
})
return resp, nil
}

computedFields := make(map[string]*tftypes.AttributePath)
var atp *tftypes.AttributePath
cfVal, ok := proposedVal["computed_fields"]
Expand Down
1 change: 1 addition & 0 deletions manifest/provider/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func (s *RawProviderServer) ReadResource(ctx context.Context, req *tfprotov5.Rea
cp := req.ClientCapabilities
if cp != nil && cp.DeferralAllowed && s.clientConfigUnknown {
// if client support it, request deferral when client configuration not fully known
resp.NewState = req.CurrentState
resp.Deferred = &tfprotov5.Deferred{
Reason: tfprotov5.DeferredReasonProviderConfigUnknown,
}
Expand Down

0 comments on commit bfd8d84

Please sign in to comment.