Skip to content

Commit

Permalink
echo resource
Browse files Browse the repository at this point in the history
  • Loading branch information
austinvalle committed Nov 1, 2024
1 parent d8bb7fe commit 44d5ec0
Show file tree
Hide file tree
Showing 6 changed files with 407 additions and 1 deletion.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.22.7

require (
github.com/hashicorp/go-memdb v1.3.4
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/terraform-json v0.23.0
github.com/hashicorp/terraform-plugin-framework v1.13.0
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1
Expand Down Expand Up @@ -33,7 +34,6 @@ require (
github.com/hashicorp/go-plugin v1.6.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hc-install v0.9.0 // indirect
github.com/hashicorp/hcl/v2 v2.22.0 // indirect
Expand Down
34 changes: 34 additions & 0 deletions internal/echoprovider/data_router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package echoprovider

import (
"context"

"github.com/hashicorp/terraform-plugin-go/tfprotov6"
)

type errUnsupportedDataSource string

func (e errUnsupportedDataSource) Error() string {
return "unsupported data source: " + string(e)
}

type dataSourceRouter map[string]tfprotov6.DataSourceServer

func (d dataSourceRouter) ValidateDataResourceConfig(ctx context.Context, req *tfprotov6.ValidateDataResourceConfigRequest) (*tfprotov6.ValidateDataResourceConfigResponse, error) {
ds, ok := d[req.TypeName]
if !ok {
return nil, errUnsupportedDataSource(req.TypeName)
}
return ds.ValidateDataResourceConfig(ctx, req)
}

func (d dataSourceRouter) ReadDataSource(ctx context.Context, req *tfprotov6.ReadDataSourceRequest) (*tfprotov6.ReadDataSourceResponse, error) {
ds, ok := d[req.TypeName]
if !ok {
return nil, errUnsupportedDataSource(req.TypeName)
}
return ds.ReadDataSource(ctx, req)
}
137 changes: 137 additions & 0 deletions internal/echoprovider/echo_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package echoprovider

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

var _ tfprotov6.ResourceServer = echoResource{}

type echoResource struct {
providerConfig *tfprotov6.DynamicValue
}

var echoSchemaType = tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"data": tftypes.DynamicPseudoType,
},
}

func (e echoResource) ApplyResourceChange(ctx context.Context, req *tfprotov6.ApplyResourceChangeRequest) (*tfprotov6.ApplyResourceChangeResponse, error) {
plannedState, err := req.PlannedState.Unmarshal(echoSchemaType)
if err != nil {
return &tfprotov6.ApplyResourceChangeResponse{
Diagnostics: []*tfprotov6.Diagnostic{
{
Severity: tfprotov6.DiagnosticSeverityError,
Detail: fmt.Sprintf("error unmarhsaling planned state data: %s", err.Error()),
},
},
}, nil
}

// Destroy Op, return the null from planned state
if plannedState.IsNull() {
return &tfprotov6.ApplyResourceChangeResponse{
NewState: req.PlannedState,
}, nil
}

if !plannedState.IsFullyKnown() {
return &tfprotov6.ApplyResourceChangeResponse{
Diagnostics: []*tfprotov6.Diagnostic{
{
Severity: tfprotov6.DiagnosticSeverityError,
Detail: "echo_resource encountered an unexpected unknown value, this resource is only meant to echo configuration from the provider config.",
},
},
}, nil
}
// Take the provider config verbatim and put back into state. It shares the same schema
// as the echo resource, so the data types/value should match up and there shouldn't be any
// unknown values present
return &tfprotov6.ApplyResourceChangeResponse{
NewState: e.providerConfig,
}, nil
}

func (e echoResource) ImportResourceState(ctx context.Context, req *tfprotov6.ImportResourceStateRequest) (*tfprotov6.ImportResourceStateResponse, error) {
return &tfprotov6.ImportResourceStateResponse{
Diagnostics: []*tfprotov6.Diagnostic{
{
Severity: tfprotov6.DiagnosticSeverityError,
Detail: "import not supported",
},
},
}, nil
}

func (e echoResource) MoveResourceState(ctx context.Context, req *tfprotov6.MoveResourceStateRequest) (*tfprotov6.MoveResourceStateResponse, error) {
return &tfprotov6.MoveResourceStateResponse{
Diagnostics: []*tfprotov6.Diagnostic{
{
Severity: tfprotov6.DiagnosticSeverityError,
Detail: "move state not supported",
},
},
}, nil
}

func (e echoResource) PlanResourceChange(ctx context.Context, req *tfprotov6.PlanResourceChangeRequest) (*tfprotov6.PlanResourceChangeResponse, error) {
return &tfprotov6.PlanResourceChangeResponse{
PlannedState: req.ProposedNewState,
}, nil
}

func (e echoResource) ReadResource(ctx context.Context, req *tfprotov6.ReadResourceRequest) (*tfprotov6.ReadResourceResponse, error) {
return &tfprotov6.ReadResourceResponse{
NewState: req.CurrentState,
}, nil
}

