Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions pkg/pf/tests/diff_test/diff_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package tfbridgetests

import (
"context"
"math/big"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-framework/resource"
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hexops/autogold/v2"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
"github.com/zclconf/go-cty/cty"

pb "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/internal/providerbuilder"
Expand Down Expand Up @@ -176,3 +181,111 @@ func TestPFDetailedDiffStringAttribute(t *testing.T) {
})
}
}

func TestPFDetailedDiffDynamicType(t *testing.T) {
t.Parallel()
if d, ok := os.LookupEnv("PULUMI_RAW_STATE_DELTA_ENABLED"); ok && cmdutil.IsTruthy(d) {
// TODO[pulumi/pulumi-terraform-bridge#3078]
t.Skip("Does not work with PULUMI_RAW_STATE_DELTA_ENABLED=true")
}

attributeSchema := rschema.Schema{
Attributes: map[string]rschema.Attribute{
"key": rschema.DynamicAttribute{
Optional: true,
},
},
}
res := pb.NewResource(pb.NewResourceArgs{
ResourceSchema: attributeSchema,
})

t.Run("no change", func(t *testing.T) {
crosstests.Diff(t, res,
map[string]cty.Value{"key": cty.StringVal("value")},
map[string]cty.Value{"key": cty.StringVal("value")},
)
})

t.Run("change", func(t *testing.T) {
crosstests.Diff(t, res,
map[string]cty.Value{"key": cty.StringVal("value")},
map[string]cty.Value{"key": cty.StringVal("value1")},
)
})

t.Run("int no change", func(t *testing.T) {
crosstests.Diff(t, res,
map[string]cty.Value{"key": cty.NumberVal(big.NewFloat(1))},
map[string]cty.Value{"key": cty.NumberVal(big.NewFloat(1))},
)
})

t.Run("type change", func(t *testing.T) {
// TODO[pulumi/pulumi-terraform-bridge#3078]
t.Skip(`Error converting tftypes.Number<"1"> (value2) at "AttributeName(\"key\")": can't unmarshal tftypes.Number into *string, expected string`)
crosstests.Diff(t, res,
map[string]cty.Value{"key": cty.StringVal("value")},
map[string]cty.Value{"key": cty.NumberVal(big.NewFloat(1))},
)
})
}

func TestPFDetailedDiffDynamicTypeWithMigration(t *testing.T) {
t.Parallel()
// TODO[pulumi/pulumi-terraform-bridge#3078]
t.Skip("DynamicPseudoType is not supported")

attributeSchema := rschema.Schema{
Attributes: map[string]rschema.Attribute{
"key": rschema.DynamicAttribute{
Optional: true,
},
},
}
res1 := pb.NewResource(pb.NewResourceArgs{
ResourceSchema: attributeSchema,
})

schema2 := rschema.Schema{
Attributes: map[string]rschema.Attribute{
"key": rschema.DynamicAttribute{
Optional: true,
},
},
Version: 1,
}
res2 := pb.NewResource(pb.NewResourceArgs{
ResourceSchema: schema2,
UpgradeStateFunc: func(ctx context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
0: {
PriorSchema: &res1.ResourceSchema,
StateUpgrader: func(ctx context.Context, usr1 resource.UpgradeStateRequest, usr2 *resource.UpgradeStateResponse) {
usr2.State = *usr1.State
},
},
}
},
})

t.Run("no change", func(t *testing.T) {
crosstests.Diff(t, res1,
map[string]cty.Value{"key": cty.StringVal("value")},
map[string]cty.Value{"key": cty.StringVal("value")},
crosstests.DiffProviderUpgradedSchema(
res2,
),
)
})

t.Run("change", func(t *testing.T) {
crosstests.Diff(t, res1,
map[string]cty.Value{"key": cty.StringVal("value")},
map[string]cty.Value{"key": cty.StringVal("value1")},
crosstests.DiffProviderUpgradedSchema(
res2,
),
)
})
}
11 changes: 6 additions & 5 deletions pkg/pf/tfbridge/resource_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,10 @@ func (p *provider) parseAndUpgradeResourceState(
}

// Otherwise fallback to imprecise legacy parsing.
tfType := rh.schema.Type(ctx).(tftypes.Object)
value, err := convert.EncodePropertyMap(rh.encoder, props)
if err != nil {
return nil, fmt.Errorf("[pf/tfbridge] Error calling EncodePropertyMap: %w", err)
}
rawState, err := pfutils.NewRawState(tfType, value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subtle! Thank you!

if err != nil {
return nil, fmt.Errorf("[pf/tfbridge] Error calling NewRawState: %w", err)
}

// Before EnableRawStateDelta rollout, the behavior used to be to skip the upgrade method in case of an exact
// version match. This seems incorrect, but to derisk fixing this problem it is flagged together with
Expand All @@ -272,6 +267,12 @@ func (p *provider) parseAndUpgradeResourceState(
}, nil
}

tfType := rh.schema.Type(ctx).(tftypes.Object)
rawState, err := pfutils.NewRawState(tfType, value)
if err != nil {
return nil, fmt.Errorf("[pf/tfbridge] Error calling NewRawState: %w", err)
}

return p.upgradeResourceState(ctx, rh, rawState, parsedMeta.PrivateState, stateVersion)
}

Expand Down