]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/plugin/grpc_provider.go
update vendor and go.mod
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / plugin / grpc_provider.go
1 package plugin
2
3 import (
4 "context"
5 "errors"
6 "log"
7 "sync"
8
9 "github.com/zclconf/go-cty/cty"
10
11 plugin "github.com/hashicorp/go-plugin"
12 proto "github.com/hashicorp/terraform/internal/tfplugin5"
13 "github.com/hashicorp/terraform/plugin/convert"
14 "github.com/hashicorp/terraform/providers"
15 "github.com/hashicorp/terraform/version"
16 "github.com/zclconf/go-cty/cty/msgpack"
17 "google.golang.org/grpc"
18 )
19
20 // GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package.
21 type GRPCProviderPlugin struct {
22 plugin.Plugin
23 GRPCProvider func() proto.ProviderServer
24 }
25
26 func (p *GRPCProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
27 return &GRPCProvider{
28 client: proto.NewProviderClient(c),
29 ctx: ctx,
30 }, nil
31 }
32
33 func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
34 proto.RegisterProviderServer(s, p.GRPCProvider())
35 return nil
36 }
37
38 // GRPCProvider handles the client, or core side of the plugin rpc connection.
39 // The GRPCProvider methods are mostly a translation layer between the
40 // terraform provioders types and the grpc proto types, directly converting
41 // between the two.
42 type GRPCProvider struct {
43 // PluginClient provides a reference to the plugin.Client which controls the plugin process.
44 // This allows the GRPCProvider a way to shutdown the plugin process.
45 PluginClient *plugin.Client
46
47 // TestServer contains a grpc.Server to close when the GRPCProvider is being
48 // used in an end to end test of a provider.
49 TestServer *grpc.Server
50
51 // Proto client use to make the grpc service calls.
52 client proto.ProviderClient
53
54 // this context is created by the plugin package, and is canceled when the
55 // plugin process ends.
56 ctx context.Context
57
58 // schema stores the schema for this provider. This is used to properly
59 // serialize the state for requests.
60 mu sync.Mutex
61 schemas providers.GetSchemaResponse
62 }
63
64 // getSchema is used internally to get the saved provider schema. The schema
65 // should have already been fetched from the provider, but we have to
66 // synchronize access to avoid being called concurrently with GetSchema.
67 func (p *GRPCProvider) getSchema() providers.GetSchemaResponse {
68 p.mu.Lock()
69 // unlock inline in case GetSchema needs to be called
70 if p.schemas.Provider.Block != nil {
71 p.mu.Unlock()
72 return p.schemas
73 }
74 p.mu.Unlock()
75
76 // the schema should have been fetched already, but give it another shot
77 // just in case things are being called out of order. This may happen for
78 // tests.
79 schemas := p.GetSchema()
80 if schemas.Diagnostics.HasErrors() {
81 panic(schemas.Diagnostics.Err())
82 }
83
84 return schemas
85 }
86
87 // getResourceSchema is a helper to extract the schema for a resource, and
88 // panics if the schema is not available.
89 func (p *GRPCProvider) getResourceSchema(name string) providers.Schema {
90 schema := p.getSchema()
91 resSchema, ok := schema.ResourceTypes[name]
92 if !ok {
93 panic("unknown resource type " + name)
94 }
95 return resSchema
96 }
97
98 // gettDatasourceSchema is a helper to extract the schema for a datasource, and
99 // panics if that schema is not available.
100 func (p *GRPCProvider) getDatasourceSchema(name string) providers.Schema {
101 schema := p.getSchema()
102 dataSchema, ok := schema.DataSources[name]
103 if !ok {
104 panic("unknown data source " + name)
105 }
106 return dataSchema
107 }
108
109 func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) {
110 log.Printf("[TRACE] GRPCProvider: GetSchema")
111 p.mu.Lock()
112 defer p.mu.Unlock()
113
114 if p.schemas.Provider.Block != nil {
115 return p.schemas
116 }
117
118 resp.ResourceTypes = make(map[string]providers.Schema)
119 resp.DataSources = make(map[string]providers.Schema)
120
121 // Some providers may generate quite large schemas, and the internal default
122 // grpc response size limit is 4MB. 64MB should cover most any use case, and
123 // if we get providers nearing that we may want to consider a finer-grained
124 // API to fetch individual resource schemas.
125 // Note: this option is marked as EXPERIMENTAL in the grpc API.
126 const maxRecvSize = 64 << 20
127 protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize})
128 if err != nil {
129 resp.Diagnostics = resp.Diagnostics.Append(err)
130 return resp
131 }
132
133 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
134
135 if protoResp.Provider == nil {
136 resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provider schema"))
137 return resp
138 }
139
140 resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider)
141
142 for name, res := range protoResp.ResourceSchemas {
143 resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res)
144 }
145
146 for name, data := range protoResp.DataSourceSchemas {
147 resp.DataSources[name] = convert.ProtoToProviderSchema(data)
148 }
149
150 p.schemas = resp
151
152 return resp
153 }
154
155 func (p *GRPCProvider) PrepareProviderConfig(r providers.PrepareProviderConfigRequest) (resp providers.PrepareProviderConfigResponse) {
156 log.Printf("[TRACE] GRPCProvider: PrepareProviderConfig")
157
158 schema := p.getSchema()
159 ty := schema.Provider.Block.ImpliedType()
160
161 mp, err := msgpack.Marshal(r.Config, ty)
162 if err != nil {
163 resp.Diagnostics = resp.Diagnostics.Append(err)
164 return resp
165 }
166
167 protoReq := &proto.PrepareProviderConfig_Request{
168 Config: &proto.DynamicValue{Msgpack: mp},
169 }
170
171 protoResp, err := p.client.PrepareProviderConfig(p.ctx, protoReq)
172 if err != nil {
173 resp.Diagnostics = resp.Diagnostics.Append(err)
174 return resp
175 }
176
177 config := cty.NullVal(ty)
178 if protoResp.PreparedConfig != nil {
179 config, err = msgpack.Unmarshal(protoResp.PreparedConfig.Msgpack, ty)
180 if err != nil {
181 resp.Diagnostics = resp.Diagnostics.Append(err)
182 return resp
183 }
184 }
185 resp.PreparedConfig = config
186
187 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
188 return resp
189 }
190
191 func (p *GRPCProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) (resp providers.ValidateResourceTypeConfigResponse) {
192 log.Printf("[TRACE] GRPCProvider: ValidateResourceTypeConfig")
193 resourceSchema := p.getResourceSchema(r.TypeName)
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(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) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) (resp providers.ValidateDataSourceConfigResponse) {
217 log.Printf("[TRACE] GRPCProvider: ValidateDataSourceConfig")
218
219 dataSchema := p.getDatasourceSchema(r.TypeName)
220
221 mp, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
222 if err != nil {
223 resp.Diagnostics = resp.Diagnostics.Append(err)
224 return resp
225 }
226
227 protoReq := &proto.ValidateDataSourceConfig_Request{
228 TypeName: r.TypeName,
229 Config: &proto.DynamicValue{Msgpack: mp},
230 }
231
232 protoResp, err := p.client.ValidateDataSourceConfig(p.ctx, protoReq)
233 if err != nil {
234 resp.Diagnostics = resp.Diagnostics.Append(err)
235 return resp
236 }
237 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
238 return resp
239 }
240
241 func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) {
242 log.Printf("[TRACE] GRPCProvider: UpgradeResourceState")
243
244 resSchema := p.getResourceSchema(r.TypeName)
245
246 protoReq := &proto.UpgradeResourceState_Request{
247 TypeName: r.TypeName,
248 Version: int64(r.Version),
249 RawState: &proto.RawState{
250 Json: r.RawStateJSON,
251 Flatmap: r.RawStateFlatmap,
252 },
253 }
254
255 protoResp, err := p.client.UpgradeResourceState(p.ctx, protoReq)
256 if err != nil {
257 resp.Diagnostics = resp.Diagnostics.Append(err)
258 return resp
259 }
260 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
261
262 state := cty.NullVal(resSchema.Block.ImpliedType())
263 if protoResp.UpgradedState != nil {
264 state, err = msgpack.Unmarshal(protoResp.UpgradedState.Msgpack, resSchema.Block.ImpliedType())
265 if err != nil {
266 resp.Diagnostics = resp.Diagnostics.Append(err)
267 return resp
268 }
269 }
270
271 resp.UpgradedState = state
272 return resp
273 }
274
275 func (p *GRPCProvider) Configure(r providers.ConfigureRequest) (resp providers.ConfigureResponse) {
276 log.Printf("[TRACE] GRPCProvider: Configure")
277
278 schema := p.getSchema()
279
280 var mp []byte
281
282 // we don't have anything to marshal if there's no config
283 mp, err := msgpack.Marshal(r.Config, schema.Provider.Block.ImpliedType())
284 if err != nil {
285 resp.Diagnostics = resp.Diagnostics.Append(err)
286 return resp
287 }
288
289 protoReq := &proto.Configure_Request{
290 TerraformVersion: version.Version,
291 Config: &proto.DynamicValue{
292 Msgpack: mp,
293 },
294 }
295
296 protoResp, err := p.client.Configure(p.ctx, protoReq)
297 if err != nil {
298 resp.Diagnostics = resp.Diagnostics.Append(err)
299 return resp
300 }
301 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
302 return resp
303 }
304
305 func (p *GRPCProvider) Stop() error {
306 log.Printf("[TRACE] GRPCProvider: Stop")
307
308 resp, err := p.client.Stop(p.ctx, new(proto.Stop_Request))
309 if err != nil {
310 return err
311 }
312
313 if resp.Error != "" {
314 return errors.New(resp.Error)
315 }
316 return nil
317 }
318
319 func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
320 log.Printf("[TRACE] GRPCProvider: ReadResource")
321
322 resSchema := p.getResourceSchema(r.TypeName)
323
324 mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
325 if err != nil {
326 resp.Diagnostics = resp.Diagnostics.Append(err)
327 return resp
328 }
329
330 protoReq := &proto.ReadResource_Request{
331 TypeName: r.TypeName,
332 CurrentState: &proto.DynamicValue{Msgpack: mp},
333 Private: r.Private,
334 }
335
336 protoResp, err := p.client.ReadResource(p.ctx, protoReq)
337 if err != nil {
338 resp.Diagnostics = resp.Diagnostics.Append(err)
339 return resp
340 }
341 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
342
343 state := cty.NullVal(resSchema.Block.ImpliedType())
344 if protoResp.NewState != nil {
345 state, err = msgpack.Unmarshal(protoResp.NewState.Msgpack, resSchema.Block.ImpliedType())
346 if err != nil {
347 resp.Diagnostics = resp.Diagnostics.Append(err)
348 return resp
349 }
350 }
351 resp.NewState = state
352 resp.Private = protoResp.Private
353
354 return resp
355 }
356
357 func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
358 log.Printf("[TRACE] GRPCProvider: PlanResourceChange")
359
360 resSchema := p.getResourceSchema(r.TypeName)
361
362 priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
363 if err != nil {
364 resp.Diagnostics = resp.Diagnostics.Append(err)
365 return resp
366 }
367
368 configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType())
369 if err != nil {
370 resp.Diagnostics = resp.Diagnostics.Append(err)
371 return resp
372 }
373
374 propMP, err := msgpack.Marshal(r.ProposedNewState, resSchema.Block.ImpliedType())
375 if err != nil {
376 resp.Diagnostics = resp.Diagnostics.Append(err)
377 return resp
378 }
379
380 protoReq := &proto.PlanResourceChange_Request{
381 TypeName: r.TypeName,
382 PriorState: &proto.DynamicValue{Msgpack: priorMP},
383 Config: &proto.DynamicValue{Msgpack: configMP},
384 ProposedNewState: &proto.DynamicValue{Msgpack: propMP},
385 PriorPrivate: r.PriorPrivate,
386 }
387
388 protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq)
389 if err != nil {
390 resp.Diagnostics = resp.Diagnostics.Append(err)
391 return resp
392 }
393 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
394
395 state := cty.NullVal(resSchema.Block.ImpliedType())
396 if protoResp.PlannedState != nil {
397 state, err = msgpack.Unmarshal(protoResp.PlannedState.Msgpack, resSchema.Block.ImpliedType())
398 if err != nil {
399 resp.Diagnostics = resp.Diagnostics.Append(err)
400 return resp
401 }
402 }
403 resp.PlannedState = state
404
405 for _, p := range protoResp.RequiresReplace {
406 resp.RequiresReplace = append(resp.RequiresReplace, convert.AttributePathToPath(p))
407 }
408
409 resp.PlannedPrivate = protoResp.PlannedPrivate
410
411 resp.LegacyTypeSystem = protoResp.LegacyTypeSystem
412
413 return resp
414 }
415
416 func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
417 log.Printf("[TRACE] GRPCProvider: ApplyResourceChange")
418
419 resSchema := p.getResourceSchema(r.TypeName)
420
421 priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
422 if err != nil {
423 resp.Diagnostics = resp.Diagnostics.Append(err)
424 return resp
425 }
426 plannedMP, err := msgpack.Marshal(r.PlannedState, resSchema.Block.ImpliedType())
427 if err != nil {
428 resp.Diagnostics = resp.Diagnostics.Append(err)
429 return resp
430 }
431 configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType())
432 if err != nil {
433 resp.Diagnostics = resp.Diagnostics.Append(err)
434 return resp
435 }
436
437 protoReq := &proto.ApplyResourceChange_Request{
438 TypeName: r.TypeName,
439 PriorState: &proto.DynamicValue{Msgpack: priorMP},
440 PlannedState: &proto.DynamicValue{Msgpack: plannedMP},
441 Config: &proto.DynamicValue{Msgpack: configMP},
442 PlannedPrivate: r.PlannedPrivate,
443 }
444
445 protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq)
446 if err != nil {
447 resp.Diagnostics = resp.Diagnostics.Append(err)
448 return resp
449 }
450 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
451
452 resp.Private = protoResp.Private
453
454 state := cty.NullVal(resSchema.Block.ImpliedType())
455 if protoResp.NewState != nil {
456 state, err = msgpack.Unmarshal(protoResp.NewState.Msgpack, resSchema.Block.ImpliedType())
457 if err != nil {
458 resp.Diagnostics = resp.Diagnostics.Append(err)
459 return resp
460 }
461 }
462 resp.NewState = state
463
464 resp.LegacyTypeSystem = protoResp.LegacyTypeSystem
465
466 return resp
467 }
468
469 func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) {
470 log.Printf("[TRACE] GRPCProvider: ImportResourceState")
471
472 protoReq := &proto.ImportResourceState_Request{
473 TypeName: r.TypeName,
474 Id: r.ID,
475 }
476
477 protoResp, err := p.client.ImportResourceState(p.ctx, protoReq)
478 if err != nil {
479 resp.Diagnostics = resp.Diagnostics.Append(err)
480 return resp
481 }
482 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
483
484 for _, imported := range protoResp.ImportedResources {
485 resource := providers.ImportedResource{
486 TypeName: imported.TypeName,
487 Private: imported.Private,
488 }
489
490 resSchema := p.getResourceSchema(resource.TypeName)
491 state := cty.NullVal(resSchema.Block.ImpliedType())
492 if imported.State != nil {
493 state, err = msgpack.Unmarshal(imported.State.Msgpack, resSchema.Block.ImpliedType())
494 if err != nil {
495 resp.Diagnostics = resp.Diagnostics.Append(err)
496 return resp
497 }
498 }
499 resource.State = state
500 resp.ImportedResources = append(resp.ImportedResources, resource)
501 }
502
503 return resp
504 }
505
506 func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
507 log.Printf("[TRACE] GRPCProvider: ReadDataSource")
508
509 dataSchema := p.getDatasourceSchema(r.TypeName)
510
511 config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
512 if err != nil {
513 resp.Diagnostics = resp.Diagnostics.Append(err)
514 return resp
515 }
516
517 protoReq := &proto.ReadDataSource_Request{
518 TypeName: r.TypeName,
519 Config: &proto.DynamicValue{
520 Msgpack: config,
521 },
522 }
523
524 protoResp, err := p.client.ReadDataSource(p.ctx, protoReq)
525 if err != nil {
526 resp.Diagnostics = resp.Diagnostics.Append(err)
527 return resp
528 }
529 resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
530
531 state := cty.NullVal(dataSchema.Block.ImpliedType())
532 if protoResp.State != nil {
533 state, err = msgpack.Unmarshal(protoResp.State.Msgpack, dataSchema.Block.ImpliedType())
534 if err != nil {
535 resp.Diagnostics = resp.Diagnostics.Append(err)
536 return resp
537 }
538 }
539 resp.State = state
540
541 return resp
542 }
543
544 // closing the grpc connection is final, and terraform will call it at the end of every phase.
545 func (p *GRPCProvider) Close() error {
546 log.Printf("[TRACE] GRPCProvider: Close")
547
548 // Make sure to stop the server if we're not running within go-plugin.
549 if p.TestServer != nil {
550 p.TestServer.Stop()
551 }
552
553 // Check this since it's not automatically inserted during plugin creation.
554 // It's currently only inserted by the command package, because that is
555 // where the factory is built and is the only point with access to the
556 // plugin.Client.
557 if p.PluginClient == nil {
558 log.Println("[DEBUG] provider has no plugin.Client")
559 return nil
560 }
561
562 p.PluginClient.Kill()
563 return nil
564 }