diff --git a/.changelog/452.txt b/.changelog/452.txt new file mode 100644 index 000000000..1c7f87c0c --- /dev/null +++ b/.changelog/452.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource: Prevented `Error Decoding Private State` errors on resources previously managed by terraform-plugin-sdk +``` diff --git a/internal/privatestate/data.go b/internal/privatestate/data.go index 89ca3e3c1..ecf610fdf 100644 --- a/internal/privatestate/data.go +++ b/internal/privatestate/data.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" ) // Data contains private state data for the framework and providers. @@ -113,6 +114,20 @@ func NewData(ctx context.Context, data []byte) (*Data, diag.Diagnostics) { err := json.Unmarshal(data, &dataMap) if err != nil { + // terraform-plugin-sdk stored private state by marshalling its data + // as map[string]any, which is slightly incompatible with trying to + // unmarshal it as map[string][]byte. If unmarshalling with + // map[string]any works, we can ignore it for now, as provider + // developers did not have access to managing the private state data. + // + // TODO: We can extract the terraform-plugin-sdk resource timeouts key + // here to extract its prior data, if necessary. + // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/400 + if anyErr := json.Unmarshal(data, new(map[string]any)); anyErr == nil { + logging.FrameworkWarn(ctx, "Discarding incompatible resource private state data", map[string]any{logging.KeyError: err.Error()}) + return nil, nil + } + diags.AddError( "Error Decoding Private State", fmt.Sprintf("An error was encountered when decoding private state: %s.\n\n"+ diff --git a/internal/privatestate/data_test.go b/internal/privatestate/data_test.go index 3a7e2ba7b..c8402a8be 100644 --- a/internal/privatestate/data_test.go +++ b/internal/privatestate/data_test.go @@ -2,6 +2,7 @@ package privatestate import ( "context" + "encoding/json" "fmt" "testing" @@ -281,6 +282,14 @@ func TestNewData(t *testing.T) { "providerKeyTwo": []byte(`{"pKeyTwo": {"k2": "two", "k3": 3}}`), }) + sdkJSON, err := json.Marshal(map[string]any{ + "schema_version": "2", + }) + + if err != nil { + t.Fatalf("unexpected error marshaling SDK JSON: %s", err) + } + testCases := map[string]struct { data []byte expected *Data @@ -410,6 +419,10 @@ func TestNewData(t *testing.T) { }, }, }, + "sdk-ignore": { + data: sdkJSON, + expected: nil, + }, } for name, testCase := range testCases {