github.com/opentofu/opentofu@v1.7.1/internal/plugin/grpc_provider.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package plugin
     7  
     8  import (
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"sync"
    13  
    14  	"github.com/zclconf/go-cty/cty"
    15  
    16  	plugin "github.com/hashicorp/go-plugin"
    17  	ctyjson "github.com/zclconf/go-cty/cty/json"
    18  	"github.com/zclconf/go-cty/cty/msgpack"
    19  	"google.golang.org/grpc"
    20  
    21  	"github.com/opentofu/opentofu/internal/addrs"
    22  	"github.com/opentofu/opentofu/internal/logging"
    23  	"github.com/opentofu/opentofu/internal/plugin/convert"
    24  	"github.com/opentofu/opentofu/internal/providers"
    25  	proto "github.com/opentofu/opentofu/internal/tfplugin5"
    26  )
    27  
    28  var logger = logging.HCLogger()
    29  
    30  // GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package.
    31  type GRPCProviderPlugin struct {
    32  	plugin.Plugin
    33  	GRPCProvider func() proto.ProviderServer
    34  }
    35  
    36  func (p *GRPCProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
    37  	return &GRPCProvider{
    38  		client: proto.NewProviderClient(c),
    39  		ctx:    ctx,
    40  	}, nil
    41  }
    42  
    43  func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
    44  	proto.RegisterProviderServer(s, p.GRPCProvider())
    45  	return nil
    46  }
    47  
    48  // GRPCProvider handles the client, or core side of the plugin rpc connection.
    49  // The GRPCProvider methods are mostly a translation layer between the
    50  // tofu providers types and the grpc proto types, directly converting
    51  // between the two.
    52  type GRPCProvider struct {
    53  	// PluginClient provides a reference to the plugin.Client which controls the plugin process.
    54  	// This allows the GRPCProvider a way to shutdown the plugin process.
    55  	PluginClient *plugin.Client
    56  
    57  	// TestServer contains a grpc.Server to close when the GRPCProvider is being
    58  	// used in an end to end test of a provider.
    59  	TestServer *grpc.Server
    60  
    61  	// Addr uniquely identifies the type of provider.
    62  	// Normally executed providers will have this set during initialization,
    63  	// but it may not always be available for alternative execute modes.
    64  	Addr addrs.Provider
    65  
    66  	// Proto client use to make the grpc service calls.
    67  	client proto.ProviderClient
    68  
    69  	// this context is created by the plugin package, and is canceled when the
    70  	// plugin process ends.
    71  	ctx context.Context
    72  
    73  	// schema stores the schema for this provider. This is used to properly
    74  	// serialize the requests for schemas.
    75  	mu     sync.Mutex
    76  	schema providers.GetProviderSchemaResponse
    77  }
    78  
    79  var _ providers.Interface = new(GRPCProvider)
    80  
    81  func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResponse) {
    82  	logger.Trace("GRPCProvider: GetProviderSchema")
    83  	p.mu.Lock()
    84  	defer p.mu.Unlock()
    85  
    86  	// First, we check the global cache.
    87  	// The cache could contain this schema if an instance of this provider has previously been started.
    88  	if !p.Addr.IsZero() {
    89  		// Even if the schema is cached, GetProviderSchemaOptional could be false. This would indicate that once instantiated,
    90  		// this provider requires the get schema call to be made at least once, as it handles part of the provider's setup.
    91  		// At this point, we don't know if this is the first call to a provider instance or not, so we don't use the result in that case.
    92  		if schemaCached, ok := providers.SchemaCache.Get(p.Addr); ok && schemaCached.ServerCapabilities.GetProviderSchemaOptional {
    93  			logger.Trace("GRPCProvider: GetProviderSchema: serving from global schema cache", "address", p.Addr)
    94  			return schemaCached
    95  		}
    96  	}
    97  
    98  	// If the local cache is non-zero, we know this instance has called
    99  	// GetProviderSchema at least once, so has satisfied the possible requirement of `GetProviderSchemaOptional=false`.
   100  	// This means that we can return early now using the locally cached schema, without making this call again.
   101  	if p.schema.Provider.Block != nil {
   102  		return p.schema
   103  	}
   104  
   105  	resp.ResourceTypes = make(map[string]providers.Schema)
   106  	resp.DataSources = make(map[string]providers.Schema)
   107  	resp.Functions = make(map[string]providers.FunctionSpec)
   108  
   109  	// Some providers may generate quite large schemas, and the internal default
   110  	// grpc response size limit is 4MB. 64MB should cover most any use case, and
   111  	// if we get providers nearing that we may want to consider a finer-grained
   112  	// API to fetch individual resource schemas.
   113  	// Note: this option is marked as EXPERIMENTAL in the grpc API. We keep
   114  	// this for compatibility, but recent providers all set the max message
   115  	// size much higher on the server side, which is the supported method for
   116  	// determining payload size.
   117  	const maxRecvSize = 64 << 20
   118  	protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize})
   119  	if err != nil {
   120  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   121  		return resp
   122  	}
   123  
   124  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   125  
   126  	if resp.Diagnostics.HasErrors() {
   127  		return resp
   128  	}
   129  
   130  	if protoResp.Provider == nil {
   131  		resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provider schema"))
   132  		return resp
   133  	}
   134  
   135  	resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider)
   136  	if protoResp.ProviderMeta == nil {
   137  		logger.Debug("No provider meta schema returned")
   138  	} else {
   139  		resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta)
   140  	}
   141  
   142  	for name, res := range protoResp.ResourceSchemas {
   143  		resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res)
   144  	}
   145  
   146  	for name, data := range protoResp.DataSourceSchemas {
   147  		resp.DataSources[name] = convert.ProtoToProviderSchema(data)
   148  	}
   149  
   150  	for name, fn := range protoResp.Functions {
   151  		resp.Functions[name] = convert.ProtoToFunctionSpec(fn)
   152  	}
   153  
   154  	if protoResp.ServerCapabilities != nil {
   155  		resp.ServerCapabilities.PlanDestroy = protoResp.ServerCapabilities.PlanDestroy
   156  		resp.ServerCapabilities.GetProviderSchemaOptional = protoResp.ServerCapabilities.GetProviderSchemaOptional
   157  	}
   158  
   159  	// Set the global provider cache so that future calls to this provider can use the cached value.
   160  	// Crucially, this doesn't look at GetProviderSchemaOptional, because the layers above could use this cache
   161  	// *without* creating an instance of this provider. And if there is no instance,
   162  	// then we don't need to set up anything (cause there is nothing to set up), so we need no call
   163  	// to the providers GetSchema rpc.
   164  	if !p.Addr.IsZero() {
   165  		providers.SchemaCache.Set(p.Addr, resp)
   166  	}
   167  
   168  	// Always store this here in the client for providers that are not able to use GetProviderSchemaOptional.
   169  	// Crucially, this indicates that we've made at least one call to GetProviderSchema to this instance of the provider,
   170  	// which means in the future we'll be able to return using this cache
   171  	// (because the possible setup contained in the GetProviderSchema call has happened).
   172  	// If GetProviderSchemaOptional is true then this cache won't actually ever be used, because the calls to this method
   173  	// will be satisfied by the global provider cache.
   174  	p.schema = resp
   175  
   176  	return resp
   177  }
   178  
   179  func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
   180  	logger.Trace("GRPCProvider: ValidateProviderConfig")
   181  
   182  	schema := p.GetProviderSchema()
   183  	if schema.Diagnostics.HasErrors() {
   184  		resp.Diagnostics = schema.Diagnostics
   185  		return resp
   186  	}
   187  
   188  	ty := schema.Provider.Block.ImpliedType()
   189  
   190  	mp, err := msgpack.Marshal(r.Config, ty)
   191  	if err != nil {
   192  		resp.Diagnostics = resp.Diagnostics.Append(err)
   193  		return resp
   194  	}
   195  
   196  	protoReq := &proto.PrepareProviderConfig_Request{
   197  		Config: &proto.DynamicValue{Msgpack: mp},
   198  	}
   199  
   200  	protoResp, err := p.client.PrepareProviderConfig(p.ctx, protoReq)
   201  	if err != nil {
   202  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   203  		return resp
   204  	}
   205  
   206  	config, err := decodeDynamicValue(protoResp.PreparedConfig, ty)
   207  	if err != nil {
   208  		resp.Diagnostics = resp.Diagnostics.Append(err)
   209  		return resp
   210  	}
   211  	resp.PreparedConfig = config
   212  
   213  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   214  	return resp
   215  }
   216  
   217  func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) {
   218  	logger.Trace("GRPCProvider: ValidateResourceConfig")
   219  
   220  	schema := p.GetProviderSchema()
   221  	if schema.Diagnostics.HasErrors() {
   222  		resp.Diagnostics = schema.Diagnostics
   223  		return resp
   224  	}
   225  
   226  	resourceSchema, ok := schema.ResourceTypes[r.TypeName]
   227  	if !ok {
   228  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   229  		return resp
   230  	}
   231  
   232  	mp, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType())
   233  	if err != nil {
   234  		resp.Diagnostics = resp.Diagnostics.Append(err)
   235  		return resp
   236  	}
   237  
   238  	protoReq := &proto.ValidateResourceTypeConfig_Request{
   239  		TypeName: r.TypeName,
   240  		Config:   &proto.DynamicValue{Msgpack: mp},
   241  	}
   242  
   243  	protoResp, err := p.client.ValidateResourceTypeConfig(p.ctx, protoReq)
   244  	if err != nil {
   245  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   246  		return resp
   247  	}
   248  
   249  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   250  	return resp
   251  }
   252  
   253  func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) {
   254  	logger.Trace("GRPCProvider: ValidateDataResourceConfig")
   255  
   256  	schema := p.GetProviderSchema()
   257  	if schema.Diagnostics.HasErrors() {
   258  		resp.Diagnostics = schema.Diagnostics
   259  		return resp
   260  	}
   261  
   262  	dataSchema, ok := schema.DataSources[r.TypeName]
   263  	if !ok {
   264  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown data source %q", r.TypeName))
   265  		return resp
   266  	}
   267  
   268  	mp, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
   269  	if err != nil {
   270  		resp.Diagnostics = resp.Diagnostics.Append(err)
   271  		return resp
   272  	}
   273  
   274  	protoReq := &proto.ValidateDataSourceConfig_Request{
   275  		TypeName: r.TypeName,
   276  		Config:   &proto.DynamicValue{Msgpack: mp},
   277  	}
   278  
   279  	protoResp, err := p.client.ValidateDataSourceConfig(p.ctx, protoReq)
   280  	if err != nil {
   281  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   282  		return resp
   283  	}
   284  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   285  	return resp
   286  }
   287  
   288  func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) {
   289  	logger.Trace("GRPCProvider: UpgradeResourceState")
   290  
   291  	schema := p.GetProviderSchema()
   292  	if schema.Diagnostics.HasErrors() {
   293  		resp.Diagnostics = schema.Diagnostics
   294  		return resp
   295  	}
   296  
   297  	resSchema, ok := schema.ResourceTypes[r.TypeName]
   298  	if !ok {
   299  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   300  		return resp
   301  	}
   302  
   303  	protoReq := &proto.UpgradeResourceState_Request{
   304  		TypeName: r.TypeName,
   305  		Version:  int64(r.Version),
   306  		RawState: &proto.RawState{
   307  			Json:    r.RawStateJSON,
   308  			Flatmap: r.RawStateFlatmap,
   309  		},
   310  	}
   311  
   312  	protoResp, err := p.client.UpgradeResourceState(p.ctx, protoReq)
   313  	if err != nil {
   314  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   315  		return resp
   316  	}
   317  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   318  
   319  	ty := resSchema.Block.ImpliedType()
   320  	resp.UpgradedState = cty.NullVal(ty)
   321  	if protoResp.UpgradedState == nil {
   322  		return resp
   323  	}
   324  
   325  	state, err := decodeDynamicValue(protoResp.UpgradedState, ty)
   326  	if err != nil {
   327  		resp.Diagnostics = resp.Diagnostics.Append(err)
   328  		return resp
   329  	}
   330  	resp.UpgradedState = state
   331  
   332  	return resp
   333  }
   334  
   335  func (p *GRPCProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
   336  	logger.Trace("GRPCProvider: ConfigureProvider")
   337  
   338  	schema := p.GetProviderSchema()
   339  	if schema.Diagnostics.HasErrors() {
   340  		resp.Diagnostics = schema.Diagnostics
   341  		return resp
   342  	}
   343  
   344  	var mp []byte
   345  
   346  	// we don't have anything to marshal if there's no config
   347  	mp, err := msgpack.Marshal(r.Config, schema.Provider.Block.ImpliedType())
   348  	if err != nil {
   349  		resp.Diagnostics = resp.Diagnostics.Append(err)
   350  		return resp
   351  	}
   352  
   353  	protoReq := &proto.Configure_Request{
   354  		TerraformVersion: r.TerraformVersion,
   355  		Config: &proto.DynamicValue{
   356  			Msgpack: mp,
   357  		},
   358  	}
   359  
   360  	protoResp, err := p.client.Configure(p.ctx, protoReq)
   361  	if err != nil {
   362  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   363  		return resp
   364  	}
   365  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   366  	return resp
   367  }
   368  
   369  func (p *GRPCProvider) Stop() error {
   370  	logger.Trace("GRPCProvider: Stop")
   371  
   372  	resp, err := p.client.Stop(p.ctx, new(proto.Stop_Request))
   373  	if err != nil {
   374  		return err
   375  	}
   376  
   377  	if resp.Error != "" {
   378  		return errors.New(resp.Error)
   379  	}
   380  	return nil
   381  }
   382  
   383  func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
   384  	logger.Trace("GRPCProvider: ReadResource")
   385  
   386  	schema := p.GetProviderSchema()
   387  	if schema.Diagnostics.HasErrors() {
   388  		resp.Diagnostics = schema.Diagnostics
   389  		return resp
   390  	}
   391  
   392  	resSchema, ok := schema.ResourceTypes[r.TypeName]
   393  	if !ok {
   394  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type " + r.TypeName))
   395  		return resp
   396  	}
   397  
   398  	metaSchema := schema.ProviderMeta
   399  
   400  	mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
   401  	if err != nil {
   402  		resp.Diagnostics = resp.Diagnostics.Append(err)
   403  		return resp
   404  	}
   405  
   406  	protoReq := &proto.ReadResource_Request{
   407  		TypeName:     r.TypeName,
   408  		CurrentState: &proto.DynamicValue{Msgpack: mp},
   409  		Private:      r.Private,
   410  	}
   411  
   412  	if metaSchema.Block != nil {
   413  		metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
   414  		if err != nil {
   415  			resp.Diagnostics = resp.Diagnostics.Append(err)
   416  			return resp
   417  		}
   418  		protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
   419  	}
   420  
   421  	protoResp, err := p.client.ReadResource(p.ctx, protoReq)
   422  	if err != nil {
   423  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   424  		return resp
   425  	}
   426  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   427  
   428  	state, err := decodeDynamicValue(protoResp.NewState, resSchema.Block.ImpliedType())
   429  	if err != nil {
   430  		resp.Diagnostics = resp.Diagnostics.Append(err)
   431  		return resp
   432  	}
   433  	resp.NewState = state
   434  	resp.Private = protoResp.Private
   435  
   436  	return resp
   437  }
   438  
   439  func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
   440  	logger.Trace("GRPCProvider: PlanResourceChange")
   441  
   442  	schema := p.GetProviderSchema()
   443  	if schema.Diagnostics.HasErrors() {
   444  		resp.Diagnostics = schema.Diagnostics
   445  		return resp
   446  	}
   447  
   448  	resSchema, ok := schema.ResourceTypes[r.TypeName]
   449  	if !ok {
   450  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   451  		return resp
   452  	}
   453  
   454  	metaSchema := schema.ProviderMeta
   455  	capabilities := schema.ServerCapabilities
   456  
   457  	// If the provider doesn't support planning a destroy operation, we can
   458  	// return immediately.
   459  	if r.ProposedNewState.IsNull() && !capabilities.PlanDestroy {
   460  		resp.PlannedState = r.ProposedNewState
   461  		resp.PlannedPrivate = r.PriorPrivate
   462  		return resp
   463  	}
   464  
   465  	priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
   466  	if err != nil {
   467  		resp.Diagnostics = resp.Diagnostics.Append(err)
   468  		return resp
   469  	}
   470  
   471  	configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType())
   472  	if err != nil {
   473  		resp.Diagnostics = resp.Diagnostics.Append(err)
   474  		return resp
   475  	}
   476  
   477  	propMP, err := msgpack.Marshal(r.ProposedNewState, resSchema.Block.ImpliedType())
   478  	if err != nil {
   479  		resp.Diagnostics = resp.Diagnostics.Append(err)
   480  		return resp
   481  	}
   482  
   483  	protoReq := &proto.PlanResourceChange_Request{
   484  		TypeName:         r.TypeName,
   485  		PriorState:       &proto.DynamicValue{Msgpack: priorMP},
   486  		Config:           &proto.DynamicValue{Msgpack: configMP},
   487  		ProposedNewState: &proto.DynamicValue{Msgpack: propMP},
   488  		PriorPrivate:     r.PriorPrivate,
   489  	}
   490  
   491  	if metaSchema.Block != nil {
   492  		metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
   493  		if err != nil {
   494  			resp.Diagnostics = resp.Diagnostics.Append(err)
   495  			return resp
   496  		}
   497  		protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
   498  	}
   499  
   500  	protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq)
   501  	if err != nil {
   502  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   503  		return resp
   504  	}
   505  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   506  
   507  	state, err := decodeDynamicValue(protoResp.PlannedState, resSchema.Block.ImpliedType())
   508  	if err != nil {
   509  		resp.Diagnostics = resp.Diagnostics.Append(err)
   510  		return resp
   511  	}
   512  	resp.PlannedState = state
   513  
   514  	for _, p := range protoResp.RequiresReplace {
   515  		resp.RequiresReplace = append(resp.RequiresReplace, convert.AttributePathToPath(p))
   516  	}
   517  
   518  	resp.PlannedPrivate = protoResp.PlannedPrivate
   519  
   520  	resp.LegacyTypeSystem = protoResp.LegacyTypeSystem
   521  
   522  	return resp
   523  }
   524  
   525  func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
   526  	logger.Trace("GRPCProvider: ApplyResourceChange")
   527  
   528  	schema := p.GetProviderSchema()
   529  	if schema.Diagnostics.HasErrors() {
   530  		resp.Diagnostics = schema.Diagnostics
   531  		return resp
   532  	}
   533  
   534  	resSchema, ok := schema.ResourceTypes[r.TypeName]
   535  	if !ok {
   536  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   537  		return resp
   538  	}
   539  
   540  	metaSchema := schema.ProviderMeta
   541  
   542  	priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
   543  	if err != nil {
   544  		resp.Diagnostics = resp.Diagnostics.Append(err)
   545  		return resp
   546  	}
   547  	plannedMP, err := msgpack.Marshal(r.PlannedState, resSchema.Block.ImpliedType())
   548  	if err != nil {
   549  		resp.Diagnostics = resp.Diagnostics.Append(err)
   550  		return resp
   551  	}
   552  	configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType())
   553  	if err != nil {
   554  		resp.Diagnostics = resp.Diagnostics.Append(err)
   555  		return resp
   556  	}
   557  
   558  	protoReq := &proto.ApplyResourceChange_Request{
   559  		TypeName:       r.TypeName,
   560  		PriorState:     &proto.DynamicValue{Msgpack: priorMP},
   561  		PlannedState:   &proto.DynamicValue{Msgpack: plannedMP},
   562  		Config:         &proto.DynamicValue{Msgpack: configMP},
   563  		PlannedPrivate: r.PlannedPrivate,
   564  	}
   565  
   566  	if metaSchema.Block != nil {
   567  		metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
   568  		if err != nil {
   569  			resp.Diagnostics = resp.Diagnostics.Append(err)
   570  			return resp
   571  		}
   572  		protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
   573  	}
   574  
   575  	protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq)
   576  	if err != nil {
   577  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   578  		return resp
   579  	}
   580  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   581  
   582  	resp.Private = protoResp.Private
   583  
   584  	state, err := decodeDynamicValue(protoResp.NewState, resSchema.Block.ImpliedType())
   585  	if err != nil {
   586  		resp.Diagnostics = resp.Diagnostics.Append(err)
   587  		return resp
   588  	}
   589  	resp.NewState = state
   590  
   591  	resp.LegacyTypeSystem = protoResp.LegacyTypeSystem
   592  
   593  	return resp
   594  }
   595  
   596  func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) {
   597  	logger.Trace("GRPCProvider: ImportResourceState")
   598  
   599  	schema := p.GetProviderSchema()
   600  	if schema.Diagnostics.HasErrors() {
   601  		resp.Diagnostics = schema.Diagnostics
   602  		return resp
   603  	}
   604  
   605  	protoReq := &proto.ImportResourceState_Request{
   606  		TypeName: r.TypeName,
   607  		Id:       r.ID,
   608  	}
   609  
   610  	protoResp, err := p.client.ImportResourceState(p.ctx, protoReq)
   611  	if err != nil {
   612  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   613  		return resp
   614  	}
   615  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   616  
   617  	for _, imported := range protoResp.ImportedResources {
   618  		resource := providers.ImportedResource{
   619  			TypeName: imported.TypeName,
   620  			Private:  imported.Private,
   621  		}
   622  
   623  		resSchema, ok := schema.ResourceTypes[r.TypeName]
   624  		if !ok {
   625  			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   626  			continue
   627  		}
   628  
   629  		state, err := decodeDynamicValue(imported.State, resSchema.Block.ImpliedType())
   630  		if err != nil {
   631  			resp.Diagnostics = resp.Diagnostics.Append(err)
   632  			return resp
   633  		}
   634  		resource.State = state
   635  		resp.ImportedResources = append(resp.ImportedResources, resource)
   636  	}
   637  
   638  	return resp
   639  }
   640  
   641  func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
   642  	logger.Trace("GRPCProvider: ReadDataSource")
   643  
   644  	schema := p.GetProviderSchema()
   645  	if schema.Diagnostics.HasErrors() {
   646  		resp.Diagnostics = schema.Diagnostics
   647  		return resp
   648  	}
   649  
   650  	dataSchema, ok := schema.DataSources[r.TypeName]
   651  	if !ok {
   652  		schema.Diagnostics = schema.Diagnostics.Append(fmt.Errorf("unknown data source %q", r.TypeName))
   653  	}
   654  
   655  	metaSchema := schema.ProviderMeta
   656  
   657  	config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
   658  	if err != nil {
   659  		resp.Diagnostics = resp.Diagnostics.Append(err)
   660  		return resp
   661  	}
   662  
   663  	protoReq := &proto.ReadDataSource_Request{
   664  		TypeName: r.TypeName,
   665  		Config: &proto.DynamicValue{
   666  			Msgpack: config,
   667  		},
   668  	}
   669  
   670  	if metaSchema.Block != nil {
   671  		metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
   672  		if err != nil {
   673  			resp.Diagnostics = resp.Diagnostics.Append(err)
   674  			return resp
   675  		}
   676  		protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
   677  	}
   678  
   679  	protoResp, err := p.client.ReadDataSource(p.ctx, protoReq)
   680  	if err != nil {
   681  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   682  		return resp
   683  	}
   684  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   685  
   686  	state, err := decodeDynamicValue(protoResp.State, dataSchema.Block.ImpliedType())
   687  	if err != nil {
   688  		resp.Diagnostics = resp.Diagnostics.Append(err)
   689  		return resp
   690  	}
   691  	resp.State = state
   692  
   693  	return resp
   694  }
   695  
   696  func (p *GRPCProvider) GetFunctions() (resp providers.GetFunctionsResponse) {
   697  	logger.Trace("GRPCProvider: GetFunctions")
   698  
   699  	protoReq := &proto.GetFunctions_Request{}
   700  
   701  	protoResp, err := p.client.GetFunctions(p.ctx, protoReq)
   702  	if err != nil {
   703  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   704  		return resp
   705  	}
   706  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   707  	resp.Functions = make(map[string]providers.FunctionSpec)
   708  
   709  	for name, fn := range protoResp.Functions {
   710  		resp.Functions[name] = convert.ProtoToFunctionSpec(fn)
   711  	}
   712  
   713  	return resp
   714  }
   715  
   716  func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp providers.CallFunctionResponse) {
   717  	logger.Trace("GRPCProvider: CallFunction")
   718  
   719  	schema := p.GetProviderSchema()
   720  	if schema.Diagnostics.HasErrors() {
   721  		// This should be unreachable
   722  		resp.Error = schema.Diagnostics.Err()
   723  		return resp
   724  	}
   725  
   726  	spec, ok := schema.Functions[r.Name]
   727  	if !ok {
   728  		funcs := p.GetFunctions()
   729  		if funcs.Diagnostics.HasErrors() {
   730  			// This should be unreachable
   731  			resp.Error = funcs.Diagnostics.Err()
   732  			return resp
   733  		}
   734  		spec, ok = funcs.Functions[r.Name]
   735  		if !ok {
   736  			// This should be unreachable
   737  			resp.Error = fmt.Errorf("invalid CallFunctionRequest: function %s not defined in provider schema", r.Name)
   738  			return resp
   739  		}
   740  	}
   741  
   742  	protoReq := &proto.CallFunction_Request{
   743  		Name:      r.Name,
   744  		Arguments: make([]*proto.DynamicValue, len(r.Arguments)),
   745  	}
   746  
   747  	// Translate the arguments
   748  	// As this is functionality is always sitting behind cty/function.Function, we skip some validation
   749  	// checks of from the function and param spec.  We still include basic validation to prevent panics,
   750  	// just in case there are bugs in cty.  See context_functions_test.go for explicit testing of argument
   751  	// handling and short-circuiting.
   752  	if len(r.Arguments) < len(spec.Parameters) {
   753  		// This should be unreachable
   754  		resp.Error = fmt.Errorf("invalid CallFunctionRequest: function %s expected %d parameters and got %d instead", r.Name, len(spec.Parameters), len(r.Arguments))
   755  		return resp
   756  	}
   757  
   758  	for i, arg := range r.Arguments {
   759  		var paramSpec providers.FunctionParameterSpec
   760  		if i < len(spec.Parameters) {
   761  			paramSpec = spec.Parameters[i]
   762  		} else {
   763  			// We are past the end of spec.Parameters, this is either variadic or an error
   764  			if spec.VariadicParameter != nil {
   765  				paramSpec = *spec.VariadicParameter
   766  			} else {
   767  				// This should be unreachable
   768  				resp.Error = fmt.Errorf("invalid CallFunctionRequest: too many arguments passed to non-variadic function %s", r.Name)
   769  			}
   770  		}
   771  
   772  		if arg.IsNull() {
   773  			if paramSpec.AllowNullValue {
   774  				continue
   775  			} else {
   776  				resp.Error = &providers.CallFunctionArgumentError{
   777  					Text:             fmt.Sprintf("parameter %s is null, which is not allowed for function %s", paramSpec.Name, r.Name),
   778  					FunctionArgument: i,
   779  				}
   780  			}
   781  
   782  		}
   783  
   784  		encodedArg, err := msgpack.Marshal(arg, paramSpec.Type)
   785  		if err != nil {
   786  			resp.Error = err
   787  			return
   788  		}
   789  
   790  		protoReq.Arguments[i] = &proto.DynamicValue{
   791  			Msgpack: encodedArg,
   792  		}
   793  	}
   794  
   795  	protoResp, err := p.client.CallFunction(p.ctx, protoReq)
   796  	if err != nil {
   797  		resp.Error = err
   798  		return
   799  	}
   800  
   801  	if protoResp.Error != nil {
   802  		err := &providers.CallFunctionArgumentError{
   803  			Text: protoResp.Error.Text,
   804  		}
   805  		if protoResp.Error.FunctionArgument != nil {
   806  			err.FunctionArgument = int(*protoResp.Error.FunctionArgument)
   807  		}
   808  		resp.Error = err
   809  		return
   810  	}
   811  
   812  	resp.Result, resp.Error = decodeDynamicValue(protoResp.Result, spec.Return)
   813  	return
   814  }
   815  
   816  // closing the grpc connection is final, and tofu will call it at the end of every phase.
   817  func (p *GRPCProvider) Close() error {
   818  	logger.Trace("GRPCProvider: Close")
   819  
   820  	// Make sure to stop the server if we're not running within go-plugin.
   821  	if p.TestServer != nil {
   822  		p.TestServer.Stop()
   823  	}
   824  
   825  	// Check this since it's not automatically inserted during plugin creation.
   826  	// It's currently only inserted by the command package, because that is
   827  	// where the factory is built and is the only point with access to the
   828  	// plugin.Client.
   829  	if p.PluginClient == nil {
   830  		logger.Debug("provider has no plugin.Client")
   831  		return nil
   832  	}
   833  
   834  	p.PluginClient.Kill()
   835  	return nil
   836  }
   837  
   838  // Decode a DynamicValue from either the JSON or MsgPack encoding.
   839  func decodeDynamicValue(v *proto.DynamicValue, ty cty.Type) (cty.Value, error) {
   840  	// always return a valid value
   841  	var err error
   842  	res := cty.NullVal(ty)
   843  	if v == nil {
   844  		return res, nil
   845  	}
   846  
   847  	switch {
   848  	case len(v.Msgpack) > 0:
   849  		res, err = msgpack.Unmarshal(v.Msgpack, ty)
   850  	case len(v.Json) > 0:
   851  		res, err = ctyjson.Unmarshal(v.Json, ty)
   852  	}
   853  	return res, err
   854  }