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