2021-02-05 21:40:58 +01:00
|
|
|
package plugin6
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
|
|
|
|
plugin "github.com/hashicorp/go-plugin"
|
|
|
|
"github.com/hashicorp/terraform/internal/logging"
|
2021-05-17 21:35:22 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/plugin6/convert"
|
2021-05-17 19:40:40 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/providers"
|
2021-02-05 21:40:58 +01:00
|
|
|
proto6 "github.com/hashicorp/terraform/internal/tfplugin6"
|
|
|
|
ctyjson "github.com/zclconf/go-cty/cty/json"
|
|
|
|
"github.com/zclconf/go-cty/cty/msgpack"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
)
|
|
|
|
|
|
|
|
var logger = logging.HCLogger()
|
|
|
|
|
|
|
|
// GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package.
|
|
|
|
type GRPCProviderPlugin struct {
|
|
|
|
plugin.Plugin
|
|
|
|
GRPCProvider func() proto6.ProviderServer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
|
|
|
return &GRPCProvider{
|
|
|
|
client: proto6.NewProviderClient(c),
|
|
|
|
ctx: ctx,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
|
|
|
|
proto6.RegisterProviderServer(s, p.GRPCProvider())
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GRPCProvider handles the client, or core side of the plugin rpc connection.
|
|
|
|
// The GRPCProvider methods are mostly a translation layer between the
|
2021-02-22 16:22:45 +01:00
|
|
|
// terraform providers types and the grpc proto types, directly converting
|
2021-02-05 21:40:58 +01:00
|
|
|
// between the two.
|
|
|
|
type GRPCProvider struct {
|
|
|
|
// PluginClient provides a reference to the plugin.Client which controls the plugin process.
|
|
|
|
// This allows the GRPCProvider a way to shutdown the plugin process.
|
|
|
|
PluginClient *plugin.Client
|
|
|
|
|
|
|
|
// TestServer contains a grpc.Server to close when the GRPCProvider is being
|
|
|
|
// used in an end to end test of a provider.
|
|
|
|
TestServer *grpc.Server
|
|
|
|
|
|
|
|
// Proto client use to make the grpc service calls.
|
|
|
|
client proto6.ProviderClient
|
|
|
|
|
|
|
|
// this context is created by the plugin package, and is canceled when the
|
|
|
|
// plugin process ends.
|
|
|
|
ctx context.Context
|
|
|
|
|
|
|
|
// schema stores the schema for this provider. This is used to properly
|
|
|
|
// serialize the state for requests.
|
|
|
|
mu sync.Mutex
|
2021-02-18 16:13:43 +01:00
|
|
|
schemas providers.GetProviderSchemaResponse
|
2021-02-05 21:40:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func New(client proto6.ProviderClient, ctx context.Context) GRPCProvider {
|
|
|
|
return GRPCProvider{
|
|
|
|
client: client,
|
|
|
|
ctx: ctx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// getSchema is used internally to get the saved provider schema. The schema
|
|
|
|
// should have already been fetched from the provider, but we have to
|
2021-02-22 16:22:45 +01:00
|
|
|
// synchronize access to avoid being called concurrently with GetProviderSchema.
|
2021-02-18 16:13:43 +01:00
|
|
|
func (p *GRPCProvider) getSchema() providers.GetProviderSchemaResponse {
|
2021-02-05 21:40:58 +01:00
|
|
|
p.mu.Lock()
|
2021-02-22 16:22:45 +01:00
|
|
|
// unlock inline in case GetProviderSchema needs to be called
|
2021-02-05 21:40:58 +01:00
|
|
|
if p.schemas.Provider.Block != nil {
|
|
|
|
p.mu.Unlock()
|
|
|
|
return p.schemas
|
|
|
|
}
|
|
|
|
p.mu.Unlock()
|
|
|
|
|
|
|
|
// the schema should have been fetched already, but give it another shot
|
|
|
|
// just in case things are being called out of order. This may happen for
|
|
|
|
// tests.
|
2021-02-22 16:22:45 +01:00
|
|
|
schemas := p.GetProviderSchema()
|
2021-02-05 21:40:58 +01:00
|
|
|
if schemas.Diagnostics.HasErrors() {
|
|
|
|
panic(schemas.Diagnostics.Err())
|
|
|
|
}
|
|
|
|
|
|
|
|
return schemas
|
|
|
|
}
|
|
|
|
|
|
|
|
// getResourceSchema is a helper to extract the schema for a resource, and
|
|
|
|
// panics if the schema is not available.
|
|
|
|
func (p *GRPCProvider) getResourceSchema(name string) providers.Schema {
|
|
|
|
schema := p.getSchema()
|
|
|
|
resSchema, ok := schema.ResourceTypes[name]
|
|
|
|
if !ok {
|
|
|
|
panic("unknown resource type " + name)
|
|
|
|
}
|
|
|
|
return resSchema
|
|
|
|
}
|
|
|
|
|
|
|
|
// gettDatasourceSchema is a helper to extract the schema for a datasource, and
|
|
|
|
// panics if that schema is not available.
|
|
|
|
func (p *GRPCProvider) getDatasourceSchema(name string) providers.Schema {
|
|
|
|
schema := p.getSchema()
|
|
|
|
dataSchema, ok := schema.DataSources[name]
|
|
|
|
if !ok {
|
|
|
|
panic("unknown data source " + name)
|
|
|
|
}
|
|
|
|
return dataSchema
|
|
|
|
}
|
|
|
|
|
|
|
|
// getProviderMetaSchema is a helper to extract the schema for the meta info
|
|
|
|
// defined for a provider,
|
|
|
|
func (p *GRPCProvider) getProviderMetaSchema() providers.Schema {
|
|
|
|
schema := p.getSchema()
|
|
|
|
return schema.ProviderMeta
|
|
|
|
}
|
|
|
|
|
2021-02-22 16:22:45 +01:00
|
|
|
func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: GetProviderSchema")
|
2021-02-05 21:40:58 +01:00
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
|
|
|
|
if p.schemas.Provider.Block != nil {
|
|
|
|
return p.schemas
|
|
|
|
}
|
|
|
|
|
|
|
|
resp.ResourceTypes = make(map[string]providers.Schema)
|
|
|
|
resp.DataSources = make(map[string]providers.Schema)
|
|
|
|
|
|
|
|
// Some providers may generate quite large schemas, and the internal default
|
|
|
|
// grpc response size limit is 4MB. 64MB should cover most any use case, and
|
|
|
|
// if we get providers nearing that we may want to consider a finer-grained
|
|
|
|
// API to fetch individual resource schemas.
|
|
|
|
// Note: this option is marked as EXPERIMENTAL in the grpc API.
|
|
|
|
const maxRecvSize = 64 << 20
|
|
|
|
protoResp, err := p.client.GetProviderSchema(p.ctx, new(proto6.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize})
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
|
|
|
|
if protoResp.Provider == nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provider schema"))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider)
|
|
|
|
if protoResp.ProviderMeta == nil {
|
|
|
|
logger.Debug("No provider meta schema returned")
|
|
|
|
} else {
|
|
|
|
resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta)
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, res := range protoResp.ResourceSchemas {
|
|
|
|
resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, data := range protoResp.DataSourceSchemas {
|
|
|
|
resp.DataSources[name] = convert.ProtoToProviderSchema(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
p.schemas = resp
|
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
2021-02-18 16:13:43 +01:00
|
|
|
func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
2021-02-05 21:40:58 +01:00
|
|
|
logger.Trace("GRPCProvider.v6: ValidateProviderConfig")
|
|
|
|
|
|
|
|
schema := p.getSchema()
|
|
|
|
ty := schema.Provider.Block.ImpliedType()
|
|
|
|
|
|
|
|
mp, err := msgpack.Marshal(r.Config, ty)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
protoReq := &proto6.ValidateProviderConfig_Request{
|
|
|
|
Config: &proto6.DynamicValue{Msgpack: mp},
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.ValidateProviderConfig(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
2021-02-18 16:13:43 +01:00
|
|
|
func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: ValidateResourceConfig")
|
2021-02-05 21:40:58 +01:00
|
|
|
resourceSchema := p.getResourceSchema(r.TypeName)
|
|
|
|
|
|
|
|
mp, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
protoReq := &proto6.ValidateResourceConfig_Request{
|
|
|
|
TypeName: r.TypeName,
|
|
|
|
Config: &proto6.DynamicValue{Msgpack: mp},
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.ValidateResourceConfig(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
2021-02-24 18:04:28 +01:00
|
|
|
func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: ValidateDataResourceConfig")
|
2021-02-05 21:40:58 +01:00
|
|
|
|
|
|
|
dataSchema := p.getDatasourceSchema(r.TypeName)
|
|
|
|
|
|
|
|
mp, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
2021-02-24 18:04:28 +01:00
|
|
|
protoReq := &proto6.ValidateDataResourceConfig_Request{
|
2021-02-05 21:40:58 +01:00
|
|
|
TypeName: r.TypeName,
|
|
|
|
Config: &proto6.DynamicValue{Msgpack: mp},
|
|
|
|
}
|
|
|
|
|
2021-02-24 18:04:28 +01:00
|
|
|
protoResp, err := p.client.ValidateDataResourceConfig(p.ctx, protoReq)
|
2021-02-05 21:40:58 +01:00
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: UpgradeResourceState")
|
|
|
|
|
|
|
|
resSchema := p.getResourceSchema(r.TypeName)
|
|
|
|
|
|
|
|
protoReq := &proto6.UpgradeResourceState_Request{
|
|
|
|
TypeName: r.TypeName,
|
|
|
|
Version: int64(r.Version),
|
|
|
|
RawState: &proto6.RawState{
|
|
|
|
Json: r.RawStateJSON,
|
|
|
|
Flatmap: r.RawStateFlatmap,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.UpgradeResourceState(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
|
|
|
|
ty := resSchema.Block.ImpliedType()
|
|
|
|
resp.UpgradedState = cty.NullVal(ty)
|
|
|
|
if protoResp.UpgradedState == nil {
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
state, err := decodeDynamicValue(protoResp.UpgradedState, ty)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.UpgradedState = state
|
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
2021-02-22 16:22:45 +01:00
|
|
|
func (p *GRPCProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: ConfigureProvider")
|
2021-02-05 21:40:58 +01:00
|
|
|
|
|
|
|
schema := p.getSchema()
|
|
|
|
|
|
|
|
var mp []byte
|
|
|
|
|
|
|
|
// we don't have anything to marshal if there's no config
|
|
|
|
mp, err := msgpack.Marshal(r.Config, schema.Provider.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
protoReq := &proto6.ConfigureProvider_Request{
|
|
|
|
TerraformVersion: r.TerraformVersion,
|
|
|
|
Config: &proto6.DynamicValue{
|
|
|
|
Msgpack: mp,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.ConfigureProvider(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProvider) Stop() error {
|
|
|
|
logger.Trace("GRPCProvider.v6: Stop")
|
|
|
|
|
|
|
|
resp, err := p.client.StopProvider(p.ctx, new(proto6.StopProvider_Request))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.Error != "" {
|
|
|
|
return errors.New(resp.Error)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: ReadResource")
|
|
|
|
|
|
|
|
resSchema := p.getResourceSchema(r.TypeName)
|
|
|
|
metaSchema := p.getProviderMetaSchema()
|
|
|
|
|
|
|
|
mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
protoReq := &proto6.ReadResource_Request{
|
|
|
|
TypeName: r.TypeName,
|
|
|
|
CurrentState: &proto6.DynamicValue{Msgpack: mp},
|
|
|
|
Private: r.Private,
|
|
|
|
}
|
|
|
|
|
|
|
|
if metaSchema.Block != nil {
|
|
|
|
metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.ReadResource(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
|
|
|
|
state, err := decodeDynamicValue(protoResp.NewState, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.NewState = state
|
|
|
|
resp.Private = protoResp.Private
|
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: PlanResourceChange")
|
|
|
|
|
|
|
|
resSchema := p.getResourceSchema(r.TypeName)
|
|
|
|
metaSchema := p.getProviderMetaSchema()
|
|
|
|
|
|
|
|
priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
propMP, err := msgpack.Marshal(r.ProposedNewState, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
protoReq := &proto6.PlanResourceChange_Request{
|
|
|
|
TypeName: r.TypeName,
|
|
|
|
PriorState: &proto6.DynamicValue{Msgpack: priorMP},
|
|
|
|
Config: &proto6.DynamicValue{Msgpack: configMP},
|
|
|
|
ProposedNewState: &proto6.DynamicValue{Msgpack: propMP},
|
|
|
|
PriorPrivate: r.PriorPrivate,
|
|
|
|
}
|
|
|
|
|
|
|
|
if metaSchema.Block != nil {
|
|
|
|
metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
|
|
|
|
state, err := decodeDynamicValue(protoResp.PlannedState, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.PlannedState = state
|
|
|
|
|
|
|
|
for _, p := range protoResp.RequiresReplace {
|
|
|
|
resp.RequiresReplace = append(resp.RequiresReplace, convert.AttributePathToPath(p))
|
|
|
|
}
|
|
|
|
|
|
|
|
resp.PlannedPrivate = protoResp.PlannedPrivate
|
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: ApplyResourceChange")
|
|
|
|
|
|
|
|
resSchema := p.getResourceSchema(r.TypeName)
|
|
|
|
metaSchema := p.getProviderMetaSchema()
|
|
|
|
|
|
|
|
priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
plannedMP, err := msgpack.Marshal(r.PlannedState, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
protoReq := &proto6.ApplyResourceChange_Request{
|
|
|
|
TypeName: r.TypeName,
|
|
|
|
PriorState: &proto6.DynamicValue{Msgpack: priorMP},
|
|
|
|
PlannedState: &proto6.DynamicValue{Msgpack: plannedMP},
|
|
|
|
Config: &proto6.DynamicValue{Msgpack: configMP},
|
|
|
|
PlannedPrivate: r.PlannedPrivate,
|
|
|
|
}
|
|
|
|
|
|
|
|
if metaSchema.Block != nil {
|
|
|
|
metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
|
|
|
|
resp.Private = protoResp.Private
|
|
|
|
|
|
|
|
state, err := decodeDynamicValue(protoResp.NewState, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.NewState = state
|
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: ImportResourceState")
|
|
|
|
|
|
|
|
protoReq := &proto6.ImportResourceState_Request{
|
|
|
|
TypeName: r.TypeName,
|
|
|
|
Id: r.ID,
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.ImportResourceState(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
|
|
|
|
for _, imported := range protoResp.ImportedResources {
|
|
|
|
resource := providers.ImportedResource{
|
|
|
|
TypeName: imported.TypeName,
|
|
|
|
Private: imported.Private,
|
|
|
|
}
|
|
|
|
|
|
|
|
resSchema := p.getResourceSchema(resource.TypeName)
|
|
|
|
state, err := decodeDynamicValue(imported.State, resSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resource.State = state
|
|
|
|
resp.ImportedResources = append(resp.ImportedResources, resource)
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
|
|
|
|
logger.Trace("GRPCProvider.v6: ReadDataSource")
|
|
|
|
|
|
|
|
dataSchema := p.getDatasourceSchema(r.TypeName)
|
|
|
|
metaSchema := p.getProviderMetaSchema()
|
|
|
|
|
|
|
|
config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
protoReq := &proto6.ReadDataSource_Request{
|
|
|
|
TypeName: r.TypeName,
|
|
|
|
Config: &proto6.DynamicValue{
|
|
|
|
Msgpack: config,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if metaSchema.Block != nil {
|
|
|
|
metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
|
|
|
|
}
|
|
|
|
|
|
|
|
protoResp, err := p.client.ReadDataSource(p.ctx, protoReq)
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
|
|
|
|
|
|
|
state, err := decodeDynamicValue(protoResp.State, dataSchema.Block.ImpliedType())
|
|
|
|
if err != nil {
|
|
|
|
resp.Diagnostics = resp.Diagnostics.Append(err)
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
resp.State = state
|
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
// closing the grpc connection is final, and terraform will call it at the end of every phase.
|
|
|
|
func (p *GRPCProvider) Close() error {
|
|
|
|
logger.Trace("GRPCProvider.v6: Close")
|
|
|
|
|
|
|
|
// Make sure to stop the server if we're not running within go-plugin.
|
|
|
|
if p.TestServer != nil {
|
|
|
|
p.TestServer.Stop()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check this since it's not automatically inserted during plugin creation.
|
|
|
|
// It's currently only inserted by the command package, because that is
|
|
|
|
// where the factory is built and is the only point with access to the
|
|
|
|
// plugin.Client.
|
|
|
|
if p.PluginClient == nil {
|
|
|
|
logger.Debug("provider has no plugin.Client")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
p.PluginClient.Kill()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode a DynamicValue from either the JSON or MsgPack encoding.
|
|
|
|
func decodeDynamicValue(v *proto6.DynamicValue, ty cty.Type) (cty.Value, error) {
|
|
|
|
// always return a valid value
|
|
|
|
var err error
|
|
|
|
res := cty.NullVal(ty)
|
|
|
|
if v == nil {
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case len(v.Msgpack) > 0:
|
|
|
|
res, err = msgpack.Unmarshal(v.Msgpack, ty)
|
|
|
|
case len(v.Json) > 0:
|
|
|
|
res, err = ctyjson.Unmarshal(v.Json, ty)
|
|
|
|
}
|
|
|
|
return res, err
|
|
|
|
}
|