Skip to content

Commit

Permalink
tfsdk: Migrate server to internal/proto6server package (#310)
Browse files Browse the repository at this point in the history
Reference: #215

This change was accomplished by:

- `mkdir internal/proto6server`
- `git mv tfsdk/serve* internal/proto6server/`
- Replacing `package tfsdk` with `package proto6server` in those moved files
- Removing `NewProtocol6Server()`, `Serve()`, and `ServeOpts` in moved files
- Adding necessary `tfsdk.` scoping to the now external `tfsdk` package references
- Moved necessary unexported methods (e.g. `Schema.tfprotov6Schema()`) to `internal/toproto6` (this package will be expanded further in the future)
- Moved attribute plan modification testing (including test plan modifiers) to `internal/proto6server/attribute_plan_modification_test.go`
- Moved attribute validation testing (including test validators) to `internal/proto6server/attribute_validation_test.go`
- Moved block plan modification testing (including test plan modifiers) to `internal/proto6server/block_plan_modification_test.go`
- Moved block validation testing (including test validators) to `internal/proto6server/block_validation_test.go`
- Copied tfsdk Config/Plan/State.getAttributeValue methods to internal/proto6server (Config/Plan/State)GetAttributeValue functions temporarily to not export existing methods

There are some other planned refactorings in the future for the `internal/proto6server` and `tfsdk` code including:

- Creating a shared implementation for the underlying `Config`/`Plan`/`State` details
- Creating a `internal/fromproto6` package with protocol version 6 type to framework type conversions
- Migrating additional framework type to protocol version 6 code to `internal/toproto6`
- Migrating RPC handling into individual code and test files

These will be handled separately to reduce review burden, as this change was already very large.
  • Loading branch information
bflad committed May 3, 2022
1 parent cd843a9 commit 09acc68
Show file tree
Hide file tree
Showing 58 changed files with 7,968 additions and 7,735 deletions.
7 changes: 7 additions & 0 deletions .changelog/310.txt
@@ -0,0 +1,7 @@
```release-note:breaking-change
tfsdk: The `NewProtocol6Server()` function, `Serve()` function, and `ServeOpts` type have been removed. Use the `providerserver` package instead.
```

```release-note:breaking-change
tfsdk: The `ModifySchemaPlanRequest`, `ModifySchemaPlanResponse`, `ValidateSchemaRequest`, and `ValidateSchemaResponse` types have been removed. These were not intended for provider developer usage.
```
230 changes: 230 additions & 0 deletions internal/proto6server/attribute_plan_modification.go
@@ -0,0 +1,230 @@
package proto6server

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/internal/logging"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// AttributeModifyPlan runs all AttributePlanModifiers
//
// TODO: Clean up this abstraction back into an internal Attribute type method.
// The extra Attribute parameter is a carry-over of creating the proto6server
// package from the tfsdk package and not wanting to export the method.
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/215
func AttributeModifyPlan(ctx context.Context, a tfsdk.Attribute, req tfsdk.ModifyAttributePlanRequest, resp *ModifySchemaPlanResponse) {
ctx = logging.FrameworkWithAttributePath(ctx, req.AttributePath.String())

attrConfig, diags := ConfigGetAttributeValue(ctx, req.Config, req.AttributePath)
resp.Diagnostics.Append(diags...)

// Only on new errors.
if diags.HasError() {
return
}
req.AttributeConfig = attrConfig

attrState, diags := StateGetAttributeValue(ctx, req.State, req.AttributePath)
resp.Diagnostics.Append(diags...)

// Only on new errors.
if diags.HasError() {
return
}
req.AttributeState = attrState

attrPlan, diags := PlanGetAttributeValue(ctx, req.Plan, req.AttributePath)
resp.Diagnostics.Append(diags...)

// Only on new errors.
if diags.HasError() {
return
}
req.AttributePlan = attrPlan

var requiresReplace bool
for _, planModifier := range a.PlanModifiers {
modifyResp := &tfsdk.ModifyAttributePlanResponse{
AttributePlan: req.AttributePlan,
RequiresReplace: requiresReplace,
}

logging.FrameworkDebug(
ctx,
"Calling provider defined AttributePlanModifier",
map[string]interface{}{
logging.KeyDescription: planModifier.Description(ctx),
},
)
planModifier.Modify(ctx, req, modifyResp)
logging.FrameworkDebug(
ctx,
"Called provider defined AttributePlanModifier",
map[string]interface{}{
logging.KeyDescription: planModifier.Description(ctx),
},
)

req.AttributePlan = modifyResp.AttributePlan
resp.Diagnostics.Append(modifyResp.Diagnostics...)
requiresReplace = modifyResp.RequiresReplace

// Only on new errors.
if modifyResp.Diagnostics.HasError() {
return
}
}

if requiresReplace {
resp.RequiresReplace = append(resp.RequiresReplace, req.AttributePath)
}

setAttrDiags := resp.Plan.SetAttribute(ctx, req.AttributePath, req.AttributePlan)
resp.Diagnostics.Append(setAttrDiags...)

if setAttrDiags.HasError() {
return
}

if a.Attributes == nil || len(a.Attributes.GetAttributes()) == 0 {
return
}

nm := a.Attributes.GetNestingMode()
switch nm {
case tfsdk.NestingModeList:
l, ok := req.AttributePlan.(types.List)

if !ok {
err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributePlan, nm, req.AttributePath)
resp.Diagnostics.AddAttributeError(
req.AttributePath,
"Attribute Plan Modification Error",
"Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(),
)

return
}

for idx := range l.Elems {
for name, attr := range a.Attributes.GetAttributes() {
attrReq := tfsdk.ModifyAttributePlanRequest{
AttributePath: req.AttributePath.WithElementKeyInt(idx).WithAttributeName(name),
Config: req.Config,
Plan: resp.Plan,
ProviderMeta: req.ProviderMeta,
State: req.State,
}

AttributeModifyPlan(ctx, attr, attrReq, resp)
}
}
case tfsdk.NestingModeSet:
s, ok := req.AttributePlan.(types.Set)

if !ok {
err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributePlan, nm, req.AttributePath)
resp.Diagnostics.AddAttributeError(
req.AttributePath,
"Attribute Plan Modification Error",
"Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(),
)

return
}

for _, value := range s.Elems {
tfValue, err := value.ToTerraformValue(ctx)
if err != nil {
err := fmt.Errorf("error running ToTerraformValue on element value: %v", value)
resp.Diagnostics.AddAttributeError(
req.AttributePath,
"Attribute Plan Modification Error",
"Attribute plan modification cannot convert element into a Terraform value. Report this to the provider developer:\n\n"+err.Error(),
)

return
}

for name, attr := range a.Attributes.GetAttributes() {
attrReq := tfsdk.ModifyAttributePlanRequest{
AttributePath: req.AttributePath.WithElementKeyValue(tfValue).WithAttributeName(name),
Config: req.Config,
Plan: resp.Plan,
ProviderMeta: req.ProviderMeta,
State: req.State,
}

AttributeModifyPlan(ctx, attr, attrReq, resp)
}
}
case tfsdk.NestingModeMap:
m, ok := req.AttributePlan.(types.Map)

if !ok {
err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributePlan, nm, req.AttributePath)
resp.Diagnostics.AddAttributeError(
req.AttributePath,
"Attribute Plan Modification Error",
"Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(),
)

return
}