func (e echoResource) UpgradeResourceState(ctx context.Context, req *tfprotov6.UpgradeResourceStateRequest) (*tfprotov6.UpgradeResourceStateResponse, error) {

rawStateValue, err := req.RawState.UnmarshalWithOpts(
echoSchemaType,
tfprotov6.UnmarshalOpts{
ValueFromJSONOpts: tftypes.ValueFromJSONOpts{
IgnoreUndefinedAttributes: true,
},
},
)

if err != nil {
return &tfprotov6.UpgradeResourceStateResponse{
Diagnostics: []*tfprotov6.Diagnostic{
{
Severity: tfprotov6.DiagnosticSeverityError,
Detail: fmt.Sprintf("error unmarhsaling raw state data: %s", err.Error()),
},
},
}, nil
}

rawDynamicValue, err := tfprotov6.NewDynamicValue(rawStateValue.Type(), rawStateValue)
if err != nil {
return &tfprotov6.UpgradeResourceStateResponse{
Diagnostics: []*tfprotov6.Diagnostic{
{
Severity: tfprotov6.DiagnosticSeverityError,
Detail: fmt.Sprintf("error creating dynamic value from raw state data: %s", err.Error()),
},
},
}, nil
}

return &tfprotov6.UpgradeResourceStateResponse{
UpgradedState: &rawDynamicValue,
Diagnostics: []*tfprotov6.Diagnostic{},
}, nil
}

func (e echoResource) ValidateResourceConfig(ctx context.Context, req *tfprotov6.ValidateResourceConfigRequest) (*tfprotov6.ValidateResourceConfigResponse, error) {
return &tfprotov6.ValidateResourceConfigResponse{}, nil
}
32 changes: 32 additions & 0 deletions internal/echoprovider/function_router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package echoprovider

import (
"context"

"github.com/hashicorp/terraform-plugin-go/tfprotov6"
)

type errUnsupportedFunction string

func (e errUnsupportedFunction) Error() string {
return "unsupported function: " + string(e)
}

type functionRouter map[string]tfprotov6.FunctionServer

func (f functionRouter) CallFunction(ctx context.Context, req *tfprotov6.CallFunctionRequest) (*tfprotov6.CallFunctionResponse, error) {
fu, ok := f[req.Name]

if !ok {
return nil, errUnsupportedFunction(req.Name)
}

return fu.CallFunction(ctx, req)
}

func (f functionRouter) GetFunctions(ctx context.Context, req *tfprotov6.GetFunctionsRequest) (*tfprotov6.GetFunctionsResponse, error) {
panic("not implemented")
}
84 changes: 84 additions & 0 deletions internal/echoprovider/resource_router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package echoprovider

import (
"context"

"github.com/hashicorp/terraform-plugin-go/tfprotov6"
)

type errUnsupportedResource string

func (e errUnsupportedResource) Error() string {
return "unsupported resource: " + string(e)
}

type resourceRouter map[string]tfprotov6.ResourceServer

func (r resourceRouter) ValidateResourceConfig(ctx context.Context, req *tfprotov6.ValidateResourceConfigRequest) (*tfprotov6.ValidateResourceConfigResponse, error) {
res, ok := r[req.TypeName]
if !ok {
return nil, errUnsupportedResource(req.TypeName)
}
return res.ValidateResourceConfig(ctx, req)
}

func (r resourceRouter) UpgradeResourceState(ctx context.Context, req *tfprotov6.UpgradeResourceStateRequest) (*tfprotov6.UpgradeResourceStateResponse, error) {
res, ok := r[req.TypeName]
if !ok {
return nil, errUnsupportedResource(req.TypeName)
}
return res.UpgradeResourceState(ctx, req)
}

func (r resourceRouter) ReadResource(ctx context.Context, req *tfprotov6.ReadResourceRequest) (*tfprotov6.ReadResourceResponse, error) {
res, ok := r[req.TypeName]
if !ok {
return nil, errUnsupportedResource(req.TypeName)
}
return res.ReadResource(ctx, req)
}

func (r resourceRouter) PlanResourceChange(ctx context.Context, req *tfprotov6.PlanResourceChangeRequest) (*tfprotov6.PlanResourceChangeResponse, error) {
res, ok := r[req.TypeName]
if !ok {
return nil, errUnsupportedResource(req.TypeName)
}
return res.PlanResourceChange(ctx, req)
}

func (r resourceRouter) ApplyResourceChange(ctx context.Context, req *tfprotov6.ApplyResourceChangeRequest) (*tfprotov6.ApplyResourceChangeResponse, error) {
res, ok := r[req.TypeName]
if !ok {
return nil, errUnsupportedResource(req.TypeName)
}
return res.ApplyResourceChange(ctx, req)
}

func (r resourceRouter) ImportResourceState(ctx context.Context, req *tfprotov6.ImportResourceStateRequest) (*tfprotov6.ImportResourceStateResponse, error) {
res, ok := r[req.TypeName]
if !ok {
return nil, errUnsupportedResource(req.TypeName)
}
return res.ImportResourceState(ctx, req)
}

func (r resourceRouter) MoveResourceState(ctx context.Context, req *tfprotov6.MoveResourceStateRequest) (*tfprotov6.MoveResourceStateResponse, error) {
_, ok := r[req.TargetTypeName]
if !ok {
return nil, errUnsupportedResource(req.TargetTypeName)
}
// If this support ever needs to be added, this can follow the existing
// pattern of calling res.MoveResourceState(ctx, req).
return &tfprotov6.MoveResourceStateResponse{
Diagnostics: []*tfprotov6.Diagnostic{
{
Severity: tfprotov6.DiagnosticSeverityError,
Summary: "Unsupported Resource Operation",
Detail: "MoveResourceState is not supported by this provider.",
},
},
}, nil
}
Loading

0 comments on commit 44d5ec0

Please sign in to comment.