github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plugin6/grpc_provider.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package plugin6
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"sync"
    11  
    12  	"github.com/zclconf/go-cty/cty"
    13  
    14  	plugin "github.com/hashicorp/go-plugin"
    15  	"github.com/terramate-io/tf/addrs"
    16  	"github.com/terramate-io/tf/logging"
    17  	"github.com/terramate-io/tf/plugin6/convert"
    18  	"github.com/terramate-io/tf/providers"
    19  	proto6 "github.com/terramate-io/tf/tfplugin6"
    20  	ctyjson "github.com/zclconf/go-cty/cty/json"
    21  	"github.com/zclconf/go-cty/cty/msgpack"
    22  	"google.golang.org/grpc"
    23  )
    24  
    25  var logger = logging.HCLogger()
    26  
    27  // GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package.
    28  type GRPCProviderPlugin struct {
    29  	plugin.Plugin
    30  	GRPCProvider func() proto6.ProviderServer
    31  }
    32  
    33  func (p *GRPCProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
    34  	return &GRPCProvider{
    35  		client: proto6.NewProviderClient(c),
    36  		ctx:    ctx,
    37  	}, nil
    38  }
    39  
    40  func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
    41  	proto6.RegisterProviderServer(s, p.GRPCProvider())
    42  	return nil
    43  }
    44  
    45  // GRPCProvider handles the client, or core side of the plugin rpc connection.
    46  // The GRPCProvider methods are mostly a translation layer between the
    47  // terraform providers types and the grpc proto types, directly converting
    48  // between the two.
    49  type GRPCProvider struct {
    50  	// PluginClient provides a reference to the plugin.Client which controls the plugin process.
    51  	// This allows the GRPCProvider a way to shutdown the plugin process.
    52  	PluginClient *plugin.Client
    53  
    54  	// TestServer contains a grpc.Server to close when the GRPCProvider is being
    55  	// used in an end to end test of a provider.
    56  	TestServer *grpc.Server
    57  
    58  	// Addr uniquely identifies the type of provider.
    59  	// Normally executed providers will have this set during initialization,
    60  	// but it may not always be available for alternative execute modes.
    61  	Addr addrs.Provider
    62  
    63  	// Proto client use to make the grpc service calls.
    64  	client proto6.ProviderClient
    65  
    66  	// this context is created by the plugin package, and is canceled when the
    67  	// plugin process ends.
    68  	ctx context.Context
    69  
    70  	// schema stores the schema for this provider. This is used to properly
    71  	// serialize the requests for schemas.
    72  	mu     sync.Mutex
    73  	schema providers.GetProviderSchemaResponse
    74  }
    75  
    76  func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResponse) {
    77  	logger.Trace("GRPCProvider.v6: GetProviderSchema")
    78  	p.mu.Lock()
    79  	defer p.mu.Unlock()
    80  
    81  	// check the global cache if we can
    82  	if !p.Addr.IsZero() && resp.ServerCapabilities.GetProviderSchemaOptional {
    83  		if resp, ok := providers.SchemaCache.Get(p.Addr); ok {
    84  			return resp
    85  		}
    86  	}
    87  
    88  	// If the local cache is non-zero, we know this instance has called
    89  	// GetProviderSchema at least once and we can return early.
    90  	if p.schema.Provider.Block != nil {
    91  		return p.schema
    92  	}
    93  
    94  	resp.ResourceTypes = make(map[string]providers.Schema)
    95  	resp.DataSources = make(map[string]providers.Schema)
    96  
    97  	// Some providers may generate quite large schemas, and the internal default
    98  	// grpc response size limit is 4MB. 64MB should cover most any use case, and
    99  	// if we get providers nearing that we may want to consider a finer-grained
   100  	// API to fetch individual resource schemas.
   101  	// Note: this option is marked as EXPERIMENTAL in the grpc API. We keep
   102  	// this for compatibility, but recent providers all set the max message
   103  	// size much higher on the server side, which is the supported method for
   104  	// determining payload size.
   105  	const maxRecvSize = 64 << 20
   106  	protoResp, err := p.client.GetProviderSchema(p.ctx, new(proto6.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize})
   107  	if err != nil {
   108  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   109  		return resp
   110  	}
   111  
   112  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   113  
   114  	if resp.Diagnostics.HasErrors() {
   115  		return resp
   116  	}
   117  
   118  	if protoResp.Provider == nil {
   119  		resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provider schema"))
   120  		return resp
   121  	}
   122  
   123  	resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider)
   124  	if protoResp.ProviderMeta == nil {
   125  		logger.Debug("No provider meta schema returned")
   126  	} else {
   127  		resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta)
   128  	}
   129  
   130  	for name, res := range protoResp.ResourceSchemas {
   131  		resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res)
   132  	}
   133  
   134  	for name, data := range protoResp.DataSourceSchemas {
   135  		resp.DataSources[name] = convert.ProtoToProviderSchema(data)
   136  	}
   137  
   138  	if protoResp.ServerCapabilities != nil {
   139  		resp.ServerCapabilities.PlanDestroy = protoResp.ServerCapabilities.PlanDestroy
   140  		resp.ServerCapabilities.GetProviderSchemaOptional = protoResp.ServerCapabilities.GetProviderSchemaOptional
   141  	}
   142  
   143  	// set the global cache if we can
   144  	if !p.Addr.IsZero() {
   145  		providers.SchemaCache.Set(p.Addr, resp)
   146  	}
   147  
   148  	// always store this here in the client for providers that are not able to
   149  	// use GetProviderSchemaOptional
   150  	p.schema = resp
   151  
   152  	return resp
   153  }
   154  
   155  func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
   156  	logger.Trace("GRPCProvider.v6: ValidateProviderConfig")
   157  
   158  	schema := p.GetProviderSchema()
   159  	if schema.Diagnostics.HasErrors() {
   160  		resp.Diagnostics = schema.Diagnostics
   161  		return resp
   162  	}
   163  
   164  	ty := schema.Provider.Block.ImpliedType()
   165  
   166  	mp, err := msgpack.Marshal(r.Config, ty)
   167  	if err != nil {
   168  		resp.Diagnostics = resp.Diagnostics.Append(err)
   169  		return resp
   170  	}
   171  
   172  	protoReq := &proto6.ValidateProviderConfig_Request{
   173  		Config: &proto6.DynamicValue{Msgpack: mp},
   174  	}
   175  
   176  	protoResp, err := p.client.ValidateProviderConfig(p.ctx, protoReq)
   177  	if err != nil {
   178  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   179  		return resp
   180  	}
   181  
   182  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   183  	return resp
   184  }
   185  
   186  func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) {
   187  	logger.Trace("GRPCProvider.v6: ValidateResourceConfig")
   188  
   189  	schema := p.GetProviderSchema()
   190  	if schema.Diagnostics.HasErrors() {
   191  		resp.Diagnostics = schema.Diagnostics
   192  		return resp
   193  	}
   194  
   195  	resourceSchema, ok := schema.ResourceTypes[r.TypeName]
   196  	if !ok {
   197  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   198  		return resp
   199  	}
   200  
   201  	mp, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType())
   202  	if err != nil {
   203  		resp.Diagnostics = resp.Diagnostics.Append(err)
   204  		return resp
   205  	}
   206  
   207  	protoReq := &proto6.ValidateResourceConfig_Request{
   208  		TypeName: r.TypeName,
   209  		Config:   &proto6.DynamicValue{Msgpack: mp},
   210  	}
   211  
   212  	protoResp, err := p.client.ValidateResourceConfig(p.ctx, protoReq)
   213  	if err != nil {
   214  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   215  		return resp
   216  	}
   217  
   218  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   219  	return resp
   220  }
   221  
   222  func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) {
   223  	logger.Trace("GRPCProvider.v6: ValidateDataResourceConfig")
   224  
   225  	schema := p.GetProviderSchema()
   226  	if schema.Diagnostics.HasErrors() {
   227  		resp.Diagnostics = schema.Diagnostics
   228  		return resp
   229  	}
   230  
   231  	dataSchema, ok := schema.DataSources[r.TypeName]
   232  	if !ok {
   233  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown data source %q", r.TypeName))
   234  		return resp
   235  	}
   236  
   237  	mp, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
   238  	if err != nil {
   239  		resp.Diagnostics = resp.Diagnostics.Append(err)
   240  		return resp
   241  	}
   242  
   243  	protoReq := &proto6.ValidateDataResourceConfig_Request{
   244  		TypeName: r.TypeName,
   245  		Config:   &proto6.DynamicValue{Msgpack: mp},
   246  	}
   247  
   248  	protoResp, err := p.client.ValidateDataResourceConfig(p.ctx, protoReq)
   249  	if err != nil {
   250  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   251  		return resp
   252  	}
   253  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   254  	return resp
   255  }
   256  
   257  func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) {
   258  	logger.Trace("GRPCProvider.v6: UpgradeResourceState")
   259  
   260  	schema := p.GetProviderSchema()
   261  	if schema.Diagnostics.HasErrors() {
   262  		resp.Diagnostics = schema.Diagnostics
   263  		return resp
   264  	}
   265  
   266  	resSchema, ok := schema.ResourceTypes[r.TypeName]
   267  	if !ok {
   268  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   269  		return resp
   270  	}
   271  
   272  	protoReq := &proto6.UpgradeResourceState_Request{
   273  		TypeName: r.TypeName,
   274  		Version:  int64(r.Version),
   275  		RawState: &proto6.RawState{
   276  			Json:    r.RawStateJSON,
   277  			Flatmap: r.RawStateFlatmap,
   278  		},
   279  	}
   280  
   281  	protoResp, err := p.client.UpgradeResourceState(p.ctx, protoReq)
   282  	if err != nil {
   283  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   284  		return resp
   285  	}
   286  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   287  
   288  	ty := resSchema.Block.ImpliedType()
   289  	resp.UpgradedState = cty.NullVal(ty)
   290  	if protoResp.UpgradedState == nil {
   291  		return resp
   292  	}
   293  
   294  	state, err := decodeDynamicValue(protoResp.UpgradedState, ty)
   295  	if err != nil {
   296  		resp.Diagnostics = resp.Diagnostics.Append(err)
   297  		return resp
   298  	}
   299  	resp.UpgradedState = state
   300  
   301  	return resp
   302  }
   303  
   304  func (p *GRPCProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
   305  	logger.Trace("GRPCProvider.v6: ConfigureProvider")
   306  
   307  	schema := p.GetProviderSchema()
   308  
   309  	var mp []byte
   310  
   311  	// we don't have anything to marshal if there's no config
   312  	mp, err := msgpack.Marshal(r.Config, schema.Provider.Block.ImpliedType())
   313  	if err != nil {
   314  		resp.Diagnostics = resp.Diagnostics.Append(err)
   315  		return resp
   316  	}
   317  
   318  	protoReq := &proto6.ConfigureProvider_Request{
   319  		TerraformVersion: r.TerraformVersion,
   320  		Config: &proto6.DynamicValue{
   321  			Msgpack: mp,
   322  		},
   323  	}
   324  
   325  	protoResp, err := p.client.ConfigureProvider(p.ctx, protoReq)
   326  	if err != nil {
   327  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   328  		return resp
   329  	}
   330  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   331  	return resp
   332  }
   333  
   334  func (p *GRPCProvider) Stop() error {
   335  	logger.Trace("GRPCProvider.v6: Stop")
   336  
   337  	resp, err := p.client.StopProvider(p.ctx, new(proto6.StopProvider_Request))
   338  	if err != nil {
   339  		return err
   340  	}
   341  
   342  	if resp.Error != "" {
   343  		return errors.New(resp.Error)
   344  	}
   345  	return nil
   346  }
   347  
   348  func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
   349  	logger.Trace("GRPCProvider.v6: ReadResource")
   350  
   351  	schema := p.GetProviderSchema()
   352  	if schema.Diagnostics.HasErrors() {
   353  		resp.Diagnostics = schema.Diagnostics
   354  		return resp
   355  	}
   356  
   357  	resSchema, ok := schema.ResourceTypes[r.TypeName]
   358  	if !ok {
   359  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type " + r.TypeName))
   360  		return resp
   361  	}
   362  
   363  	metaSchema := schema.ProviderMeta
   364  
   365  	mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
   366  	if err != nil {
   367  		resp.Diagnostics = resp.Diagnostics.Append(err)
   368  		return resp
   369  	}
   370  
   371  	protoReq := &proto6.ReadResource_Request{
   372  		TypeName:     r.TypeName,
   373  		CurrentState: &proto6.DynamicValue{Msgpack: mp},
   374  		Private:      r.Private,
   375  	}
   376  
   377  	if metaSchema.Block != nil {
   378  		metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
   379  		if err != nil {
   380  			resp.Diagnostics = resp.Diagnostics.Append(err)
   381  			return resp
   382  		}
   383  		protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
   384  	}
   385  
   386  	protoResp, err := p.client.ReadResource(p.ctx, protoReq)
   387  	if err != nil {
   388  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   389  		return resp
   390  	}
   391  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   392  
   393  	state, err := decodeDynamicValue(protoResp.NewState, resSchema.Block.ImpliedType())
   394  	if err != nil {
   395  		resp.Diagnostics = resp.Diagnostics.Append(err)
   396  		return resp
   397  	}
   398  	resp.NewState = state
   399  	resp.Private = protoResp.Private
   400  
   401  	return resp
   402  }
   403  
   404  func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
   405  	logger.Trace("GRPCProvider.v6: PlanResourceChange")
   406  
   407  	schema := p.GetProviderSchema()
   408  	if schema.Diagnostics.HasErrors() {
   409  		resp.Diagnostics = schema.Diagnostics
   410  		return resp
   411  	}
   412  
   413  	resSchema, ok := schema.ResourceTypes[r.TypeName]
   414  	if !ok {
   415  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   416  		return resp
   417  	}
   418  
   419  	metaSchema := schema.ProviderMeta
   420  	capabilities := schema.ServerCapabilities
   421  
   422  	// If the provider doesn't support planning a destroy operation, we can
   423  	// return immediately.
   424  	if r.ProposedNewState.IsNull() && !capabilities.PlanDestroy {
   425  		resp.PlannedState = r.ProposedNewState
   426  		resp.PlannedPrivate = r.PriorPrivate
   427  		return resp
   428  	}
   429  
   430  	priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
   431  	if err != nil {
   432  		resp.Diagnostics = resp.Diagnostics.Append(err)
   433  		return resp
   434  	}
   435  
   436  	configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType())
   437  	if err != nil {
   438  		resp.Diagnostics = resp.Diagnostics.Append(err)
   439  		return resp
   440  	}
   441  
   442  	propMP, err := msgpack.Marshal(r.ProposedNewState, resSchema.Block.ImpliedType())
   443  	if err != nil {
   444  		resp.Diagnostics = resp.Diagnostics.Append(err)
   445  		return resp
   446  	}
   447  
   448  	protoReq := &proto6.PlanResourceChange_Request{
   449  		TypeName:         r.TypeName,
   450  		PriorState:       &proto6.DynamicValue{Msgpack: priorMP},
   451  		Config:           &proto6.DynamicValue{Msgpack: configMP},
   452  		ProposedNewState: &proto6.DynamicValue{Msgpack: propMP},
   453  		PriorPrivate:     r.PriorPrivate,
   454  	}
   455  
   456  	if metaSchema.Block != nil {
   457  		metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
   458  		if err != nil {
   459  			resp.Diagnostics = resp.Diagnostics.Append(err)
   460  			return resp
   461  		}
   462  		protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
   463  	}
   464  
   465  	protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq)
   466  	if err != nil {
   467  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   468  		return resp
   469  	}
   470  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   471  
   472  	state, err := decodeDynamicValue(protoResp.PlannedState, resSchema.Block.ImpliedType())
   473  	if err != nil {
   474  		resp.Diagnostics = resp.Diagnostics.Append(err)
   475  		return resp
   476  	}
   477  	resp.PlannedState = state
   478  
   479  	for _, p := range protoResp.RequiresReplace {
   480  		resp.RequiresReplace = append(resp.RequiresReplace, convert.AttributePathToPath(p))
   481  	}
   482  
   483  	resp.PlannedPrivate = protoResp.PlannedPrivate
   484  
   485  	resp.LegacyTypeSystem = protoResp.LegacyTypeSystem
   486  
   487  	return resp
   488  }
   489  
   490  func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
   491  	logger.Trace("GRPCProvider.v6: ApplyResourceChange")
   492  
   493  	schema := p.GetProviderSchema()
   494  	if schema.Diagnostics.HasErrors() {
   495  		resp.Diagnostics = schema.Diagnostics
   496  		return resp
   497  	}
   498  
   499  	resSchema, ok := schema.ResourceTypes[r.TypeName]
   500  	if !ok {
   501  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   502  		return resp
   503  	}
   504  
   505  	metaSchema := schema.ProviderMeta
   506  
   507  	priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
   508  	if err != nil {
   509  		resp.Diagnostics = resp.Diagnostics.Append(err)
   510  		return resp
   511  	}
   512  	plannedMP, err := msgpack.Marshal(r.PlannedState, resSchema.Block.ImpliedType())
   513  	if err != nil {
   514  		resp.Diagnostics = resp.Diagnostics.Append(err)
   515  		return resp
   516  	}
   517  	configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType())
   518  	if err != nil {
   519  		resp.Diagnostics = resp.Diagnostics.Append(err)
   520  		return resp
   521  	}
   522  
   523  	protoReq := &proto6.ApplyResourceChange_Request{
   524  		TypeName:       r.TypeName,
   525  		PriorState:     &proto6.DynamicValue{Msgpack: priorMP},
   526  		PlannedState:   &proto6.DynamicValue{Msgpack: plannedMP},
   527  		Config:         &proto6.DynamicValue{Msgpack: configMP},
   528  		PlannedPrivate: r.PlannedPrivate,
   529  	}
   530  
   531  	if metaSchema.Block != nil {
   532  		metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
   533  		if err != nil {
   534  			resp.Diagnostics = resp.Diagnostics.Append(err)
   535  			return resp
   536  		}
   537  		protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
   538  	}
   539  
   540  	protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq)
   541  	if err != nil {
   542  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   543  		return resp
   544  	}
   545  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   546  
   547  	resp.Private = protoResp.Private
   548  
   549  	state, err := decodeDynamicValue(protoResp.NewState, resSchema.Block.ImpliedType())
   550  	if err != nil {
   551  		resp.Diagnostics = resp.Diagnostics.Append(err)
   552  		return resp
   553  	}
   554  	resp.NewState = state
   555  
   556  	resp.LegacyTypeSystem = protoResp.LegacyTypeSystem
   557  
   558  	return resp
   559  }
   560  
   561  func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) {
   562  	logger.Trace("GRPCProvider.v6: ImportResourceState")
   563  
   564  	schema := p.GetProviderSchema()
   565  	if schema.Diagnostics.HasErrors() {
   566  		resp.Diagnostics = schema.Diagnostics
   567  		return resp
   568  	}
   569  
   570  	protoReq := &proto6.ImportResourceState_Request{
   571  		TypeName: r.TypeName,
   572  		Id:       r.ID,
   573  	}
   574  
   575  	protoResp, err := p.client.ImportResourceState(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  	for _, imported := range protoResp.ImportedResources {
   583  		resource := providers.ImportedResource{
   584  			TypeName: imported.TypeName,
   585  			Private:  imported.Private,
   586  		}
   587  
   588  		resSchema, ok := schema.ResourceTypes[r.TypeName]
   589  		if !ok {
   590  			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
   591  			continue
   592  		}
   593  
   594  		state, err := decodeDynamicValue(imported.State, resSchema.Block.ImpliedType())
   595  		if err != nil {
   596  			resp.Diagnostics = resp.Diagnostics.Append(err)
   597  			return resp
   598  		}
   599  		resource.State = state
   600  		resp.ImportedResources = append(resp.ImportedResources, resource)
   601  	}
   602  
   603  	return resp
   604  }
   605  
   606  func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
   607  	logger.Trace("GRPCProvider.v6: ReadDataSource")
   608  
   609  	schema := p.GetProviderSchema()
   610  	if schema.Diagnostics.HasErrors() {
   611  		resp.Diagnostics = schema.Diagnostics
   612  		return resp
   613  	}
   614  
   615  	dataSchema, ok := schema.DataSources[r.TypeName]
   616  	if !ok {
   617  		schema.Diagnostics = schema.Diagnostics.Append(fmt.Errorf("unknown data source %q", r.TypeName))
   618  	}
   619  
   620  	metaSchema := schema.ProviderMeta
   621  
   622  	config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
   623  	if err != nil {
   624  		resp.Diagnostics = resp.Diagnostics.Append(err)
   625  		return resp
   626  	}
   627  
   628  	protoReq := &proto6.ReadDataSource_Request{
   629  		TypeName: r.TypeName,
   630  		Config: &proto6.DynamicValue{
   631  			Msgpack: config,
   632  		},
   633  	}
   634  
   635  	if metaSchema.Block != nil {
   636  		metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
   637  		if err != nil {
   638  			resp.Diagnostics = resp.Diagnostics.Append(err)
   639  			return resp
   640  		}
   641  		protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
   642  	}
   643  
   644  	protoResp, err := p.client.ReadDataSource(p.ctx, protoReq)
   645  	if err != nil {
   646  		resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
   647  		return resp
   648  	}
   649  	resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
   650  
   651  	state, err := decodeDynamicValue(protoResp.State, dataSchema.Block.ImpliedType())
   652  	if err != nil {
   653  		resp.Diagnostics = resp.Diagnostics.Append(err)
   654  		return resp
   655  	}
   656  	resp.State = state
   657  
   658  	return resp
   659  }
   660  
   661  // closing the grpc connection is final, and terraform will call it at the end of every phase.
   662  func (p *GRPCProvider) Close() error {
   663  	logger.Trace("GRPCProvider.v6: Close")
   664  
   665  	// Make sure to stop the server if we're not running within go-plugin.
   666  	if p.TestServer != nil {
   667  		p.TestServer.Stop()
   668  	}
   669  
   670  	// Check this since it's not automatically inserted during plugin creation.
   671  	// It's currently only inserted by the command package, because that is
   672  	// where the factory is built and is the only point with access to the
   673  	// plugin.Client.
   674  	if p.PluginClient == nil {
   675  		logger.Debug("provider has no plugin.Client")
   676  		return nil
   677  	}
   678  
   679  	p.PluginClient.Kill()
   680  	return nil
   681  }
   682  
   683  // Decode a DynamicValue from either the JSON or MsgPack encoding.
   684  func decodeDynamicValue(v *proto6.DynamicValue, ty cty.Type) (cty.Value, error) {
   685  	// always return a valid value
   686  	var err error
   687  	res := cty.NullVal(ty)
   688  	if v == nil {
   689  		return res, nil
   690  	}
   691  
   692  	switch {
   693  	case len(v.Msgpack) > 0:
   694  		res, err = msgpack.Unmarshal(v.Msgpack, ty)
   695  	case len(v.Json) > 0:
   696  		res, err = ctyjson.Unmarshal(v.Json, ty)
   697  	}
   698  	return res, err
   699  }