diff --git a/pkg/pf/tfbridge/provider_diff.go b/pkg/pf/tfbridge/provider_diff.go index 5341813cb..15b4a763f 100644 --- a/pkg/pf/tfbridge/provider_diff.go +++ b/pkg/pf/tfbridge/provider_diff.go @@ -19,6 +19,7 @@ import ( "fmt" "sort" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" @@ -29,58 +30,38 @@ import ( "github.com/pulumi/pulumi-terraform-bridge/v3/unstable/propertyvalue" ) -// Diff checks what impacts a hypothetical update will have on the resource's properties. Receives checkedInputs from -// Check and the prior state. The implementation here calls PlanResourceChange Terraform method. Essentially: -// -// Diff(priorState, checkedInputs): -// proposedNewState = priorState.applyChanges(checkedInputs) -// plannedState = PlanResourceChange(priorState, proposedNewState) -// priorState.Diff(plannedState) -func (p *provider) DiffWithContext( - ctx context.Context, - urn resource.URN, - id resource.ID, - priorStateMap resource.PropertyMap, - checkedInputs resource.PropertyMap, - allowUnknowns bool, - ignoreChanges []string, -) (plugin.DiffResult, error) { - ctx = p.initLogging(ctx, p.logSink, urn) - rh, err := p.resourceHandle(ctx, urn) - if err != nil { - return plugin.DiffResult{}, err - } - - priorStateMap, err = transformFromState(ctx, rh, priorStateMap) +func (p *provider) getPlanAndPriorState( + ctx context.Context, priorStateMap resource.PropertyMap, rh resourceHandle, + checkedInputs resource.PropertyMap, ignoreChanges []string, +) (*tfprotov6.PlanResourceChangeResponse, *upgradedResourceState, error) { + priorStateMap, err := transformFromState(ctx, rh, priorStateMap) if err != nil { - return plugin.DiffResult{}, err + return nil, nil, err } checkedInputs, err = propertyvalue.ApplyIgnoreChanges(priorStateMap, checkedInputs, ignoreChanges) if err != nil { - return plugin.DiffResult{}, fmt.Errorf("failed to apply ignore changes: %w", err) + return nil, nil, fmt.Errorf("failed to apply ignore changes: %w", err) } rawPriorState, err := parseResourceState(&rh, priorStateMap) if err != nil { - return plugin.DiffResult{}, err + return nil, nil, err } priorState, err := p.UpgradeResourceState(ctx, &rh, rawPriorState) if err != nil { - return plugin.DiffResult{}, err + return nil, nil, err } - tfType := rh.schema.Type(ctx).(tftypes.Object) - checkedInputsValue, err := convert.EncodePropertyMap(rh.encoder, checkedInputs) if err != nil { - return plugin.DiffResult{}, err + return nil, nil, err } planResp, err := p.plan(ctx, rh.terraformResourceName, rh.schema, priorState, checkedInputsValue) if err != nil { - return plugin.DiffResult{}, err + return nil, nil, err } // NOTE: this currently ignores planRep.PlanedPrivate but it is unclear if it should signal differences between @@ -89,9 +70,42 @@ func (p *provider) DiffWithContext( // to surface private state differences to the user from the Diff method. if err := p.processDiagnostics(planResp.Diagnostics); err != nil { + return nil, nil, err + } + + return planResp, priorState, nil +} + +// Diff checks what impacts a hypothetical update will have on the resource's properties. Receives checkedInputs from +// Check and the prior state. The implementation here calls PlanResourceChange Terraform method. Essentially: +// +// Diff(priorState, checkedInputs): +// proposedNewState = priorState.applyChanges(checkedInputs) +// plannedState = PlanResourceChange(priorState, proposedNewState) +// priorState.Diff(plannedState) +func (p *provider) DiffWithContext( + ctx context.Context, + urn resource.URN, + id resource.ID, + priorStateMap resource.PropertyMap, + checkedInputs resource.PropertyMap, + allowUnknowns bool, + ignoreChanges []string, +) (plugin.DiffResult, error) { + ctx = p.initLogging(ctx, p.logSink, urn) + rh, err := p.resourceHandle(ctx, urn) + if err != nil { return plugin.DiffResult{}, err } + planResp, priorState, err := p.getPlanAndPriorState( + ctx, priorStateMap, rh, checkedInputs, ignoreChanges) + if err != nil { + return plugin.DiffResult{}, err + } + + tfType := rh.schema.Type(ctx).(tftypes.Object) + // TODO[pulumi/pulumi-terraform-bridge#751] ignoreChanges support plannedStateValue, err := planResp.PlannedState.Unmarshal(tfType) if err != nil { @@ -135,6 +149,13 @@ func (p *provider) DiffWithContext( } if providerOpts.enableAccurateBridgePreview { + if len(planResp.RequiresReplace) > 0 { + // If we have a replace, we need to recompute the plan with a nil prior + // in order to correctly mark computed properties for recomputation. + //nolint:lll + // https://github.com/opentofu/opentofu/blob/864aa9d1d629090cfc4ddf9fdd344d34dee9793e/internal/tofu/node_resource_abstract_instance.go#L1054 + // TODO plan again with nil prior + } pluginDetailedDiff, err := calculateDetailedDiff(ctx, &rh, priorState, plannedStateValue, checkedInputs) if err != nil { return plugin.DiffResult{}, err