Skip to content

Resource Identity: Add identity data to RPCs needed to store/read from state #1112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
28f4719
new attribute additions (breaks build for existing internal schema at…
austinvalle Feb 24, 2025
c4ad0c5
update internal testing/testschema
austinvalle Feb 24, 2025
2950f20
implement in datasource/schema
austinvalle Feb 24, 2025
f2243ba
implement in ephemeral/schema
austinvalle Feb 24, 2025
057093a
implement in provider/metaschema
austinvalle Feb 24, 2025
2fed091
implement in provider/schema
austinvalle Feb 24, 2025
3c759ec
implement in resource/schema
austinvalle Feb 24, 2025
167b824
add to final internal/testing/testschema
austinvalle Feb 24, 2025
77ef579
get resource identity schema implementation
austinvalle Mar 6, 2025
0f63086
Merge branch 'main' into feat/resource-identity
austinvalle Mar 6, 2025
63dc825
protov6 + tests
austinvalle Mar 6, 2025
f97d278
fix up package docs + build specific TODO comments
austinvalle Mar 6, 2025
5578a7d
spellcheck!
austinvalle Mar 6, 2025
dd0bcbc
Merge branch 'main' into feat/resource-identity
austinvalle Mar 10, 2025
7eba2e7
add tfsdk object
austinvalle Mar 10, 2025
d07f756
ReadResource RPC implementation for v5
austinvalle Mar 12, 2025
e3dd2b6
ReadResource RPC implementation for v6
austinvalle Mar 12, 2025
8873227
protov5 + fwserver implementation for PlanResourceChange
austinvalle Mar 12, 2025
774af73
protov6 implementation of PlanResourceChange
austinvalle Mar 12, 2025
8a90ecb
implement ApplyResourceChange fwserver + protov5
austinvalle Mar 13, 2025
bb07ce6
protov6 implementation of apply resource
austinvalle Mar 14, 2025
df3e558
update go dep
austinvalle Mar 14, 2025
717befd
Merge branch 'main' into feat/resource-identity_data-req-resp
austinvalle Mar 18, 2025
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
9 changes: 8 additions & 1 deletion internal/fromproto5/applyresourcechange.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

// ApplyResourceChangeRequest returns the *fwserver.ApplyResourceChangeRequest
// equivalent of a *tfprotov5.ApplyResourceChangeRequest.
func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) {
func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) {
if proto5 == nil {
return nil, nil
}
Expand All @@ -40,6 +40,7 @@ func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyReso

fw := &fwserver.ApplyResourceChangeRequest{
ResourceSchema: resourceSchema,
IdentitySchema: identitySchema,
Resource: resource,
}

Expand All @@ -55,6 +56,12 @@ func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyReso

fw.PlannedState = plannedState

plannedIdentity, plannedIdentityDiags := ResourceIdentity(ctx, proto5.PlannedIdentity, identitySchema)

diags.Append(plannedIdentityDiags...)

fw.PlannedIdentity = plannedIdentity

priorState, priorStateDiags := State(ctx, proto5.PriorState, resourceSchema)

diags.Append(priorStateDiags...)
Expand Down
64 changes: 63 additions & 1 deletion internal/fromproto5/applyresourcechange_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-framework/internal/privatestate"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)
Expand Down Expand Up @@ -48,6 +49,30 @@ func TestApplyResourceChangeRequest(t *testing.T) {
},
}

testIdentityProto5Type := tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_identity_attribute": tftypes.String,
},
}

testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{
"test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"),
})

testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value)

if err != nil {
t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err)
}

testIdentitySchema := identityschema.Schema{
Attributes: map[string]identityschema.Attribute{
"test_identity_attribute": identityschema.StringAttribute{
RequiredForImport: true,
},
},
}

testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{
"providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`),
})
Expand All @@ -61,6 +86,7 @@ func TestApplyResourceChangeRequest(t *testing.T) {
resourceSchema fwschema.Schema
resource resource.Resource
providerMetaSchema fwschema.Schema
identitySchema fwschema.Schema
expected *fwserver.ApplyResourceChangeRequest
expectedDiagnostics diag.Diagnostics
}{
Expand Down Expand Up @@ -137,6 +163,42 @@ func TestApplyResourceChangeRequest(t *testing.T) {
ResourceSchema: testFwSchema,
},
},
"plannedidentity-missing-schema": {
input: &tfprotov5.ApplyResourceChangeRequest{
PlannedIdentity: &tfprotov5.ResourceIdentityData{
IdentityData: &testIdentityProto5DynamicValue,
},
},
resourceSchema: testFwSchema,
expected: &fwserver.ApplyResourceChangeRequest{
ResourceSchema: testFwSchema,
},
expectedDiagnostics: diag.Diagnostics{
diag.NewErrorDiagnostic(
"Unable to Convert Resource Identity",
"An unexpected error was encountered when converting the resource identity from the protocol type. "+
"Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+
"This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.",
),
},
},
"plannedidentity": {
input: &tfprotov5.ApplyResourceChangeRequest{
PlannedIdentity: &tfprotov5.ResourceIdentityData{
IdentityData: &testIdentityProto5DynamicValue,
},
},
identitySchema: testIdentitySchema,
resourceSchema: testFwSchema,
expected: &fwserver.ApplyResourceChangeRequest{
IdentitySchema: testIdentitySchema,
PlannedIdentity: &tfsdk.ResourceIdentity{
Raw: testIdentityProto5Value,
Schema: testIdentitySchema,
},
ResourceSchema: testFwSchema,
},
},
"plannedprivate-malformed-json": {
input: &tfprotov5.ApplyResourceChangeRequest{
PlannedPrivate: []byte(`{`),
Expand Down Expand Up @@ -253,7 +315,7 @@ func TestApplyResourceChangeRequest(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

got, diags := fromproto5.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema)
got, diags := fromproto5.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema)

if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" {
t.Errorf("unexpected difference: %s", diff)
Expand Down
9 changes: 8 additions & 1 deletion internal/fromproto5/planresourcechange.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

// PlanResourceChangeRequest returns the *fwserver.PlanResourceChangeRequest
// equivalent of a *tfprotov5.PlanResourceChangeRequest.
func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) {
func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, identitySchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) {
if proto5 == nil {
return nil, nil
}
Expand All @@ -41,6 +41,7 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour
fw := &fwserver.PlanResourceChangeRequest{
ResourceBehavior: resourceBehavior,
ResourceSchema: resourceSchema,
IdentitySchema: identitySchema,
Resource: reqResource,
ClientCapabilities: ModifyPlanClientCapabilities(proto5.ClientCapabilities),
}
Expand All @@ -57,6 +58,12 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour

fw.PriorState = priorState

priorIdentity, priorIdentityDiags := ResourceIdentity(ctx, proto5.PriorIdentity, identitySchema)

diags.Append(priorIdentityDiags...)

fw.PriorIdentity = priorIdentity

proposedNewState, proposedNewStateDiags := Plan(ctx, proto5.ProposedNewState, resourceSchema)

diags.Append(proposedNewStateDiags...)
Expand Down
64 changes: 63 additions & 1 deletion internal/fromproto5/planresourcechange_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-framework/internal/privatestate"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)
Expand Down Expand Up @@ -48,6 +49,30 @@ func TestPlanResourceChangeRequest(t *testing.T) {
},
}

testIdentityProto5Type := tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_identity_attribute": tftypes.String,
},
}

testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{
"test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"),
})

testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value)

if err != nil {
t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err)
}

testIdentitySchema := identityschema.Schema{
Attributes: map[string]identityschema.Attribute{
"test_identity_attribute": identityschema.StringAttribute{
RequiredForImport: true,
},
},
}

testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{
"providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`),
})
Expand All @@ -58,6 +83,7 @@ func TestPlanResourceChangeRequest(t *testing.T) {
input *tfprotov5.PlanResourceChangeRequest
resourceBehavior resource.ResourceBehavior
resourceSchema fwschema.Schema
identitySchema fwschema.Schema
resource resource.Resource
providerMetaSchema fwschema.Schema
expected *fwserver.PlanResourceChangeRequest
Expand Down Expand Up @@ -182,6 +208,42 @@ func TestPlanResourceChangeRequest(t *testing.T) {
ResourceSchema: testFwSchema,
},
},
"prioridentity-missing-schema": {
input: &tfprotov5.PlanResourceChangeRequest{
PriorIdentity: &tfprotov5.ResourceIdentityData{
IdentityData: &testIdentityProto5DynamicValue,
},
},
resourceSchema: testFwSchema,
expected: &fwserver.PlanResourceChangeRequest{
ResourceSchema: testFwSchema,
},
expectedDiagnostics: diag.Diagnostics{
diag.NewErrorDiagnostic(
"Unable to Convert Resource Identity",
"An unexpected error was encountered when converting the resource identity from the protocol type. "+
"Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+
"This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.",
),
},
},
"prioridentity": {
input: &tfprotov5.PlanResourceChangeRequest{
PriorIdentity: &tfprotov5.ResourceIdentityData{
IdentityData: &testIdentityProto5DynamicValue,
},
},
identitySchema: testIdentitySchema,
resourceSchema: testFwSchema,
expected: &fwserver.PlanResourceChangeRequest{
IdentitySchema: testIdentitySchema,
PriorIdentity: &tfsdk.ResourceIdentity{
Raw: testIdentityProto5Value,
Schema: testIdentitySchema,
},
ResourceSchema: testFwSchema,
},
},
"providermeta-missing-data": {
input: &tfprotov5.PlanResourceChangeRequest{},
resourceSchema: testFwSchema,
Expand Down Expand Up @@ -265,7 +327,7 @@ func TestPlanResourceChangeRequest(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

got, diags := fromproto5.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior)
got, diags := fromproto5.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior, testCase.identitySchema)

