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 }