2023-05-02 11:33:06 -04:00
// Copyright IBM Corp. 2014, 2026
2023-08-10 18:43:27 -04:00
// SPDX-License-Identifier: BUSL-1.1
2023-05-02 11:33:06 -04:00
2018-08-08 16:57:31 -04:00
package plugin
import (
"context"
"errors"
2022-05-03 10:22:55 -04:00
"fmt"
2025-06-04 03:40:10 -04:00
"io"
2018-08-08 16:57:31 -04:00
"sync"
2018-08-14 16:55:20 -04:00
plugin "github.com/hashicorp/go-plugin"
2025-07-18 14:14:44 -04:00
"github.com/zclconf/go-cty/cty"
2024-01-31 15:10:41 -05:00
"github.com/zclconf/go-cty/cty/function"
2024-01-11 04:08:50 -05:00
ctyjson "github.com/zclconf/go-cty/cty/json"
"github.com/zclconf/go-cty/cty/msgpack"
"google.golang.org/grpc"
2025-03-11 15:58:44 -04:00
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
2024-01-11 04:08:50 -05:00
2023-07-03 12:37:50 -04:00
"github.com/hashicorp/terraform/internal/addrs"
2020-10-22 17:28:54 -04:00
"github.com/hashicorp/terraform/internal/logging"
2021-05-17 15:35:22 -04:00
"github.com/hashicorp/terraform/internal/plugin/convert"
2021-05-17 13:40:40 -04:00
"github.com/hashicorp/terraform/internal/providers"
2018-11-19 12:39:16 -05:00
proto "github.com/hashicorp/terraform/internal/tfplugin5"
2018-08-08 16:57:31 -04:00
)
2020-10-22 17:28:54 -04:00
var logger = logging . HCLogger ( )
2018-08-14 16:55:20 -04:00
// GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package.
type GRPCProviderPlugin struct {
plugin . Plugin
GRPCProvider func ( ) proto . ProviderServer
}
func ( p * GRPCProviderPlugin ) GRPCClient ( ctx context . Context , broker * plugin . GRPCBroker , c * grpc . ClientConn ) ( interface { } , error ) {
return & GRPCProvider {
client : proto . NewProviderClient ( c ) ,
ctx : ctx ,
} , nil
}
func ( p * GRPCProviderPlugin ) GRPCServer ( broker * plugin . GRPCBroker , s * grpc . Server ) error {
proto . RegisterProviderServer ( s , p . GRPCProvider ( ) )
return nil
}
2018-08-08 16:57:31 -04:00
// GRPCProvider handles the client, or core side of the plugin rpc connection.
// The GRPCProvider methods are mostly a translation layer between the
2021-02-18 10:13:43 -05:00
// terraform providers types and the grpc proto types, directly converting
2018-08-08 16:57:31 -04:00
// between the two.
type GRPCProvider struct {
2018-10-02 16:06:12 -04:00
// PluginClient provides a reference to the plugin.Client which controls the plugin process.
// This allows the GRPCProvider a way to shutdown the plugin process.
PluginClient * plugin . Client
2019-04-29 14:14:04 -04:00
// TestServer contains a grpc.Server to close when the GRPCProvider is being
2018-10-15 21:15:08 -04:00
// used in an end to end test of a provider.
2019-04-29 14:14:04 -04:00
TestServer * grpc . Server
2018-10-15 21:15:08 -04:00
2023-07-03 12:37:50 -04:00
// Addr uniquely identifies the type of provider.
// Normally executed providers will have this set during initialization,
// but it may not always be available for alternative execute modes.
Addr addrs . Provider
2018-10-02 16:06:12 -04:00
// Proto client use to make the grpc service calls.
2018-08-08 16:57:31 -04:00
client proto . ProviderClient
// this context is created by the plugin package, and is canceled when the
// plugin process ends.
ctx context . Context
// schema stores the schema for this provider. This is used to properly
2023-07-06 16:48:06 -04:00
// serialize the requests for schemas.
mu sync . Mutex
schema providers . GetProviderSchemaResponse
2018-08-08 16:57:31 -04:00
}
2023-09-05 17:14:57 -04:00
func ( p * GRPCProvider ) GetProviderSchema ( ) providers . GetProviderSchemaResponse {
2021-02-18 10:13:43 -05:00
logger . Trace ( "GRPCProvider: GetProviderSchema" )
2018-08-08 16:57:31 -04:00
p . mu . Lock ( )
defer p . mu . Unlock ( )
2023-07-03 12:37:50 -04:00
// check the global cache if we can
2023-09-05 17:14:57 -04:00
if ! p . Addr . IsZero ( ) {
if resp , ok := providers . SchemaCache . Get ( p . Addr ) ; ok && resp . ServerCapabilities . GetProviderSchemaOptional {
logger . Trace ( "GRPCProvider: returning cached schema" , p . Addr . String ( ) )
2023-07-03 12:37:50 -04:00
return resp
}
}
2023-07-18 13:43:21 -04:00
// If the local cache is non-zero, we know this instance has called
// GetProviderSchema at least once and we can return early.
2025-03-04 10:33:43 -05:00
if p . schema . Provider . Body != nil {
2023-07-06 16:48:06 -04:00
return p . schema
2018-08-08 16:57:31 -04:00
}
2023-09-05 17:14:57 -04:00
var resp providers . GetProviderSchemaResponse
2018-08-08 16:57:31 -04:00
resp . ResourceTypes = make ( map [ string ] providers . Schema )
resp . DataSources = make ( map [ string ] providers . Schema )
2024-09-26 17:14:01 -04:00
resp . EphemeralResourceTypes = make ( map [ string ] providers . Schema )
2025-05-19 04:20:52 -04:00
resp . ListResourceTypes = make ( map [ string ] providers . Schema )
2025-06-30 06:39:16 -04:00
resp . Actions = make ( map [ string ] providers . ActionSchema )
2018-08-08 16:57:31 -04:00
2019-04-02 15:43:48 -04:00
// Some providers may generate quite large schemas, and the internal default
// grpc response size limit is 4MB. 64MB should cover most any use case, and
// if we get providers nearing that we may want to consider a finer-grained
// API to fetch individual resource schemas.
2022-06-02 18:03:35 -04:00
// Note: this option is marked as EXPERIMENTAL in the grpc API. We keep
// this for compatibility, but recent providers all set the max message
// size much higher on the server side, which is the supported method for
// determining payload size.
2019-04-02 15:43:48 -04:00
const maxRecvSize = 64 << 20
protoResp , err := p . client . GetSchema ( p . ctx , new ( proto . GetProviderSchema_Request ) , grpc . MaxRecvMsgSizeCallOption { MaxRecvMsgSize : maxRecvSize } )
2018-08-08 16:57:31 -04:00
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-10-06 14:26:07 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2022-06-03 14:27:55 -04:00
if resp . Diagnostics . HasErrors ( ) {
return resp
}
2018-08-08 16:57:31 -04:00
if protoResp . Provider == nil {
resp . Diagnostics = resp . Diagnostics . Append ( errors . New ( "missing provider schema" ) )
return resp
}
2025-03-11 15:58:44 -04:00
identResp , err := p . client . GetResourceIdentitySchemas ( p . ctx , new ( proto . GetResourceIdentitySchemas_Request ) )
if err != nil {
if status . Code ( err ) == codes . Unimplemented {
// We don't treat this as an error if older providers don't implement this method,
// so we create an empty map for identity schemas
identResp = & proto . GetResourceIdentitySchemas_Response {
IdentitySchemas : map [ string ] * proto . ResourceIdentitySchema { } ,
}
} else {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
}
2025-05-16 07:55:12 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( identResp . Diagnostics ) )
if resp . Diagnostics . HasErrors ( ) {
return resp
}
2025-03-11 15:58:44 -04:00
resp . Provider = convert . ProtoToProviderSchema ( protoResp . Provider , nil )
2020-03-05 19:53:24 -05:00
if protoResp . ProviderMeta == nil {
2020-10-22 17:28:54 -04:00
logger . Debug ( "No provider meta schema returned" )
2020-03-05 19:53:24 -05:00
} else {
2025-03-11 15:58:44 -04:00
resp . ProviderMeta = convert . ProtoToProviderSchema ( protoResp . ProviderMeta , nil )
2020-03-05 19:53:24 -05:00
}
2018-08-08 16:57:31 -04:00
for name , res := range protoResp . ResourceSchemas {
2025-03-11 15:58:44 -04:00
id := identResp . IdentitySchemas [ name ] // We're fine if the id is not found
resp . ResourceTypes [ name ] = convert . ProtoToProviderSchema ( res , id )
2018-08-08 16:57:31 -04:00
}
for name , data := range protoResp . DataSourceSchemas {
2025-03-11 15:58:44 -04:00
resp . DataSources [ name ] = convert . ProtoToProviderSchema ( data , nil )
2018-08-08 16:57:31 -04:00
}
2024-09-26 17:14:01 -04:00
for name , ephem := range protoResp . EphemeralResourceSchemas {
2025-03-11 15:58:44 -04:00
resp . EphemeralResourceTypes [ name ] = convert . ProtoToProviderSchema ( ephem , nil )
2024-09-26 17:14:01 -04:00
}
2025-05-19 04:20:52 -04:00
for name , list := range protoResp . ListResourceSchemas {
2025-09-24 03:30:59 -04:00
resp . ListResourceTypes [ name ] = convert . ProtoToListSchema ( list )
2025-05-19 04:20:52 -04:00
}
2025-06-30 06:39:16 -04:00
for name , action := range protoResp . ActionSchemas {
resp . Actions [ name ] = convert . ProtoToActionSchema ( action )
}
2023-09-28 16:54:44 -04:00
if decls , err := convert . FunctionDeclsFromProto ( protoResp . Functions ) ; err == nil {
resp . Functions = decls
} else {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2022-07-06 13:47:30 -04:00
if protoResp . ServerCapabilities != nil {
resp . ServerCapabilities . PlanDestroy = protoResp . ServerCapabilities . PlanDestroy
2023-07-06 16:48:06 -04:00
resp . ServerCapabilities . GetProviderSchemaOptional = protoResp . ServerCapabilities . GetProviderSchemaOptional
2024-01-11 04:08:50 -05:00
resp . ServerCapabilities . MoveResourceState = protoResp . ServerCapabilities . MoveResourceState
2025-07-25 15:28:18 -04:00
resp . ServerCapabilities . GenerateResourceConfig = protoResp . ServerCapabilities . GenerateResourceConfig
2022-06-02 18:03:35 -04:00
}
2023-07-03 12:37:50 -04:00
// set the global cache if we can
2023-07-18 13:43:21 -04:00
if ! p . Addr . IsZero ( ) {
2023-07-06 16:48:06 -04:00
providers . SchemaCache . Set ( p . Addr , resp )
}
2018-08-08 16:57:31 -04:00
2023-07-18 13:43:21 -04:00
// always store this here in the client for providers that are not able to
// use GetProviderSchemaOptional
p . schema = resp
2018-08-08 16:57:31 -04:00
return resp
}
2025-03-11 15:58:44 -04:00
func ( p * GRPCProvider ) GetResourceIdentitySchemas ( ) providers . GetResourceIdentitySchemasResponse {
2025-03-24 17:34:03 -04:00
logger . Trace ( "GRPCProvider: GetResourceIdentitySchemas" )
2025-03-11 15:58:44 -04:00
var resp providers . GetResourceIdentitySchemasResponse
resp . IdentityTypes = make ( map [ string ] providers . IdentitySchema )
protoResp , err := p . client . GetResourceIdentitySchemas ( p . ctx , new ( proto . GetResourceIdentitySchemas_Request ) )
if err != nil {
if status . Code ( err ) == codes . Unimplemented {
// We expect no error here if older providers don't implement this method
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
if resp . Diagnostics . HasErrors ( ) {
return resp
}
for name , res := range protoResp . IdentitySchemas {
resp . IdentityTypes [ name ] = providers . IdentitySchema {
Version : res . Version ,
Body : convert . ProtoToIdentitySchema ( res . IdentityAttributes ) ,
}
}
return resp
}
2021-02-18 10:13:43 -05:00
func ( p * GRPCProvider ) ValidateProviderConfig ( r providers . ValidateProviderConfigRequest ) ( resp providers . ValidateProviderConfigResponse ) {
logger . Trace ( "GRPCProvider: ValidateProviderConfig" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
2025-03-04 10:33:43 -05:00
ty := schema . Provider . Body . ImpliedType ( )
2018-10-17 21:20:40 -04:00
mp , err := msgpack . Marshal ( r . Config , ty )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2018-10-17 21:20:40 -04:00
protoReq := & proto . PrepareProviderConfig_Request {
2018-08-08 16:57:31 -04:00
Config : & proto . DynamicValue { Msgpack : mp } ,
}
2018-10-17 21:20:40 -04:00
protoResp , err := p . client . PrepareProviderConfig ( p . ctx , protoReq )
2018-08-08 16:57:31 -04:00
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-10-06 14:26:07 -04:00
2020-10-16 15:05:49 -04:00
config , err := decodeDynamicValue ( protoResp . PreparedConfig , ty )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
2018-10-17 21:20:40 -04:00
}
resp . PreparedConfig = config
2018-08-15 12:03:39 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2018-08-08 16:57:31 -04:00
return resp
}
2021-02-18 10:13:43 -05:00
func ( p * GRPCProvider ) ValidateResourceConfig ( r providers . ValidateResourceConfigRequest ) ( resp providers . ValidateResourceConfigResponse ) {
logger . Trace ( "GRPCProvider: ValidateResourceConfig" )
2022-05-03 10:22:55 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
resourceSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q" , r . TypeName ) )
2022-05-03 10:22:55 -04:00
return resp
}
2018-08-08 16:57:31 -04:00
2025-03-04 10:33:43 -05:00
mp , err := msgpack . Marshal ( r . Config , resourceSchema . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . ValidateResourceTypeConfig_Request {
2024-12-18 03:30:32 -05:00
TypeName : r . TypeName ,
Config : & proto . DynamicValue { Msgpack : mp } ,
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
2018-08-08 16:57:31 -04:00
}
protoResp , err := p . client . ValidateResourceTypeConfig ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-08-15 12:03:39 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2018-08-08 16:57:31 -04:00
return resp
}
2021-02-24 12:04:28 -05:00
func ( p * GRPCProvider ) ValidateDataResourceConfig ( r providers . ValidateDataResourceConfigRequest ) ( resp providers . ValidateDataResourceConfigResponse ) {
logger . Trace ( "GRPCProvider: ValidateDataResourceConfig" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
dataSchema , ok := schema . DataSources [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown data source %q" , r . TypeName ) )
2022-05-03 10:22:55 -04:00
return resp
}
2018-08-08 16:57:31 -04:00
2025-03-04 10:33:43 -05:00
mp , err := msgpack . Marshal ( r . Config , dataSchema . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . ValidateDataSourceConfig_Request {
TypeName : r . TypeName ,
Config : & proto . DynamicValue { Msgpack : mp } ,
}
protoResp , err := p . client . ValidateDataSourceConfig ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-08-15 12:03:39 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2018-08-08 16:57:31 -04:00
return resp
}
2025-05-19 04:20:52 -04:00
func ( p * GRPCProvider ) ValidateListResourceConfig ( r providers . ValidateListResourceConfigRequest ) ( resp providers . ValidateListResourceConfigResponse ) {
logger . Trace ( "GRPCProvider: ValidateListResourceConfig" )
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
listResourceSchema , ok := schema . ListResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown list resource type %q" , r . TypeName ) )
return resp
}
2025-06-10 14:08:54 -04:00
configSchema := listResourceSchema . Body . BlockTypes [ "config" ]
2025-09-24 03:30:59 -04:00
if ! r . Config . Type ( ) . HasAttribute ( "config" ) {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "missing required attribute \"config\"; this is a bug in Terraform - please report it" ) )
return resp
2025-06-11 09:37:45 -04:00
}
2025-09-24 03:30:59 -04:00
config := r . Config . GetAttr ( "config" )
2025-06-11 09:37:45 -04:00
mp , err := msgpack . Marshal ( config , configSchema . ImpliedType ( ) )
2025-05-19 04:20:52 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . ValidateListResourceConfig_Request {
TypeName : r . TypeName ,
Config : & proto . DynamicValue { Msgpack : mp } ,
}
protoResp , err := p . client . ValidateListResourceConfig ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
return resp
}
2018-08-08 16:57:31 -04:00
func ( p * GRPCProvider ) UpgradeResourceState ( r providers . UpgradeResourceStateRequest ) ( resp providers . UpgradeResourceStateResponse ) {
2020-10-22 17:28:54 -04:00
logger . Trace ( "GRPCProvider: UpgradeResourceState" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
resSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q" , r . TypeName ) )
2022-05-03 10:22:55 -04:00
return resp
}
2018-08-08 16:57:31 -04:00
protoReq := & proto . UpgradeResourceState_Request {
TypeName : r . TypeName ,
Version : int64 ( r . Version ) ,
RawState : & proto . RawState {
Json : r . RawStateJSON ,
Flatmap : r . RawStateFlatmap ,
} ,
}
protoResp , err := p . client . UpgradeResourceState ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-10-06 14:26:07 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2018-08-08 16:57:31 -04:00
2025-03-04 10:33:43 -05:00
ty := resSchema . Body . ImpliedType ( )
2020-10-16 15:05:49 -04:00
resp . UpgradedState = cty . NullVal ( ty )
if protoResp . UpgradedState == nil {
return resp
2018-08-08 16:57:31 -04:00
}
2020-10-16 15:05:49 -04:00
state , err := decodeDynamicValue ( protoResp . UpgradedState , ty )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2018-08-08 16:57:31 -04:00
resp . UpgradedState = state
2020-10-16 15:05:49 -04:00
2018-08-08 16:57:31 -04:00
return resp
}
2025-03-11 15:58:44 -04:00
func ( p * GRPCProvider ) UpgradeResourceIdentity ( r providers . UpgradeResourceIdentityRequest ) ( resp providers . UpgradeResourceIdentityResponse ) {
logger . Trace ( "GRPCProvider: UpgradeResourceIdentity" )
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
resSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource identity type %q" , r . TypeName ) )
return resp
}
protoReq := & proto . UpgradeResourceIdentity_Request {
TypeName : r . TypeName ,
Version : int64 ( r . Version ) ,
RawIdentity : & proto . RawState {
Json : r . RawIdentityJSON ,
} ,
}
protoResp , err := p . client . UpgradeResourceIdentity ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
ty := resSchema . Identity . ImpliedType ( )
resp . UpgradedIdentity = cty . NullVal ( ty )
if protoResp . UpgradedIdentity == nil {
return resp
}
identity , err := decodeDynamicValue ( protoResp . UpgradedIdentity . IdentityData , ty )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
resp . UpgradedIdentity = identity
return resp
}
2021-02-18 10:13:43 -05:00
func ( p * GRPCProvider ) ConfigureProvider ( r providers . ConfigureProviderRequest ) ( resp providers . ConfigureProviderResponse ) {
logger . Trace ( "GRPCProvider: ConfigureProvider" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
2018-08-08 16:57:31 -04:00
var mp [ ] byte
// we don't have anything to marshal if there's no config
2025-03-04 10:33:43 -05:00
mp , err := msgpack . Marshal ( r . Config , schema . Provider . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . Configure_Request {
2019-08-30 07:12:41 -04:00
TerraformVersion : r . TerraformVersion ,
2018-08-08 16:57:31 -04:00
Config : & proto . DynamicValue {
Msgpack : mp ,
} ,
2024-12-18 03:30:32 -05:00
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
2018-08-08 16:57:31 -04:00
}
protoResp , err := p . client . Configure ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-08-15 12:03:39 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2018-08-08 16:57:31 -04:00
return resp
}
func ( p * GRPCProvider ) Stop ( ) error {
2020-10-22 17:28:54 -04:00
logger . Trace ( "GRPCProvider: Stop" )
2018-08-24 20:10:26 -04:00
2018-08-08 16:57:31 -04:00
resp , err := p . client . Stop ( p . ctx , new ( proto . Stop_Request ) )
if err != nil {
return err
}
if resp . Error != "" {
return errors . New ( resp . Error )
}
return nil
}
func ( p * GRPCProvider ) ReadResource ( r providers . ReadResourceRequest ) ( resp providers . ReadResourceResponse ) {
2020-10-22 17:28:54 -04:00
logger . Trace ( "GRPCProvider: ReadResource" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
resSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok {
2025-02-12 11:51:53 -05:00
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %s" , r . TypeName ) )
2022-05-03 10:22:55 -04:00
return resp
}
2018-08-08 16:57:31 -04:00
2022-06-02 18:03:35 -04:00
metaSchema := schema . ProviderMeta
2025-03-04 10:33:43 -05:00
mp , err := msgpack . Marshal ( r . PriorState , resSchema . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . ReadResource_Request {
2024-12-18 03:30:32 -05:00
TypeName : r . TypeName ,
CurrentState : & proto . DynamicValue { Msgpack : mp } ,
Private : r . Private ,
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
2018-08-08 16:57:31 -04:00
}
2025-03-04 10:33:43 -05:00
if metaSchema . Body != nil {
metaMP , err := msgpack . Marshal ( r . ProviderMeta , metaSchema . Body . ImpliedType ( ) )
2020-03-05 19:53:24 -05:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq . ProviderMeta = & proto . DynamicValue { Msgpack : metaMP }
}
2025-02-27 06:17:00 -05:00
if ! r . CurrentIdentity . IsNull ( ) {
if resSchema . Identity == nil {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "identity type not found for resource type %s" , r . TypeName ) )
return resp
}
currentIdentityMP , err := msgpack . Marshal ( r . CurrentIdentity , resSchema . Identity . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq . CurrentIdentity = & proto . ResourceIdentityData {
IdentityData : & proto . DynamicValue { Msgpack : currentIdentityMP } ,
}
}
2018-08-08 16:57:31 -04:00
protoResp , err := p . client . ReadResource ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2024-03-26 12:22:04 -04:00
resp . Deferred = convert . ProtoToDeferred ( protoResp . Deferred )
2018-08-15 12:03:39 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2018-08-08 16:57:31 -04:00
2025-03-04 10:33:43 -05:00
state , err := decodeDynamicValue ( protoResp . NewState , resSchema . Body . ImpliedType ( ) )
2020-10-16 15:05:49 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
2018-08-08 16:57:31 -04:00
}
resp . NewState = state
2019-06-03 18:08:26 -04:00
resp . Private = protoResp . Private
2018-10-03 13:45:12 -04:00
2025-02-27 06:17:00 -05:00
if protoResp . NewIdentity != nil && protoResp . NewIdentity . IdentityData != nil {
if resSchema . Identity == nil {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown identity type %q" , r . TypeName ) )
}
resp . Identity , err = decodeDynamicValue ( protoResp . NewIdentity . IdentityData , resSchema . Identity . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
}
}
2018-08-08 16:57:31 -04:00
return resp
}
func ( p * GRPCProvider ) PlanResourceChange ( r providers . PlanResourceChangeRequest ) ( resp providers . PlanResourceChangeResponse ) {
2020-10-22 17:28:54 -04:00
logger . Trace ( "GRPCProvider: PlanResourceChange" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
resSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q" , r . TypeName ) )
return resp
}
metaSchema := schema . ProviderMeta
2022-07-06 13:47:30 -04:00
capabilities := schema . ServerCapabilities
2022-06-02 18:03:35 -04:00
// If the provider doesn't support planning a destroy operation, we can
// return immediately.
if r . ProposedNewState . IsNull ( ) && ! capabilities . PlanDestroy {
resp . PlannedState = r . ProposedNewState
resp . PlannedPrivate = r . PriorPrivate
2022-05-03 10:22:55 -04:00
return resp
}
2018-08-08 16:57:31 -04:00
2025-03-04 10:33:43 -05:00
priorMP , err := msgpack . Marshal ( r . PriorState , resSchema . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2018-12-03 12:47:31 -05:00
2025-03-04 10:33:43 -05:00
configMP , err := msgpack . Marshal ( r . Config , resSchema . Body . ImpliedType ( ) )
2018-12-03 12:47:31 -05:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2025-03-04 10:33:43 -05:00
propMP , err := msgpack . Marshal ( r . ProposedNewState , resSchema . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . PlanResourceChange_Request {
2024-12-18 03:30:32 -05:00
TypeName : r . TypeName ,
PriorState : & proto . DynamicValue { Msgpack : priorMP } ,
Config : & proto . DynamicValue { Msgpack : configMP } ,
ProposedNewState : & proto . DynamicValue { Msgpack : propMP } ,
PriorPrivate : r . PriorPrivate ,
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
2018-08-08 16:57:31 -04:00
}
2025-03-04 10:33:43 -05:00
if metaSchema . Body != nil {
metaTy := metaSchema . Body . ImpliedType ( )
2024-02-13 19:28:49 -05:00
metaVal := r . ProviderMeta
if metaVal == cty . NilVal {
metaVal = cty . NullVal ( metaTy )
}
metaMP , err := msgpack . Marshal ( metaVal , metaTy )
2020-03-05 19:53:24 -05:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq . ProviderMeta = & proto . DynamicValue { Msgpack : metaMP }
}
2025-02-27 06:17:00 -05:00
if ! r . PriorIdentity . IsNull ( ) {
if resSchema . Identity == nil {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "identity type not found for resource type %q" , r . TypeName ) )
return resp
}
priorIdentityMP , err := msgpack . Marshal ( r . PriorIdentity , resSchema . Identity . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq . PriorIdentity = & proto . ResourceIdentityData {
IdentityData : & proto . DynamicValue { Msgpack : priorIdentityMP } ,
}
}
2018-08-08 16:57:31 -04:00
protoResp , err := p . client . PlanResourceChange ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-08-15 12:03:39 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2018-08-08 16:57:31 -04:00
2025-03-04 10:33:43 -05:00
state , err := decodeDynamicValue ( protoResp . PlannedState , resSchema . Body . ImpliedType ( ) )
2020-10-16 15:05:49 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
2018-08-08 16:57:31 -04:00
}
resp . PlannedState = state
for _ , p := range protoResp . RequiresReplace {
2018-08-15 12:03:39 -04:00
resp . RequiresReplace = append ( resp . RequiresReplace , convert . AttributePathToPath ( p ) )
2018-08-08 16:57:31 -04:00
}
resp . PlannedPrivate = protoResp . PlannedPrivate
2019-02-08 23:32:44 -05:00
resp . LegacyTypeSystem = protoResp . LegacyTypeSystem
2024-04-08 09:35:16 -04:00
resp . Deferred = convert . ProtoToDeferred ( protoResp . Deferred )
2025-02-27 06:17:00 -05:00
if protoResp . PlannedIdentity != nil && protoResp . PlannedIdentity . IdentityData != nil {
if resSchema . Identity == nil {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown identity type %s" , r . TypeName ) )
return resp
}
resp . PlannedIdentity , err = decodeDynamicValue ( protoResp . PlannedIdentity . IdentityData , resSchema . Identity . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
}
2018-08-08 16:57:31 -04:00
return resp
}
func ( p * GRPCProvider ) ApplyResourceChange ( r providers . ApplyResourceChangeRequest ) ( resp providers . ApplyResourceChangeResponse ) {
2020-10-22 17:28:54 -04:00
logger . Trace ( "GRPCProvider: ApplyResourceChange" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
resSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q" , r . TypeName ) )
2022-05-03 10:22:55 -04:00
return resp
}
2018-08-08 16:57:31 -04:00
2022-06-02 18:03:35 -04:00
metaSchema := schema . ProviderMeta
2025-03-04 10:33:43 -05:00
priorMP , err := msgpack . Marshal ( r . PriorState , resSchema . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2025-03-04 10:33:43 -05:00
plannedMP , err := msgpack . Marshal ( r . PlannedState , resSchema . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2025-03-04 10:33:43 -05:00
configMP , err := msgpack . Marshal ( r . Config , resSchema . Body . ImpliedType ( ) )
2019-01-04 13:47:04 -05:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2018-08-08 16:57:31 -04:00
protoReq := & proto . ApplyResourceChange_Request {
TypeName : r . TypeName ,
PriorState : & proto . DynamicValue { Msgpack : priorMP } ,
PlannedState : & proto . DynamicValue { Msgpack : plannedMP } ,
2019-01-04 13:47:04 -05:00
Config : & proto . DynamicValue { Msgpack : configMP } ,
2018-08-08 16:57:31 -04:00
PlannedPrivate : r . PlannedPrivate ,
}
2025-03-04 10:33:43 -05:00
if metaSchema . Body != nil {
metaTy := metaSchema . Body . ImpliedType ( )
2024-02-13 19:28:49 -05:00
metaVal := r . ProviderMeta
if metaVal == cty . NilVal {
metaVal = cty . NullVal ( metaTy )
}
metaMP , err := msgpack . Marshal ( metaVal , metaTy )
2020-03-05 19:53:24 -05:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq . ProviderMeta = & proto . DynamicValue { Msgpack : metaMP }
}
2025-02-27 06:17:00 -05:00
if ! r . PlannedIdentity . IsNull ( ) {
if resSchema . Identity == nil {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "identity type not found for resource type %s" , r . TypeName ) )
return resp
}
identityMP , err := msgpack . Marshal ( r . PlannedIdentity , resSchema . Identity . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq . PlannedIdentity = & proto . ResourceIdentityData {
IdentityData : & proto . DynamicValue { Msgpack : identityMP } ,
}
}
2018-08-08 16:57:31 -04:00
protoResp , err := p . client . ApplyResourceChange ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-10-06 14:26:07 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2018-08-08 16:57:31 -04:00
resp . Private = protoResp . Private
2025-03-04 10:33:43 -05:00
state , err := decodeDynamicValue ( protoResp . NewState , resSchema . Body . ImpliedType ( ) )
2020-10-16 15:05:49 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
2018-08-08 16:57:31 -04:00
}
2018-10-03 13:45:12 -04:00
resp . NewState = state
2018-08-08 16:57:31 -04:00
2019-02-05 17:28:58 -05:00
resp . LegacyTypeSystem = protoResp . LegacyTypeSystem
2025-02-27 06:17:00 -05:00
if protoResp . NewIdentity != nil && protoResp . NewIdentity . IdentityData != nil {
if resSchema . Identity == nil {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "identity type not found for resource type %s" , r . TypeName ) )
return resp
}
newIdentity , err := decodeDynamicValue ( protoResp . NewIdentity . IdentityData , resSchema . Identity . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
resp . NewIdentity = newIdentity
}
2018-08-08 16:57:31 -04:00
return resp
}
func ( p * GRPCProvider ) ImportResourceState ( r providers . ImportResourceStateRequest ) ( resp providers . ImportResourceStateResponse ) {
2020-10-22 17:28:54 -04:00
logger . Trace ( "GRPCProvider: ImportResourceState" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
2018-08-08 16:57:31 -04:00
protoReq := & proto . ImportResourceState_Request {
2024-12-18 03:30:32 -05:00
TypeName : r . TypeName ,
Id : r . ID ,
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
2018-08-08 16:57:31 -04:00
}
2025-02-27 06:17:00 -05:00
if ! r . Identity . IsNull ( ) {
resSchema := schema . ResourceTypes [ r . TypeName ]
if resSchema . Identity == nil {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown identity type %q" , r . TypeName ) )
return resp
}
mp , err := msgpack . Marshal ( r . Identity , resSchema . Identity . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq . Identity = & proto . ResourceIdentityData {
IdentityData : & proto . DynamicValue {
Msgpack : mp ,
} ,
}
}
2018-08-08 16:57:31 -04:00
protoResp , err := p . client . ImportResourceState ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-10-06 14:26:07 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2024-04-15 09:17:00 -04:00
resp . Deferred = convert . ProtoToDeferred ( protoResp . Deferred )
2018-08-08 16:57:31 -04:00
for _ , imported := range protoResp . ImportedResources {
resource := providers . ImportedResource {
TypeName : imported . TypeName ,
Private : imported . Private ,
}
2022-06-02 18:03:35 -04:00
resSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q" , r . TypeName ) )
continue
2022-05-03 10:22:55 -04:00
}
2025-03-04 10:33:43 -05:00
state , err := decodeDynamicValue ( imported . State , resSchema . Body . ImpliedType ( ) )
2020-10-16 15:05:49 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
2018-10-03 13:45:12 -04:00
}
2018-08-08 16:57:31 -04:00
resource . State = state
2025-02-27 06:17:00 -05:00
if imported . Identity != nil && imported . Identity . IdentityData != nil {
importedIdentitySchema , ok := schema . ResourceTypes [ imported . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q" , imported . TypeName ) )
continue
}
2025-04-01 12:03:06 -04:00
importedIdentity , err := decodeDynamicValue ( imported . Identity . IdentityData , importedIdentitySchema . Identity . ImpliedType ( ) )
2025-02-27 06:17:00 -05:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
resource . Identity = importedIdentity
}
2018-08-08 16:57:31 -04:00
resp . ImportedResources = append ( resp . ImportedResources , resource )
}
return resp
}
2025-07-25 15:28:18 -04:00
func ( p * GRPCProvider ) GenerateResourceConfig ( r providers . GenerateResourceConfigRequest ) ( resp providers . GenerateResourceConfigResponse ) {
logger . Trace ( "GRPCProvider: GenerateResourceConfig" )
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
resSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q" , r . TypeName ) )
return resp
}
2025-12-10 09:19:29 -05:00
mp , err := msgpack . Marshal ( r . State , resSchema . Body . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2025-07-25 15:28:18 -04:00
protoReq := & proto . GenerateResourceConfig_Request {
TypeName : r . TypeName ,
2025-12-10 09:19:29 -05:00
State : & proto . DynamicValue { Msgpack : mp } ,
2025-07-25 15:28:18 -04:00
}
protoResp , err := p . client . GenerateResourceConfig ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
ty := resSchema . Body . ImpliedType ( )
state , err := decodeDynamicValue ( protoResp . Config , ty )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
resp . Config = state
return resp
}
2024-01-11 04:08:50 -05:00
func ( p * GRPCProvider ) MoveResourceState ( r providers . MoveResourceStateRequest ) ( resp providers . MoveResourceStateResponse ) {
logger . Trace ( "GRPCProvider: MoveResourceState" )
protoReq := & proto . MoveResourceState_Request {
SourceProviderAddress : r . SourceProviderAddress ,
SourceTypeName : r . SourceTypeName ,
SourceSchemaVersion : r . SourceSchemaVersion ,
SourceState : & proto . RawState {
Json : r . SourceStateJSON ,
} ,
2024-01-26 07:46:31 -05:00
SourcePrivate : r . SourcePrivate ,
2024-01-11 04:08:50 -05:00
TargetTypeName : r . TargetTypeName ,
}
2025-02-27 06:17:00 -05:00
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
if len ( r . SourceIdentity ) > 0 {
protoReq . SourceIdentity = & proto . RawState {
Json : r . SourceIdentity ,
}
}
2024-01-11 04:08:50 -05:00
protoResp , err := p . client . MoveResourceState ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
if resp . Diagnostics . HasErrors ( ) {
return resp
}
targetType , ok := schema . ResourceTypes [ r . TargetTypeName ]
if ! ok {
// We should have validated this earlier in the process, but we'll
// still return an error instead of crashing in case something went
// wrong.
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q; this is a bug in Terraform - please report it" , r . TargetTypeName ) )
return resp
}
2025-03-04 10:33:43 -05:00
resp . TargetState , err = decodeDynamicValue ( protoResp . TargetState , targetType . Body . ImpliedType ( ) )
2024-01-11 04:08:50 -05:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2024-01-26 07:46:31 -05:00
resp . TargetPrivate = protoResp . TargetPrivate
2025-02-27 06:17:00 -05:00
if protoResp . TargetIdentity != nil && protoResp . TargetIdentity . IdentityData != nil {
targetResSchema := schema . ResourceTypes [ r . TargetTypeName ]
if targetResSchema . Identity == nil {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown identity type %s" , r . TargetTypeName ) )
return resp
}
resp . TargetIdentity , err = decodeDynamicValue ( protoResp . TargetIdentity . IdentityData , targetResSchema . Identity . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
}
2024-01-11 04:08:50 -05:00
return resp
}
2018-08-08 16:57:31 -04:00
func ( p * GRPCProvider ) ReadDataSource ( r providers . ReadDataSourceRequest ) ( resp providers . ReadDataSourceResponse ) {
2020-10-22 17:28:54 -04:00
logger . Trace ( "GRPCProvider: ReadDataSource" )
2018-08-24 20:10:26 -04:00
2023-07-03 12:37:50 -04:00
schema := p . GetProviderSchema ( )
2022-06-02 18:03:35 -04:00
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
2022-05-03 10:22:55 -04:00
return resp
}
2018-08-08 16:57:31 -04:00
2022-06-02 18:03:35 -04:00
dataSchema , ok := schema . DataSources [ r . TypeName ]
if ! ok {
schema . Diagnostics = schema . Diagnostics . Append ( fmt . Errorf ( "unknown data source %q" , r . TypeName ) )
}
metaSchema := schema . ProviderMeta
2025-03-04 10:33:43 -05:00
config , err := msgpack . Marshal ( r . Config , dataSchema . Body . ImpliedType ( ) )
2018-08-08 16:57:31 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . ReadDataSource_Request {
TypeName : r . TypeName ,
Config : & proto . DynamicValue {
Msgpack : config ,
} ,
2024-12-18 03:30:32 -05:00
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
2018-08-08 16:57:31 -04:00
}
2025-03-04 10:33:43 -05:00
if metaSchema . Body != nil {
metaMP , err := msgpack . Marshal ( r . ProviderMeta , metaSchema . Body . ImpliedType ( ) )
2020-03-05 19:53:24 -05:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq . ProviderMeta = & proto . DynamicValue { Msgpack : metaMP }
}
2018-08-08 16:57:31 -04:00
protoResp , err := p . client . ReadDataSource ( p . ctx , protoReq )
if err != nil {
2020-10-22 17:28:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
2018-08-08 16:57:31 -04:00
return resp
}
2018-10-06 14:26:07 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2025-03-04 10:33:43 -05:00
state , err := decodeDynamicValue ( protoResp . State , dataSchema . Body . ImpliedType ( ) )
2020-10-16 15:05:49 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
2018-08-08 16:57:31 -04:00
}
resp . State = state
2024-04-04 11:03:39 -04:00
resp . Deferred = convert . ProtoToDeferred ( protoResp . Deferred )
2018-08-08 16:57:31 -04:00
2018-10-03 13:45:12 -04:00
return resp
2018-08-08 16:57:31 -04:00
}
2024-09-26 17:14:01 -04:00
func ( p * GRPCProvider ) ValidateEphemeralResourceConfig ( r providers . ValidateEphemeralResourceConfigRequest ) ( resp providers . ValidateEphemeralResourceConfigResponse ) {
2024-09-20 11:08:50 -04:00
logger . Trace ( "GRPCProvider: ValidateEphemeralResourceConfig" )
2024-09-26 17:14:01 -04:00
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
ephemSchema , ok := schema . EphemeralResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown ephemeral resource %q" , r . TypeName ) )
return resp
}
2025-03-04 10:33:43 -05:00
mp , err := msgpack . Marshal ( r . Config , ephemSchema . Body . ImpliedType ( ) )
2024-09-26 17:14:01 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . ValidateEphemeralResourceConfig_Request {
TypeName : r . TypeName ,
Config : & proto . DynamicValue { Msgpack : mp } ,
}
protoResp , err := p . client . ValidateEphemeralResourceConfig ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
return resp
2024-09-19 10:10:20 -04:00
}
2024-09-20 11:08:50 -04:00
func ( p * GRPCProvider ) OpenEphemeralResource ( r providers . OpenEphemeralResourceRequest ) ( resp providers . OpenEphemeralResourceResponse ) {
logger . Trace ( "GRPCProvider: OpenEphemeralResource" )
2024-09-26 17:14:01 -04:00
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
ephemSchema , ok := schema . EphemeralResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown ephemeral resource %q" , r . TypeName ) )
return resp
}
2025-03-04 10:33:43 -05:00
config , err := msgpack . Marshal ( r . Config , ephemSchema . Body . ImpliedType ( ) )
2024-09-26 17:14:01 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . OpenEphemeralResource_Request {
TypeName : r . TypeName ,
Config : & proto . DynamicValue {
Msgpack : config ,
} ,
2024-12-18 03:30:32 -05:00
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
2024-09-26 17:14:01 -04:00
}
protoResp , err := p . client . OpenEphemeralResource ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
2025-03-04 10:33:43 -05:00
state , err := decodeDynamicValue ( protoResp . Result , ephemSchema . Body . ImpliedType ( ) )
2024-09-26 17:14:01 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
if protoResp . RenewAt != nil {
resp . RenewAt = protoResp . RenewAt . AsTime ( )
}
resp . Result = state
resp . Private = protoResp . Private
resp . Deferred = convert . ProtoToDeferred ( protoResp . Deferred )
return resp
2024-09-13 08:54:44 -04:00
}
2024-09-20 11:08:50 -04:00
func ( p * GRPCProvider ) RenewEphemeralResource ( r providers . RenewEphemeralResourceRequest ) ( resp providers . RenewEphemeralResourceResponse ) {
logger . Trace ( "GRPCProvider: RenewEphemeralResource" )
2024-09-26 17:14:01 -04:00
protoReq := & proto . RenewEphemeralResource_Request {
TypeName : r . TypeName ,
Private : r . Private ,
}
protoResp , err := p . client . RenewEphemeralResource ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
if protoResp . RenewAt != nil {
resp . RenewAt = protoResp . RenewAt . AsTime ( )
}
resp . Private = protoResp . Private
return resp
2024-09-13 08:54:44 -04:00
}
2024-09-20 11:08:50 -04:00
func ( p * GRPCProvider ) CloseEphemeralResource ( r providers . CloseEphemeralResourceRequest ) ( resp providers . CloseEphemeralResourceResponse ) {
logger . Trace ( "GRPCProvider: CloseEphemeralResource" )
2024-09-26 17:14:01 -04:00
protoReq := & proto . CloseEphemeralResource_Request {
TypeName : r . TypeName ,
Private : r . Private ,
}
protoResp , err := p . client . CloseEphemeralResource ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
return resp
2024-09-13 08:54:44 -04:00
}
2023-09-28 16:54:44 -04:00
func ( p * GRPCProvider ) CallFunction ( r providers . CallFunctionRequest ) ( resp providers . CallFunctionResponse ) {
logger . Trace ( "GRPCProvider" , "CallFunction" , r . FunctionName )
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
2024-01-31 15:10:41 -05:00
resp . Err = schema . Diagnostics . Err ( )
2023-09-28 16:54:44 -04:00
return resp
}
funcDecl , ok := schema . Functions [ r . FunctionName ]
// We check for various problems with the request below in the interests
// of robustness, just to avoid crashing while trying to encode/decode, but
// if we reach any of these errors then that suggests a bug in the caller,
// because we should catch function calls that don't match the schema at an
// earlier point than this.
if ! ok {
// Should only get here if the caller has a bug, because we should
// have detected earlier any attempt to call a function that the
// provider didn't declare.
2024-01-31 15:10:41 -05:00
resp . Err = fmt . Errorf ( "provider has no function named %q" , r . FunctionName )
2023-09-28 16:54:44 -04:00
return resp
}
if len ( r . Arguments ) < len ( funcDecl . Parameters ) {
2024-01-31 15:10:41 -05:00
resp . Err = fmt . Errorf ( "not enough arguments for function %q" , r . FunctionName )
2023-09-28 16:54:44 -04:00
return resp
}
if funcDecl . VariadicParameter == nil && len ( r . Arguments ) > len ( funcDecl . Parameters ) {
2024-01-31 15:10:41 -05:00
resp . Err = fmt . Errorf ( "too many arguments for function %q" , r . FunctionName )
2023-09-28 16:54:44 -04:00
return resp
}
args := make ( [ ] * proto . DynamicValue , len ( r . Arguments ) )
for i , argVal := range r . Arguments {
var paramDecl providers . FunctionParam
if i < len ( funcDecl . Parameters ) {
paramDecl = funcDecl . Parameters [ i ]
} else {
paramDecl = * funcDecl . VariadicParameter
}
argValRaw , err := msgpack . Marshal ( argVal , paramDecl . Type )
if err != nil {
2024-01-31 15:10:41 -05:00
resp . Err = err
2023-09-28 16:54:44 -04:00
return resp
}
args [ i ] = & proto . DynamicValue {
Msgpack : argValRaw ,
}
}
protoResp , err := p . client . CallFunction ( p . ctx , & proto . CallFunction_Request {
Name : r . FunctionName ,
Arguments : args ,
} )
if err != nil {
2024-01-31 15:10:41 -05:00
// functions can only support simple errors, but use our grpcError
// diagnostic function to format common problems is a more
// user-friendly manner.
resp . Err = grpcErr ( err ) . Err ( )
2023-09-28 16:54:44 -04:00
return resp
}
2024-01-31 15:10:41 -05:00
if protoResp . Error != nil {
resp . Err = errors . New ( protoResp . Error . Text )
// If this is a problem with a specific argument, we can wrap the error
// in a function.ArgError
if protoResp . Error . FunctionArgument != nil {
resp . Err = function . NewArgError ( int ( * protoResp . Error . FunctionArgument ) , resp . Err )
}
2023-09-28 16:54:44 -04:00
return resp
}
resultVal , err := decodeDynamicValue ( protoResp . Result , funcDecl . ReturnType )
if err != nil {
2024-01-31 15:10:41 -05:00
resp . Err = err
2023-09-28 16:54:44 -04:00
return resp
}
resp . Result = resultVal
return resp
}
2025-06-04 03:40:10 -04:00
func ( p * GRPCProvider ) ListResource ( r providers . ListResourceRequest ) providers . ListResourceResponse {
logger . Trace ( "GRPCProvider: ListResource" )
var resp providers . ListResourceResponse
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
listResourceSchema , ok := schema . ListResourceTypes [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown list resource type %q" , r . TypeName ) )
return resp
}
resourceSchema , ok := schema . ResourceTypes [ r . TypeName ]
if ! ok || resourceSchema . Identity == nil {
2025-11-04 07:43:19 -05:00
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "Identity schema not found for resource type %s; this is a bug in the provider - please report it there" , r . TypeName ) )
2025-06-04 03:40:10 -04:00
return resp
}
2025-06-10 14:08:54 -04:00
configSchema := listResourceSchema . Body . BlockTypes [ "config" ]
2025-09-24 03:30:59 -04:00
if ! r . Config . Type ( ) . HasAttribute ( "config" ) {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "missing required attribute \"config\"; this is a bug in Terraform - please report it" ) )
return resp
2025-06-13 07:28:19 -04:00
}
2025-09-24 03:30:59 -04:00
config := r . Config . GetAttr ( "config" )
2025-06-13 07:28:19 -04:00
mp , err := msgpack . Marshal ( config , configSchema . ImpliedType ( ) )
2025-06-04 03:40:10 -04:00
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . ListResource_Request {
TypeName : r . TypeName ,
Config : & proto . DynamicValue { Msgpack : mp } ,
IncludeResourceObject : r . IncludeResourceObject ,
Limit : r . Limit ,
}
// Start the streaming RPC with a context. The context will be cancelled
// when this function returns, which will stop the stream if it is still
// running.
ctx , cancel := context . WithCancel ( p . ctx )
defer cancel ( )
client , err := p . client . ListResource ( ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
2025-06-10 14:08:54 -04:00
resp . Result = cty . DynamicVal
results := make ( [ ] cty . Value , 0 )
2025-06-04 03:40:10 -04:00
// Process the stream
for {
if int64 ( len ( results ) ) >= r . Limit {
// If we have reached the limit, we stop receiving events
2025-06-10 14:08:54 -04:00
break
2025-06-04 03:40:10 -04:00
}
event , err := client . Recv ( )
if err == io . EOF {
// End of stream, we're done
2025-06-10 14:08:54 -04:00
break
2025-06-04 03:40:10 -04:00
}
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
2025-06-10 14:08:54 -04:00
break
2025-06-04 03:40:10 -04:00
}
2025-08-08 05:24:05 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( event . Diagnostic ) )
if resp . Diagnostics . HasErrors ( ) {
// If we have errors, we stop processing and return early
break
}
if resp . Diagnostics . HasWarnings ( ) &&
( event . Identity == nil || event . Identity . IdentityData == nil ) {
// If we have warnings but no identity data, we continue with the next event
continue
}
2025-06-10 14:08:54 -04:00
obj := map [ string ] cty . Value {
"display_name" : cty . StringVal ( event . DisplayName ) ,
"state" : cty . NullVal ( resourceSchema . Body . ImpliedType ( ) ) ,
"identity" : cty . NullVal ( resourceSchema . Identity . ImpliedType ( ) ) ,
2025-06-04 03:40:10 -04:00
}
// Handle identity data - it must be present
if event . Identity == nil || event . Identity . IdentityData == nil {
2025-06-10 14:08:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "missing identity data in ListResource event for %s" , r . TypeName ) )
2025-06-04 03:40:10 -04:00
} else {
identityVal , err := decodeDynamicValue ( event . Identity . IdentityData , resourceSchema . Identity . ImpliedType ( ) )
if err != nil {
2025-06-10 14:08:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( err )
2025-06-04 03:40:10 -04:00
} else {
2025-06-10 14:08:54 -04:00
obj [ "identity" ] = identityVal
2025-06-04 03:40:10 -04:00
}
}
// Handle resource object if present and requested
if event . ResourceObject != nil && r . IncludeResourceObject {
// Use the ResourceTypes schema for the resource object
resourceObj , err := decodeDynamicValue ( event . ResourceObject , resourceSchema . Body . ImpliedType ( ) )
if err != nil {
2025-06-10 14:08:54 -04:00
resp . Diagnostics = resp . Diagnostics . Append ( err )
2025-06-04 03:40:10 -04:00
} else {
2025-06-10 14:08:54 -04:00
obj [ "state" ] = resourceObj
2025-06-04 03:40:10 -04:00
}
}
2025-06-10 14:08:54 -04:00
if resp . Diagnostics . HasErrors ( ) {
2025-08-08 05:24:05 -04:00
// If validation errors occurred, we stop processing and return early
2025-06-10 14:08:54 -04:00
break
}
results = append ( results , cty . ObjectVal ( obj ) )
2025-06-04 03:40:10 -04:00
}
2025-06-10 14:08:54 -04:00
// The provider result of a list resource is always a list, but
// we will wrap that list in an object with a single attribute "data",
// so that we can differentiate between a list resource instance (list.aws_instance.test[index])
// and the elements of the result of a list resource instance (list.aws_instance.test.data[index])
2025-06-18 05:16:29 -04:00
resp . Result = cty . ObjectVal ( map [ string ] cty . Value {
"data" : cty . TupleVal ( results ) ,
"config" : config ,
} )
2025-06-10 14:08:54 -04:00
return resp
2025-05-19 04:20:52 -04:00
}
2025-06-03 09:52:35 -04:00
func ( p * GRPCProvider ) ValidateStateStoreConfig ( r providers . ValidateStateStoreConfigRequest ) providers . ValidateStateStoreConfigResponse {
panic ( "not implemented" )
}
func ( p * GRPCProvider ) ConfigureStateStore ( r providers . ConfigureStateStoreRequest ) providers . ConfigureStateStoreResponse {
panic ( "not implemented" )
}
2025-09-26 12:03:02 -04:00
func ( p * GRPCProvider ) ReadStateBytes ( r providers . ReadStateBytesRequest ) providers . ReadStateBytesResponse {
panic ( "not implemented" )
}
func ( p * GRPCProvider ) WriteStateBytes ( r providers . WriteStateBytesRequest ) providers . WriteStateBytesResponse {
panic ( "not implemented" )
}
2025-10-03 05:02:02 -04:00
func ( p * GRPCProvider ) LockState ( r providers . LockStateRequest ) providers . LockStateResponse {
panic ( "not implemented" )
}
func ( p * GRPCProvider ) UnlockState ( r providers . UnlockStateRequest ) providers . UnlockStateResponse {
panic ( "not implemented" )
}
2025-07-07 05:40:45 -04:00
func ( p * GRPCProvider ) GetStates ( r providers . GetStatesRequest ) providers . GetStatesResponse {
panic ( "not implemented" )
}
func ( p * GRPCProvider ) DeleteState ( r providers . DeleteStateRequest ) providers . DeleteStateResponse {
panic ( "not implemented" )
}
2025-06-30 06:39:16 -04:00
func ( p * GRPCProvider ) PlanAction ( r providers . PlanActionRequest ) ( resp providers . PlanActionResponse ) {
logger . Trace ( "GRPCProvider: PlanAction" )
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
actionSchema , ok := schema . Actions [ r . ActionType ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown action %q" , r . ActionType ) )
return resp
}
configMP , err := msgpack . Marshal ( r . ProposedActionData , actionSchema . ConfigSchema . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . PlanAction_Request {
ActionType : r . ActionType ,
Config : & proto . DynamicValue { Msgpack : configMP } ,
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
}
protoResp , err := p . client . PlanAction ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
if resp . Diagnostics . HasErrors ( ) {
return resp
}
return resp
}
func ( p * GRPCProvider ) InvokeAction ( r providers . InvokeActionRequest ) ( resp providers . InvokeActionResponse ) {
logger . Trace ( "GRPCProvider: InvokeAction" )
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
actionSchema , ok := schema . Actions [ r . ActionType ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown action %q" , r . ActionType ) )
return resp
}
configMP , err := msgpack . Marshal ( r . PlannedActionData , actionSchema . ConfigSchema . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . InvokeAction_Request {
2025-08-05 08:14:32 -04:00
ActionType : r . ActionType ,
Config : & proto . DynamicValue { Msgpack : configMP } ,
ClientCapabilities : clientCapabilitiesToProto ( r . ClientCapabilities ) ,
2025-06-30 06:39:16 -04:00
}
protoClient , err := p . client . InvokeAction ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Events = func ( yield func ( providers . InvokeActionEvent ) bool ) {
logger . Trace ( "GRPCProvider: InvokeAction: streaming events" )
for {
event , err := protoClient . Recv ( )
if err == io . EOF {
logger . Trace ( "GRPCProvider: InvokeAction: end of stream" )
break
}
if err != nil {
// We handle this by returning a finished response with the error
// If the client errors we won't be receiving any more events.
yield ( providers . InvokeActionEvent_Completed {
Diagnostics : grpcErr ( err ) ,
} )
break
}
switch ev := event . Type . ( type ) {
case * proto . InvokeAction_Event_Progress_ :
yield ( providers . InvokeActionEvent_Progress {
Message : ev . Progress . Message ,
} )
case * proto . InvokeAction_Event_Completed_ :
diags := convert . ProtoToDiagnostics ( ev . Completed . Diagnostics )
2025-09-12 05:09:07 -04:00
2025-06-30 06:39:16 -04:00
yield ( providers . InvokeActionEvent_Completed {
2025-09-12 05:09:07 -04:00
Diagnostics : diags ,
2025-06-30 06:39:16 -04:00
} )
default :
panic ( fmt . Sprintf ( "unexpected event type %T in InvokeAction response" , event . Type ) )
}
}
}
return resp
}
2025-07-18 14:14:44 -04:00
func ( p * GRPCProvider ) ValidateActionConfig ( r providers . ValidateActionConfigRequest ) ( resp providers . ValidateActionConfigResponse ) {
logger . Trace ( "GRPCProvider: ValidateActionConfig" )
schema := p . GetProviderSchema ( )
if schema . Diagnostics . HasErrors ( ) {
resp . Diagnostics = schema . Diagnostics
return resp
}
actionSchema , ok := schema . Actions [ r . TypeName ]
if ! ok {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "unknown resource type %q" , r . TypeName ) )
return resp
}
mp , err := msgpack . Marshal ( r . Config , actionSchema . ConfigSchema . ImpliedType ( ) )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( err )
return resp
}
protoReq := & proto . ValidateActionConfig_Request {
TypeName : r . TypeName ,
Config : & proto . DynamicValue { Msgpack : mp } ,
}
protoResp , err := p . client . ValidateActionConfig ( p . ctx , protoReq )
if err != nil {
resp . Diagnostics = resp . Diagnostics . Append ( grpcErr ( err ) )
return resp
}
resp . Diagnostics = resp . Diagnostics . Append ( convert . ProtoToDiagnostics ( protoResp . Diagnostics ) )
return resp
}
2018-08-08 16:57:31 -04:00
// closing the grpc connection is final, and terraform will call it at the end of every phase.
func ( p * GRPCProvider ) Close ( ) error {
2020-10-22 17:28:54 -04:00
logger . Trace ( "GRPCProvider: Close" )
2018-10-16 14:31:27 -04:00
2019-04-29 14:14:04 -04:00
// Make sure to stop the server if we're not running within go-plugin.
if p . TestServer != nil {
p . TestServer . Stop ( )
2018-10-16 14:31:27 -04:00
}
// Check this since it's not automatically inserted during plugin creation.
// It's currently only inserted by the command package, because that is
// where the factory is built and is the only point with access to the
// plugin.Client.
2018-10-02 16:06:12 -04:00
if p . PluginClient == nil {
2020-10-22 17:28:54 -04:00
logger . Debug ( "provider has no plugin.Client" )
2018-10-02 16:06:12 -04:00
return nil
}
p . PluginClient . Kill ( )
2018-08-08 16:57:31 -04:00
return nil
}
2020-10-16 15:05:49 -04:00
// Decode a DynamicValue from either the JSON or MsgPack encoding.
func decodeDynamicValue ( v * proto . DynamicValue , ty cty . Type ) ( cty . Value , error ) {
// always return a valid value
2020-10-19 09:31:37 -04:00
var err error
2020-10-16 15:05:49 -04:00
res := cty . NullVal ( ty )
if v == nil {
return res , nil
}
2020-10-19 09:31:37 -04:00
switch {
case len ( v . Msgpack ) > 0 :
res , err = msgpack . Unmarshal ( v . Msgpack , ty )
case len ( v . Json ) > 0 :
res , err = ctyjson . Unmarshal ( v . Json , ty )
2020-10-16 15:05:49 -04:00
}
2020-10-19 09:31:37 -04:00
return res , err
2020-10-16 15:05:49 -04:00
}
2024-12-18 03:30:32 -05:00
func clientCapabilitiesToProto ( c providers . ClientCapabilities ) * proto . ClientCapabilities {
return & proto . ClientCapabilities {
DeferralAllowed : c . DeferralAllowed ,
WriteOnlyAttributesAllowed : c . WriteOnlyAttributesAllowed ,
}
}