Skip to content
Closed
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
52 changes: 0 additions & 52 deletions internal/framework5provider/move_state_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ import (

"github.com/hashicorp/terraform-plugin-framework/path"
"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/types"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

var _ resource.Resource = MoveStateResource{}
var _ resource.ResourceWithIdentity = MoveStateResource{}
var _ resource.ResourceWithMoveState = MoveStateResource{}

func NewMoveStateResource() resource.Resource {
Expand All @@ -41,16 +38,6 @@ func (r MoveStateResource) Schema(_ context.Context, _ resource.SchemaRequest, r
}
}

func (r MoveStateResource) IdentitySchema(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
resp.IdentitySchema = identityschema.Schema{
Attributes: map[string]identityschema.Attribute{
"id": identityschema.StringAttribute{
RequiredForImport: true,
},
},
}
}

func (r MoveStateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data MoveStateResourceModel

Expand Down Expand Up @@ -121,46 +108,7 @@ func (r MoveStateResource) MoveState(ctx context.Context) []resource.StateMover
}

resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("moved_random_string"), oldState.Result)...)
resp.Diagnostics.Append(resp.TargetIdentity.SetAttribute(ctx, path.Root("id"), oldState.Result)...)
case "registry.terraform.io/hashicorp/framework": // Corner provider (testing identity moves)
if req.SourceTypeName != "framework_identity" {
resp.Diagnostics.AddError(
"Invalid Move State Request",
fmt.Sprintf("The \"framework_move_state\" resource can only be sourced from the \"random_string\" or \"framework_identity\" managed resources:\n\n"+
"req.SourceProviderAddress: %q\n"+
"req.SourceTypeName: %q\n",
req.SourceProviderAddress,
req.SourceTypeName,
),
)
return
}

oldIdentityVal, err := req.SourceIdentity.Unmarshal(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"id": tftypes.String,
"name": tftypes.String,
},
},
)
if err != nil {
resp.Diagnostics.AddError(
"Unexpected Move State Error",
fmt.Sprintf("Error decoding source identity: %s", err.Error()),
)
return
}

var sourceIdentityObj map[string]tftypes.Value
var sourceID, sourceName string

oldIdentityVal.As(&sourceIdentityObj) //nolint:errcheck // This is just a quick test of grabbing raw identity data
sourceIdentityObj["id"].As(&sourceID) //nolint:errcheck // This is just a quick test of grabbing raw identity data
sourceIdentityObj["name"].As(&sourceName) //nolint:errcheck // This is just a quick test of grabbing raw identity data

resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("moved_random_string"), sourceName)...)
resp.Diagnostics.Append(resp.TargetIdentity.SetAttribute(ctx, path.Root("id"), sourceID)...)
default:
resp.Diagnostics.AddError(
"Invalid Move State Request",
Expand Down
46 changes: 0 additions & 46 deletions internal/framework5provider/move_state_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-testing/compare"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
Expand Down Expand Up @@ -67,48 +66,3 @@ func TestMoveStateResource(t *testing.T) {
},
})
}

func TestMoveStateResource_identity(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_12_0),
},
ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){
"framework": providerserver.NewProtocol5WithError(New()),
},
Steps: []resource.TestStep{
{
Config: `resource "framework_identity" "old" {
name = "tom"
}`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectIdentity("framework_identity.old", map[string]knownvalue.Check{
"id": knownvalue.StringExact("id-123"),
"name": knownvalue.StringExact("tom"),
}),
},
},
{
Config: `
moved {
from = framework_identity.old
to = framework_move_state.new
}
resource "framework_move_state" "new" {}
`,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectEmptyPlan(),
},
},
ConfigStateChecks: []statecheck.StateCheck{
// The previous framework_identity.old identity should be moved to this new location, split into the new location identity and state.
statecheck.ExpectIdentity("framework_move_state.new", map[string]knownvalue.Check{
"id": knownvalue.StringExact("id-123"),
}),
statecheck.ExpectKnownValue("framework_move_state.new", tfjsonpath.New("moved_random_string"), knownvalue.StringExact("tom")),
},
},
},
})
}
160 changes: 160 additions & 0 deletions internal/framework5provider/move_state_resource_with_identity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package framework

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/path"
"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/types"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

var _ resource.Resource = MoveStateResourceWithIdentity{}
var _ resource.ResourceWithIdentity = MoveStateResourceWithIdentity{}
var _ resource.ResourceWithMoveState = MoveStateResourceWithIdentity{}

func NewMoveStateResourceWithIdentity() resource.Resource {
return &MoveStateResourceWithIdentity{}
}

// MoveStateResourceWithIdentity is for testing the MoveResourceState RPC
// https://developer.hashicorp.com/terraform/plugin/framework/resources/state-move
type MoveStateResourceWithIdentity struct{}

