Skip to content

Commit cc9a430

Browse files
authored
Preserve TF raw state as deltas in Pulumi state (#2945)
This change fixes a long-standing problem of resource state data being distorted by the bridge, which is particularly sensitive around provider upgrade scenarios where schemas are changing and the TF provider expects to receive the older state record verbatim to auto-migrate it to the new schema. Before the change states were stored as PropertyMap and converted to TF representation on-read against the currently known provider schema, which may be different from the schema at the write time. The fix is two-fold: - at write time, compute the delta between TF raw state and Pulumi PropertyMap and store it in the PropertyMap - at read time, if the delta is found, reconstruct the original byte-for-byte Terraform RawState and pass it to Terraform to migrate. The change is feature-flagged and we aim for a gradual rollout but eventually make the new behavior the default. - Set EnableRawStateDelta: true on info.Provider to enable for a provider - Set PULUMI_RAW_STATE_DELTA_ENABLED=true to dynamically enable (or disable) this behavior One particular technical complication is around Set structures, which Pulumi does not model. For the deltas to work correctly the set element ordering in Pulumi array representation needs to be consistent between the TF and Pulumi states. This is accomplished for the SDKv2 bridge by preserving the cty.Value-canonical ordering but for the PF bridge by preserving the "pulumi-natural" ordering of set elements. Fixes #1667
1 parent 7a38124 commit cc9a430

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+3269
-535
lines changed

.github/workflows/build-and-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
# go version in our go.mod files.
2727
go-version: [1.22.x, 1.23.x]
2828
platform: [ubuntu-latest, macos-latest, windows-latest]
29-
feature-flags: ["DEFAULT"]
29+
feature-flags: ["DEFAULT", "PULUMI_RAW_STATE_DELTA_ENABLED"]
3030
# Needs to match TOTAL_SHARDS
3131
shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
3232
exclude:

dynamic/provider_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/hexops/autogold/v2"
1515
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
1616
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
17+
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
1718
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
1819
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
1920
"github.com/stretchr/testify/assert"
@@ -65,6 +66,8 @@ func TestStacktraceDisplayed(t *testing.T) {
6566
func TestPrimitiveTypes(t *testing.T) {
6667
t.Parallel()
6768
skipWindows(t)
69+
skipUnlessDeltasEnabled(t)
70+
6871
ctx := context.Background()
6972

7073
grpc := grpcTestServer(ctx, t)
@@ -206,6 +209,12 @@ func TestPrimitiveTypes(t *testing.T) {
206209
}, noParallel))
207210
}
208211

