6 "github.com/hashicorp/terraform/helper/schema"
7 proto "github.com/hashicorp/terraform/internal/tfplugin5"
8 "github.com/hashicorp/terraform/plugin/convert"
9 "github.com/hashicorp/terraform/terraform"
10 "github.com/zclconf/go-cty/cty"
11 ctyconvert "github.com/zclconf/go-cty/cty/convert"
12 "github.com/zclconf/go-cty/cty/msgpack"
13 context "golang.org/x/net/context"
16 // NewGRPCProvisionerServerShim wraps a terraform.ResourceProvisioner in a
17 // proto.ProvisionerServer implementation. If the provided provisioner is not a
18 // *schema.Provisioner, this will return nil,
19 func NewGRPCProvisionerServerShim(p terraform.ResourceProvisioner) *GRPCProvisionerServer {
20 sp, ok := p.(*schema.Provisioner)
24 return &GRPCProvisionerServer{
29 type GRPCProvisionerServer struct {
30 provisioner *schema.Provisioner
33 func (s *GRPCProvisionerServer) GetSchema(_ context.Context, req *proto.GetProvisionerSchema_Request) (*proto.GetProvisionerSchema_Response, error) {
34 resp := &proto.GetProvisionerSchema_Response{}
36 resp.Provisioner = &proto.Schema{
37 Block: convert.ConfigSchemaToProto(schema.InternalMap(s.provisioner.Schema).CoreConfigSchema()),
43 func (s *GRPCProvisionerServer) ValidateProvisionerConfig(_ context.Context, req *proto.ValidateProvisionerConfig_Request) (*proto.ValidateProvisionerConfig_Response, error) {
44 resp := &proto.ValidateProvisionerConfig_Response{}
46 cfgSchema := schema.InternalMap(s.provisioner.Schema).CoreConfigSchema()
48 configVal, err := msgpack.Unmarshal(req.Config.Msgpack, cfgSchema.ImpliedType())
50 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
54 config := terraform.NewResourceConfigShimmed(configVal, cfgSchema)
56 warns, errs := s.provisioner.Validate(config)
57 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
62 // stringMapFromValue converts a cty.Value to a map[stirng]string.
63 // This will panic if the val is not a cty.Map(cty.String).
64 func stringMapFromValue(val cty.Value) map[string]string {
65 m := map[string]string{}
66 if val.IsNull() || !val.IsKnown() {
70 for it := val.ElementIterator(); it.Next(); {
71 ak, av := it.Element()
74 if !av.IsKnown() || av.IsNull() {
78 av, _ = ctyconvert.Convert(av, cty.String)
79 m[name] = av.AsString()
85 // uiOutput implements the terraform.UIOutput interface to adapt the grpc
86 // stream to the legacy Provisioner.Apply method.
87 type uiOutput struct {
88 srv proto.Provisioner_ProvisionResourceServer
91 func (o uiOutput) Output(s string) {
92 err := o.srv.Send(&proto.ProvisionResource_Response{
96 log.Printf("[ERROR] %s", err)
100 func (s *GRPCProvisionerServer) ProvisionResource(req *proto.ProvisionResource_Request, srv proto.Provisioner_ProvisionResourceServer) error {
101 // We send back a diagnostics over the stream if there was a
102 // provisioner-side problem.
103 srvResp := &proto.ProvisionResource_Response{}
105 cfgSchema := schema.InternalMap(s.provisioner.Schema).CoreConfigSchema()
106 cfgVal, err := msgpack.Unmarshal(req.Config.Msgpack, cfgSchema.ImpliedType())
108 srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err)
112 resourceConfig := terraform.NewResourceConfigShimmed(cfgVal, cfgSchema)
114 connVal, err := msgpack.Unmarshal(req.Connection.Msgpack, cty.Map(cty.String))
116 srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err)
121 conn := stringMapFromValue(connVal)
123 instanceState := &terraform.InstanceState{
124 Ephemeral: terraform.EphemeralState{
127 Meta: make(map[string]interface{}),
130 err = s.provisioner.Apply(uiOutput{srv}, instanceState, resourceConfig)
132 srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err)
138 func (s *GRPCProvisionerServer) Stop(_ context.Context, req *proto.Stop_Request) (*proto.Stop_Response, error) {
139 resp := &proto.Stop_Response{}
141 err := s.provisioner.Stop()
143 resp.Error = err.Error()