func (r MoveStateResourceWithIdentity) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_move_state_with_identity"
}

func (r MoveStateResourceWithIdentity) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"moved_random_string": schema.StringAttribute{
Computed: true,
},
},
}
}

func (r MoveStateResourceWithIdentity) IdentitySchema(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
resp.IdentitySchema = identityschema.Schema{
Attributes: map[string]identityschema.Attribute{
"id": identityschema.StringAttribute{
RequiredForImport: true,
},
},
}
}

func (r MoveStateResourceWithIdentity) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data MoveStateResourceWithIdentityModel

resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r MoveStateResourceWithIdentity) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data MoveStateResourceWithIdentityModel

resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r MoveStateResourceWithIdentity) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data MoveStateResourceWithIdentityModel

resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r MoveStateResourceWithIdentity) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
}

func (r MoveStateResourceWithIdentity) MoveState(ctx context.Context) []resource.StateMover {
return []resource.StateMover{
{
SourceSchema: &schema.Schema{
Attributes: map[string]schema.Attribute{
"result": schema.StringAttribute{},
},
},
StateMover: func(ctx context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) {
switch req.SourceProviderAddress {
case "registry.terraform.io/hashicorp/framework": // Corner provider (testing identity moves)
if req.SourceTypeName != "framework_identity" {
resp.Diagnostics.AddError(
"Invalid Move State Request",
fmt.Sprintf("The \"framework_move_state\" resource can only be sourced from the \"random_string\" or \"framework_identity\" managed resources:\n\n"+
"req.SourceProviderAddress: %q\n"+
"req.SourceTypeName: %q\n",
req.SourceProviderAddress,
req.SourceTypeName,
),
)
return
}

oldIdentityVal, err := req.SourceIdentity.Unmarshal(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"id": tftypes.String,
"name": tftypes.String,
},
},
)
if err != nil {
resp.Diagnostics.AddError(
"Unexpected Move State Error",
fmt.Sprintf("Error decoding source identity: %s", err.Error()),
)
return
}

var sourceIdentityObj map[string]tftypes.Value
var sourceID, sourceName string

oldIdentityVal.As(&sourceIdentityObj) //nolint:errcheck // This is just a quick test of grabbing raw identity data
sourceIdentityObj["id"].As(&sourceID) //nolint:errcheck // This is just a quick test of grabbing raw identity data
sourceIdentityObj["name"].As(&sourceName) //nolint:errcheck // This is just a quick test of grabbing raw identity data

resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("moved_random_string"), sourceName)...)
resp.Diagnostics.Append(resp.TargetIdentity.SetAttribute(ctx, path.Root("id"), sourceID)...)
default:
resp.Diagnostics.AddError(
"Invalid Move State Request",
fmt.Sprintf("This test can only migrate resource state from hardcoded provider/resource types:\n\n"+
"req.SourceProviderAddress: %q\n"+
"req.SourceTypeName: %q\n",
req.SourceProviderAddress,
req.SourceTypeName,
),
)
}
},
},
}
}

type MoveStateResourceWithIdentityModel struct {
MovedRandomString types.String `tfsdk:"moved_random_string"`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package framework

import (
"testing"

"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)

func TestMoveStateResource_identity(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_12_0),
},
ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){
"framework": providerserver.NewProtocol5WithError(New()),
},
Steps: []resource.TestStep{
{
Config: `resource "framework_identity" "old" {
name = "tom"
}`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectIdentity("framework_identity.old", map[string]knownvalue.Check{
"id": knownvalue.StringExact("id-123"),
"name": knownvalue.StringExact("tom"),
}),
},
},
{
Config: `
moved {
from = framework_identity.old
to = framework_move_state_with_identity.new
}
resource "framework_move_state_with_identity" "new" {}
`,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectEmptyPlan(),
},
},
ConfigStateChecks: []statecheck.StateCheck{
// The previous framework_identity.old identity should be moved to this new location, split into the new location identity and state.
statecheck.ExpectIdentity("framework_move_state_with_identity.new", map[string]knownvalue.Check{
"id": knownvalue.StringExact("id-123"),
}),
statecheck.ExpectKnownValue("framework_move_state_with_identity.new", tfjsonpath.New("moved_random_string"), knownvalue.StringExact("tom")),
},
},
},
})
}
1 change: 1 addition & 0 deletions internal/framework5provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func (p *testProvider) Resources(_ context.Context) []func() resource.Resource {
NewFloat64PrecisionResource,
NewTFSDKReflectionResource,
NewMoveStateResource,
NewMoveStateResourceWithIdentity,
NewSetNestedBlockWithDefaultsResource,
NewSetSemanticEqualityResource,
NewCustomTypeResource,
Expand Down
Loading
Loading