]>
Commit | Line | Data |
---|---|---|
1 | package plugin | |
2 | ||
3 | import ( | |
4 | "context" | |
5 | "errors" | |
6 | "io" | |
7 | "log" | |
8 | "sync" | |
9 | ||
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" | |
18 | ) | |
19 | ||
20 | // GRPCProvisionerPlugin is the plugin.GRPCPlugin implementation. | |
21 | type GRPCProvisionerPlugin struct { | |
22 | plugin.Plugin | |
23 | GRPCProvisioner func() proto.ProvisionerServer | |
24 | } | |
25 | ||
26 | func (p *GRPCProvisionerPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { | |
27 | return &GRPCProvisioner{ | |
28 | client: proto.NewProvisionerClient(c), | |
29 | ctx: ctx, | |
30 | }, nil | |
31 | } | |
32 | ||
33 | func (p *GRPCProvisionerPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { | |
34 | proto.RegisterProvisionerServer(s, p.GRPCProvisioner()) | |
35 | return nil | |
36 | } | |
37 | ||
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 | |
43 | ||
44 | client proto.ProvisionerClient | |
45 | ctx context.Context | |
46 | ||
47 | // Cache the schema since we need it for serialization in each method call. | |
48 | mu sync.Mutex | |
49 | schema *configschema.Block | |
50 | } | |
51 | ||
52 | func (p *GRPCProvisioner) GetSchema() (resp provisioners.GetSchemaResponse) { | |
53 | p.mu.Lock() | |
54 | defer p.mu.Unlock() | |
55 | ||
56 | if p.schema != nil { | |
57 | return provisioners.GetSchemaResponse{ | |
58 | Provisioner: p.schema, | |
59 | } | |
60 | } | |
61 | ||
62 | protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProvisionerSchema_Request)) | |
63 | if err != nil { | |
64 | resp.Diagnostics = resp.Diagnostics.Append(err) | |
65 | return resp | |
66 | } | |
67 | resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) | |
68 | ||
69 | if protoResp.Provisioner == nil { | |
70 | resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provisioner schema")) | |
71 | return resp | |
72 | } | |
73 | ||
74 | resp.Provisioner = convert.ProtoToConfigSchema(protoResp.Provisioner.Block) | |
75 | ||
76 | p.schema = resp.Provisioner | |
77 | ||
78 | return resp | |
79 | } | |
80 | ||
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) | |
85 | return resp | |
86 | } | |
87 | ||
88 | mp, err := msgpack.Marshal(r.Config, schema.Provisioner.ImpliedType()) | |
89 | if err != nil { | |
90 | resp.Diagnostics = resp.Diagnostics.Append(err) | |
91 | return resp | |
92 | } | |
93 | ||
94 | protoReq := &proto.ValidateProvisionerConfig_Request{ | |
95 | Config: &proto.DynamicValue{Msgpack: mp}, | |
96 | } | |
97 | protoResp, err := p.client.ValidateProvisionerConfig(p.ctx, protoReq) | |
98 | if err != nil { | |
99 | resp.Diagnostics = resp.Diagnostics.Append(err) | |
100 | return resp | |
101 | } | |
102 | resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) | |
103 | return resp | |
104 | } | |
105 | ||
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) | |
110 | return resp | |
111 | } | |
112 | ||
113 | mp, err := msgpack.Marshal(r.Config, schema.Provisioner.ImpliedType()) | |
114 | if err != nil { | |
115 | resp.Diagnostics = resp.Diagnostics.Append(err) | |
116 | return resp | |
117 | } | |
118 | ||
119 | // connection is always assumed to be a simple string map | |
120 | connMP, err := msgpack.Marshal(r.Connection, cty.Map(cty.String)) | |
121 | if err != nil { | |
122 | resp.Diagnostics = resp.Diagnostics.Append(err) | |
123 | return resp | |
124 | } | |
125 | ||
126 | protoReq := &proto.ProvisionResource_Request{ | |
127 | Config: &proto.DynamicValue{Msgpack: mp}, | |
128 | Connection: &proto.DynamicValue{Msgpack: connMP}, | |
129 | } | |
130 | ||
131 | outputClient, err := p.client.ProvisionResource(p.ctx, protoReq) | |
132 | if err != nil { | |
133 | resp.Diagnostics = resp.Diagnostics.Append(err) | |
134 | return resp | |
135 | } | |
136 | ||
137 | for { | |
138 | rcv, err := outputClient.Recv() | |
139 | if rcv != nil { | |
140 | r.UIOutput.Output(rcv.Output) | |
141 | } | |
142 | if err != nil { | |
143 | if err != io.EOF { | |
144 | resp.Diagnostics = resp.Diagnostics.Append(err) | |
145 | } | |
146 | break | |
147 | } | |
148 | ||
149 | if len(rcv.Diagnostics) > 0 { | |
150 | resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(rcv.Diagnostics)) | |
151 | break | |
152 | } | |
153 | } | |
154 | ||
155 | return resp | |
156 | } | |
157 | ||
158 | func (p *GRPCProvisioner) Stop() error { | |
159 | protoResp, err := p.client.Stop(p.ctx, &proto.Stop_Request{}) | |
160 | if err != nil { | |
161 | return err | |
162 | } | |
163 | if protoResp.Error != "" { | |
164 | return errors.New(protoResp.Error) | |
165 | } | |
166 | return nil | |
167 | } | |
168 | ||
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") | |
173 | return nil | |
174 | } | |
175 | ||
176 | p.PluginClient.Kill() | |
177 | return nil | |
178 | } |