for key := range m.Elems {
for name, attr := range a.Attributes.GetAttributes() {
attrReq := tfsdk.ModifyAttributePlanRequest{
AttributePath: req.AttributePath.WithElementKeyString(key).WithAttributeName(name),
Config: req.Config,
Plan: resp.Plan,
ProviderMeta: req.ProviderMeta,
State: req.State,
}

AttributeModifyPlan(ctx, attr, attrReq, resp)
}
}
case tfsdk.NestingModeSingle:
o, ok := req.AttributePlan.(types.Object)

if !ok {
err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributePlan, nm, req.AttributePath)
resp.Diagnostics.AddAttributeError(
req.AttributePath,
"Attribute Plan Modification Error",
"Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(),
)

return
}

if len(o.Attrs) == 0 {
return
}

for name, attr := range a.Attributes.GetAttributes() {
attrReq := tfsdk.ModifyAttributePlanRequest{
AttributePath: req.AttributePath.WithAttributeName(name),
Config: req.Config,
Plan: resp.Plan,
ProviderMeta: req.ProviderMeta,
State: req.State,
}

AttributeModifyPlan(ctx, attr, attrReq, resp)
}
default:
err := fmt.Errorf("unknown attribute nesting mode (%T: %v) at path: %s", nm, nm, req.AttributePath)
resp.Diagnostics.AddAttributeError(
req.AttributePath,
"Attribute Plan Modification Error",
"Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(),
)

return
}
}

0 comments on commit 09acc68

Please sign in to comment.