212+
func skipUnlessDeltasEnabled(t *testing.T) {
213+
if d, ok := os.LookupEnv("PULUMI_RAW_STATE_DELTA_ENABLED"); !ok || !cmdutil.IsTruthy(d) {
214+
t.Skip("This test requires PULUMI_RAW_STATE_DELTA_ENABLED=true environment")
215+
}
216+
}
217+
209218
func TestConfigure(t *testing.T) {
210219
t.Parallel()
211220
skipWindows(t)

dynamic/testdata/TestPrimitiveTypes/create.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
22
"id": "example-id",
33
"properties": {
4+
"__pulumi_raw_state_delta": {
5+
"obj": {}
6+
},
47
"attrBoolComputed": false,
58
"attrBoolRequired": true,
69
"attrIntComputed": 128,

dynamic/testdata/TestPrimitiveTypes/import.golden

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,37 @@
77
"attrStringRequired": "imported value"
88
},
99
"properties": {
10+
"__pulumi_raw_state_delta": {
11+
"obj": {
12+
"ps": {
13+
"attrBoolComputed": {
14+
"replace": {
15+
"raw": null
16+
}
17+
},
18+
"attrNumberComputed": {
19+
"replace": {
20+
"raw": null
21+
}
22+
},
23+
"attrStringComputed": {
24+
"replace": {
25+
"raw": null
26+
}
27+
},
28+
"attrStringDefault": {
29+
"replace": {
30+
"raw": null
31+
}
32+
},
33+
"attrStringDefaultOverridden": {
34+
"replace": {
35+
"raw": null
36+
}
37+
}
38+
}
39+
}
40+
},
1041
"attrBoolRequired": true,
1142
"attrIntComputed": 10000,
1243
"attrIntRequired": 1234,

dynamic/testdata/TestPrimitiveTypes/read.golden

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22
"id": "output-id",
33
"inputs": {},
44
"properties": {
5+
"__pulumi_raw_state_delta": {
6+
"obj": {
7+
"ps": {
8+
"attrBoolComputed": {
9+
"replace": {
10+
"raw": null
11+
}
12+
},
13+
"attrNumberComputed": {
14+
"replace": {
15+
"raw": null
16+
}
17+
}
18+
}
19+
}
20+
},
521
"attrBoolRequired": true,
622
"attrIntComputed": 10000,
723
"attrIntRequired": 64,

dynamic/testdata/TestPrimitiveTypes/update.golden

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
{
22
"properties": {
3+
"__pulumi_raw_state_delta": {
4+
"obj": {
5+
"ps": {
6+
"attrBoolComputed": {
7+
"replace": {
8+
"raw": null
9+
}
10+
},
11+
"attrNumberComputed": {
12+
"replace": {
13+
"raw": null
14+
}
15+
},
16+
"attrStringComputed": {
17+
"replace": {
18+
"raw": null
19+
}
20+
}
21+
}
22+
}
23+
},
324
"attrBoolRequired": true,
425
"attrIntComputed": 256,
526
"attrIntRequired": 64,

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module github.com/pulumi/pulumi-terraform-bridge/v3
22

3-
go 1.22.3
3+
go 1.23
44

5-
toolchain go1.23.2
5+
toolchain go1.23.7
66

77
require (
88
github.com/apparentlymart/go-cidr v1.1.0
@@ -44,7 +44,7 @@ require (
4444
github.com/opentofu/registry-address v0.0.0-20230922120653-901b9ae4061a
4545
github.com/pkg/errors v0.9.1
4646
github.com/pulumi/inflector v0.1.1
47-
github.com/pulumi/providertest v0.1.3
47+
github.com/pulumi/providertest v0.3.0
4848
github.com/pulumi/pulumi-java/pkg v1.8.0
4949
github.com/pulumi/pulumi-yaml v1.15.1
5050
github.com/pulumi/schema-tools v0.1.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,8 +2198,8 @@ github.com/pulumi/esc v0.13.0 h1:O2MPR2koScaQ2fXwyer8Q3Dd7z+DCnaDfsgNl5mVNMk=
21982198
github.com/pulumi/esc v0.13.0/go.mod h1:IIQo6W6Uzajt6f1RW4QvNxIRDlbK3TNQysnrwBHNo3U=
21992199
github.com/pulumi/inflector v0.1.1 h1:dvlxlWtXwOJTUUtcYDvwnl6Mpg33prhK+7mzeF+SobA=
22002200
github.com/pulumi/inflector v0.1.1/go.mod h1:HUFCjcPTz96YtTuUlwG3i3EZG4WlniBvR9bd+iJxCUY=
2201-
github.com/pulumi/providertest v0.1.3 h1:GpNKRy/haNjRHiUA9bi4diU4Op2zf3axYXbga5AepHg=
2202-
github.com/pulumi/providertest v0.1.3/go.mod h1:GcsqEGgSngwaNOD+kICJPIUQlnA911fGBU8HDlJvVL0=
2201+
github.com/pulumi/providertest v0.3.0 h1:Dv10aoFaNQBDRtK8cRbd2uk9vEhpC/NryN+0y7NX4/o=
2202+
github.com/pulumi/providertest v0.3.0/go.mod h1:aTxVfdxP/Pe0iPbokjBp99x0XaY4fkZB2CjIS5wysMs=
22032203
github.com/pulumi/pulumi-java/pkg v1.8.0 h1:xCTQqTGxDj1f+VmCR//V0x355rAkc2b2VCIig1Ln/n8=
22042204
github.com/pulumi/pulumi-java/pkg v1.8.0/go.mod h1:VH4YGMcPEYuMyOJjohMTepAqPSFPgmz4I3U4q5sJ89o=
22052205
github.com/pulumi/pulumi-yaml v1.15.1 h1:4T36uwbJlQMbcK/X3U9BuqMZFEN4lnAIysPtqDvm0Tg=

pkg/internal/tests/cross-tests/diff_cross_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,6 +1836,7 @@ func TestStateUpgradeSet(t *testing.T) {
18361836

18371837
func TestDiffProviderUpgradeMaxItemsOneChanged(t *testing.T) {
18381838
t.Parallel()
1839+
skipUnlessDeltasEnabled(t)
18391840

18401841
resWithMaxItemsOne := &schema.Resource{
18411842
Schema: map[string]*schema.Schema{"prop": {
@@ -1871,8 +1872,6 @@ func TestDiffProviderUpgradeMaxItemsOneChanged(t *testing.T) {
18711872
map[string]cty.Value{"prop": cty.ListVal([]cty.Value{cty.StringVal("a")})},
18721873
map[string]cty.Value{"prop": cty.ListVal([]cty.Value{cty.StringVal("a")})},
18731874
DiffProviderUpgradedSchema(resWithMaxItemsOne),
1874-
// Note we produce a diff here while TF does not.
1875-
DiffSkipDiffEquivalenceCheck(),
18761875
)
18771876

18781877
autogold.ExpectFile(t, res.PulumiOut)
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
`Previewing update (test):
22
pulumi:pulumi:Stack: (same)
33
[urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test]
4-
~ crossprovider:index/testRes:TestRes: (update)
5-
[id=newid]
6-
[urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example]
7-
+ prop: "a"
84
Resources:
9-
~ 1 to update
10-
1 unchanged
5+
2 unchanged
116
`

0 commit comments

Comments
 (0)