if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" {
t.Errorf("unexpected difference: %s", diff)
Expand Down
9 changes: 8 additions & 1 deletion internal/fromproto5/readresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

// ReadResourceRequest returns the *fwserver.ReadResourceRequest
// equivalent of a *tfprotov5.ReadResourceRequest.
func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) {
func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) {
if proto5 == nil {
return nil, nil
}
Expand All @@ -26,6 +26,7 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ

fw := &fwserver.ReadResourceRequest{
Resource: reqResource,
IdentitySchema: identitySchema,
ClientCapabilities: ReadResourceClientCapabilities(proto5.ClientCapabilities),
}

Expand All @@ -35,6 +36,12 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ

fw.CurrentState = currentState

currentIdentity, currentIdentityDiags := ResourceIdentity(ctx, proto5.CurrentIdentity, identitySchema)

diags.Append(currentIdentityDiags...)

fw.CurrentIdentity = currentIdentity

providerMeta, providerMetaDiags := ProviderMeta(ctx, proto5.ProviderMeta, providerMetaSchema)

diags.Append(providerMetaDiags...)
Expand Down
59 changes: 58 additions & 1 deletion internal/fromproto5/readresource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-framework/internal/privatestate"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)
Expand Down Expand Up @@ -48,6 +49,30 @@ func TestReadResourceRequest(t *testing.T) {
},
}

testIdentityProto5Type := tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_identity_attribute": tftypes.String,
},
}

testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{
"test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"),
})

testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value)

if err != nil {
t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err)
}

testIdentitySchema := identityschema.Schema{
Attributes: map[string]identityschema.Attribute{
"test_identity_attribute": identityschema.StringAttribute{
RequiredForImport: true,
},
},
}

testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{
"providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`),
})
Expand All @@ -59,6 +84,7 @@ func TestReadResourceRequest(t *testing.T) {
testCases := map[string]struct {
input *tfprotov5.ReadResourceRequest
resourceSchema fwschema.Schema
identitySchema fwschema.Schema
resource resource.Resource
providerMetaSchema fwschema.Schema
expected *fwserver.ReadResourceRequest
Expand Down Expand Up @@ -99,6 +125,37 @@ func TestReadResourceRequest(t *testing.T) {
},
},
},
"currentidentity-missing-schema": {
input: &tfprotov5.ReadResourceRequest{
CurrentIdentity: &tfprotov5.ResourceIdentityData{
IdentityData: &testIdentityProto5DynamicValue,
},
},
expected: &fwserver.ReadResourceRequest{},
expectedDiagnostics: diag.Diagnostics{
diag.NewErrorDiagnostic(
"Unable to Convert Resource Identity",
"An unexpected error was encountered when converting the resource identity from the protocol type. "+
"Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+
"This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.",
),
},
},
"currentidentity": {
input: &tfprotov5.ReadResourceRequest{
CurrentIdentity: &tfprotov5.ResourceIdentityData{
IdentityData: &testIdentityProto5DynamicValue,
},
},
identitySchema: testIdentitySchema,
expected: &fwserver.ReadResourceRequest{
IdentitySchema: testIdentitySchema,
CurrentIdentity: &tfsdk.ResourceIdentity{
Raw: testIdentityProto5Value,
Schema: testIdentitySchema,
},
},
},
"private-malformed-json": {
input: &tfprotov5.ReadResourceRequest{
Private: []byte(`{`),
Expand Down Expand Up @@ -200,7 +257,7 @@ func TestReadResourceRequest(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

got, diags := fromproto5.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema)
got, diags := fromproto5.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema)

if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" {
t.Errorf("unexpected difference: %s", diff)
Expand Down
Loading
Loading