Skip to content

Commit

Permalink
Use OpenTofu planner with Plugin Framework providers (#2188)
Browse files Browse the repository at this point in the history
OpenTofu planner code is now used with Plugin Framework providers to
emulate Terraform CLI behavior for `objchange.ProposedNew`. Note that
this code was already in use for SDKv2-based resources prior to the
change. The vendored code replaces a custom implementation that is now
removed.

Fixes #2172

Observable changes: Plugin Framework based resources may now reorder
outputs for Set collections.

This change is known to fix bugs such as
#2192 - to be
confirmed with a regression test in a follow-up PR.
  • Loading branch information
t0yv0 authored Jul 26, 2024
1 parent 4df78cb commit 7f2e4c0
Show file tree
Hide file tree
Showing 40 changed files with 7,896 additions and 762 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ issues:
- pkg/tf2pulumi/internal/addrs
- pkg/tf2pulumi/internal/config
- pkg/tf2pulumi/internal/configs
- pkg/vendored
linters-settings:
gci:
sections:
Expand Down
1 change: 1 addition & 0 deletions dynamic/testdata/TestPrimitiveTypes/diff(some).golden
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"changes": "DIFF_SOME",
"diffs": [
"attrNumberComputed",
"attrNumberRequired",
"attrStringDefault",
"attrStringDefaultOverridden",
"attrStringRequired"
Expand Down
2 changes: 1 addition & 1 deletion pf/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/zclconf/go-cty v1.14.2 // indirect
github.com/zclconf/go-cty v1.14.2
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
gocloud.dev v0.37.0 // indirect
Expand Down
119 changes: 119 additions & 0 deletions pf/internal/pfutils/convert_rschema_to_proto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2016-2024, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pfutils

import (
"context"
"errors"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-framework/resource"
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
)

// Assist converting Plugin framework schemata to proto schemata for a resource.
func convertResourceSchemaToProto(ctx context.Context, s *rschema.Schema) (*tfprotov6.Schema, error) {
p := &singleResourceProvider{s}

mk := providerserver.NewProtocol6WithError(p)
srv, err := mk()
if err != nil {
return nil, err
}
resp, err := srv.GetProviderSchema(ctx, &tfprotov6.GetProviderSchemaRequest{})
if err != nil {
return nil, err
}
var diagErrors []error
for _, d := range resp.Diagnostics {
if d.Severity == tfprotov6.DiagnosticSeverityError {
diagErrors = append(diagErrors, d.Attribute.NewErrorf("%s\n%s", d.Summary, d.Detail))
}
}
if err := errors.Join(diagErrors...); err != nil {
return nil, err
}
for _, r := range resp.ResourceSchemas {
return r, nil
}
return nil, fmt.Errorf("GetProviderSchema did not return any resource schemas")
}

type singleResourceProvider struct {
resourceSchema *rschema.Schema
}

var _ provider.Provider = &singleResourceProvider{}

func (srp singleResourceProvider) Metadata(
ctx context.Context,
req provider.MetadataRequest,
resp *provider.MetadataResponse,
) {
resp.TypeName = "p"
}

func (srp singleResourceProvider) Schema(context.Context, provider.SchemaRequest, *provider.SchemaResponse) {
}

func (srp singleResourceProvider) Configure(context.Context, provider.ConfigureRequest, *provider.ConfigureResponse) {
}

func (srp singleResourceProvider) DataSources(context.Context) []func() datasource.DataSource {
return nil
}

func (srp singleResourceProvider) Resources(context.Context) []func() resource.Resource {
mk := func() resource.Resource {
return &schemaOnlyResource{srp.resourceSchema}
}
return []func() resource.Resource{mk}
}

type schemaOnlyResource struct {
schema *rschema.Schema
}

var _ resource.Resource = &schemaOnlyResource{}

func (r *schemaOnlyResource) Metadata(
ctx context.Context,
req resource.MetadataRequest,
resp *resource.MetadataResponse,
) {
resp.TypeName = "r"
}

func (r *schemaOnlyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
if r.schema != nil {
resp.Schema = *r.schema
}
}

func (r *schemaOnlyResource) Create(context.Context, resource.CreateRequest, *resource.CreateResponse) {
}

func (r *schemaOnlyResource) Read(context.Context, resource.ReadRequest, *resource.ReadResponse) {
}

func (r *schemaOnlyResource) Update(context.Context, resource.UpdateRequest, *resource.UpdateResponse) {
}

func (r *schemaOnlyResource) Delete(context.Context, resource.DeleteRequest, *resource.DeleteResponse) {
}
Loading

0 comments on commit 7f2e4c0

Please sign in to comment.