mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
Previously we were using a mixture of old and new, with our code generation using the plugin from the old github.com/golang/protobuf library but our callers using the modern google.golang.org/protobuf . We were also using pretty ancient version of protoc. This brings us up to the current latest releases and consistently using the new Go protobuf library. There have been some notable changes to these tools in the meantime: Previously the protoc-gen-go plugin handled grpc by having its own additional level of Go-specific "plugins" of which the gRPC codegen was an example. Now the protobuf generator and the gRPC generator are separate plugins handled directly by protoc, which means the command line arguments are a different shape and the gRPC stubs get generated in a separate file from the main protobuf messages, rather than all being in one .pb.go file as before.The results are otherwise similar, though. The grpc codegen now also defaults to requiring that implementations embed the generated "unimplemented" server, which is an implementation of each service where the methods just immediately return the "unimplemented" error. This is not super important for us because we maintain the generated interfaces and their implementations together in the same repository anyway, but adding the "unimplemented" server embeds was not a big change and so seems better to follow the prevailing convention. Using these new versions means that we could in principle now switch to using protobuf edition 2024 and the new "sealed" style for Go code generation, but this commit does not include any such changes and focuses only on getting things upgraded with as few other changes as possible. We can discuss using different codegen style later and deal with that in separate commits. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
121 lines
3.6 KiB
Go
121 lines
3.6 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package grpcwrap
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"github.com/opentofu/opentofu/internal/communicator/shared"
|
|
"github.com/opentofu/opentofu/internal/configs/configschema"
|
|
"github.com/opentofu/opentofu/internal/plugin/convert"
|
|
"github.com/opentofu/opentofu/internal/provisioners"
|
|
"github.com/opentofu/opentofu/internal/tfplugin5"
|
|
)
|
|
|
|
// New wraps a provisioners.Interface to implement a grpc ProviderServer.
|
|
// This is useful for creating a test binary out of an internal provider
|
|
// implementation.
|
|
func Provisioner(p provisioners.Interface) tfplugin5.ProvisionerServer {
|
|
return &provisioner{
|
|
provisioner: p,
|
|
schema: p.GetSchema().Provisioner,
|
|
}
|
|
}
|
|
|
|
type provisioner struct {
|
|
provisioner provisioners.Interface
|
|
schema *configschema.Block
|
|
|
|
tfplugin5.UnimplementedProvisionerServer
|
|
}
|
|
|
|
func (p *provisioner) GetSchema(_ context.Context, req *tfplugin5.GetProvisionerSchema_Request) (*tfplugin5.GetProvisionerSchema_Response, error) {
|
|
resp := &tfplugin5.GetProvisionerSchema_Response{}
|
|
|
|
resp.Provisioner = &tfplugin5.Schema{
|
|
Block: &tfplugin5.Schema_Block{},
|
|
}
|
|
|
|
if p.schema != nil {
|
|
resp.Provisioner.Block = convert.ConfigSchemaToProto(p.schema)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *provisioner) ValidateProvisionerConfig(_ context.Context, req *tfplugin5.ValidateProvisionerConfig_Request) (*tfplugin5.ValidateProvisionerConfig_Response, error) {
|
|
resp := &tfplugin5.ValidateProvisionerConfig_Response{}
|
|
ty := p.schema.ImpliedType()
|
|
|
|
configVal, err := decodeDynamicValue(req.Config, ty)
|
|
if err != nil {
|
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
|
return resp, nil
|
|
}
|
|
|
|
validateResp := p.provisioner.ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{
|
|
Config: configVal,
|
|
})
|
|
|
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics)
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *provisioner) ProvisionResource(req *tfplugin5.ProvisionResource_Request, srv tfplugin5.Provisioner_ProvisionResourceServer) error {
|
|
// We send back a diagnostics over the stream if there was a
|
|
// provisioner-side problem.
|
|
srvResp := &tfplugin5.ProvisionResource_Response{}
|
|
|
|
ty := p.schema.ImpliedType()
|
|
configVal, err := decodeDynamicValue(req.Config, ty)
|
|
if err != nil {
|
|
srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err)
|
|
return srv.Send(srvResp)
|
|
}
|
|
|
|
connVal, err := decodeDynamicValue(req.Connection, shared.ConnectionBlockSupersetSchema.ImpliedType())
|
|
if err != nil {
|
|
srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err)
|
|
return srv.Send(srvResp)
|
|
}
|
|
|
|
resp := p.provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{
|
|
Config: configVal,
|
|
Connection: connVal,
|
|
UIOutput: uiOutput{srv},
|
|
})
|
|
|
|
srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, resp.Diagnostics)
|
|
return srv.Send(srvResp)
|
|
}
|
|
|
|
func (p *provisioner) Stop(context.Context, *tfplugin5.Stop_Request) (*tfplugin5.Stop_Response, error) {
|
|
resp := &tfplugin5.Stop_Response{}
|
|
err := p.provisioner.Stop()
|
|
if err != nil {
|
|
resp.Error = err.Error()
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// uiOutput implements the terraform.UIOutput interface to adapt the grpc
|
|
// stream to the legacy Provisioner.Apply method.
|
|
type uiOutput struct {
|
|
srv tfplugin5.Provisioner_ProvisionResourceServer
|
|
}
|
|
|
|
func (o uiOutput) Output(s string) {
|
|
err := o.srv.Send(&tfplugin5.ProvisionResource_Response{
|
|
Output: strings.ToValidUTF8(s, string(utf8.RuneError)),
|
|
})
|
|
if err != nil {
|
|
log.Printf("[ERROR] %s", err)
|
|
}
|
|
}
|