10 plugin "github.com/hashicorp/go-plugin"
11 "github.com/hashicorp/terraform/configs/configschema"
12 proto "github.com/hashicorp/terraform/internal/tfplugin5"
13 "github.com/hashicorp/terraform/plugin/convert"
14 "github.com/hashicorp/terraform/provisioners"
15 "github.com/zclconf/go-cty/cty"
16 "github.com/zclconf/go-cty/cty/msgpack"
17 "google.golang.org/grpc"
20 // GRPCProvisionerPlugin is the plugin.GRPCPlugin implementation.
21 type GRPCProvisionerPlugin struct {
23 GRPCProvisioner func() proto.ProvisionerServer
26 func (p *GRPCProvisionerPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
27 return &GRPCProvisioner{
28 client: proto.NewProvisionerClient(c),
33 func (p *GRPCProvisionerPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
34 proto.RegisterProvisionerServer(s, p.GRPCProvisioner())
38 // provisioners.Interface grpc implementation
39 type GRPCProvisioner struct {
40 // PluginClient provides a reference to the plugin.Client which controls the plugin process.
41 // This allows the GRPCProvider a way to shutdown the plugin process.
42 PluginClient *plugin.Client
44 client proto.ProvisionerClient
47 // Cache the schema since we need it for serialization in each method call.
49 schema *configschema.Block
52 func (p *GRPCProvisioner) GetSchema() (resp provisioners.GetSchemaResponse) {
57 return provisioners.GetSchemaResponse{
58 Provisioner: p.schema,
62 protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProvisionerSchema_Request))
64 resp.Diagnostics = resp.Diagnostics.Append(err)
67 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
69 if protoResp.Provisioner == nil {
70 resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provisioner schema"))
74 resp.Provisioner = convert.ProtoToConfigSchema(protoResp.Provisioner.Block)
76 p.schema = resp.Provisioner
81 func (p *GRPCProvisioner) ValidateProvisionerConfig(r provisioners.ValidateProvisionerConfigRequest) (resp provisioners.ValidateProvisionerConfigResponse) {
82 schema := p.GetSchema()
83 if schema.Diagnostics.HasErrors() {
84 resp.Diagnostics = resp.Diagnostics.Append(schema.Diagnostics)
88 mp, err := msgpack.Marshal(r.Config, schema.Provisioner.ImpliedType())
90 resp.Diagnostics = resp.Diagnostics.Append(err)
94 protoReq := &proto.ValidateProvisionerConfig_Request{
95 Config: &proto.DynamicValue{Msgpack: mp},
97 protoResp, err := p.client.ValidateProvisionerConfig(p.ctx, protoReq)
99 resp.Diagnostics = resp.Diagnostics.Append(err)
102 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
106 func (p *GRPCProvisioner) ProvisionResource(r provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
107 schema := p.GetSchema()
108 if schema.Diagnostics.HasErrors() {
109 resp.Diagnostics = resp.Diagnostics.Append(schema.Diagnostics)
113 mp, err := msgpack.Marshal(r.Config, schema.Provisioner.ImpliedType())
115 resp.Diagnostics = resp.Diagnostics.Append(err)
119 // connection is always assumed to be a simple string map
120 connMP, err := msgpack.Marshal(r.Connection, cty.Map(cty.String))
122 resp.Diagnostics = resp.Diagnostics.Append(err)
126 protoReq := &proto.ProvisionResource_Request{
127 Config: &proto.DynamicValue{Msgpack: mp},
128 Connection: &proto.DynamicValue{Msgpack: connMP},
131 outputClient, err := p.client.ProvisionResource(p.ctx, protoReq)
133 resp.Diagnostics = resp.Diagnostics.Append(err)
138 rcv, err := outputClient.Recv()
140 r.UIOutput.Output(rcv.Output)
144 resp.Diagnostics = resp.Diagnostics.Append(err)
149 if len(rcv.Diagnostics) > 0 {
150 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(rcv.Diagnostics))
158 func (p *GRPCProvisioner) Stop() error {
159 protoResp, err := p.client.Stop(p.ctx, &proto.Stop_Request{})
163 if protoResp.Error != "" {
164 return errors.New(protoResp.Error)
169 func (p *GRPCProvisioner) Close() error {
170 // check this since it's not automatically inserted during plugin creation
171 if p.PluginClient == nil {
172 log.Println("[DEBUG] provider has no plugin.Client")
176 p.PluginClient.Kill()