github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/grpcwrap/provisioner.go (about) 1 package grpcwrap 2 3 import ( 4 "context" 5 "log" 6 "strings" 7 "unicode/utf8" 8 9 "github.com/eliastor/durgaform/internal/communicator/shared" 10 "github.com/eliastor/durgaform/internal/configs/configschema" 11 "github.com/eliastor/durgaform/internal/plugin/convert" 12 "github.com/eliastor/durgaform/internal/provisioners" 13 "github.com/eliastor/durgaform/internal/tfplugin5" 14 ) 15 16 // New wraps a provisioners.Interface to implement a grpc ProviderServer. 17 // This is useful for creating a test binary out of an internal provider 18 // implementation. 19 func Provisioner(p provisioners.Interface) tfplugin5.ProvisionerServer { 20 return &provisioner{ 21 provisioner: p, 22 schema: p.GetSchema().Provisioner, 23 } 24 } 25 26 type provisioner struct { 27 provisioner provisioners.Interface 28 schema *configschema.Block 29 } 30 31 func (p *provisioner) GetSchema(_ context.Context, req *tfplugin5.GetProvisionerSchema_Request) (*tfplugin5.GetProvisionerSchema_Response, error) { 32 resp := &tfplugin5.GetProvisionerSchema_Response{} 33 34 resp.Provisioner = &tfplugin5.Schema{ 35 Block: &tfplugin5.Schema_Block{}, 36 } 37 38 if p.schema != nil { 39 resp.Provisioner.Block = convert.ConfigSchemaToProto(p.schema) 40 } 41 42 return resp, nil 43 } 44 45 func (p *provisioner) ValidateProvisionerConfig(_ context.Context, req *tfplugin5.ValidateProvisionerConfig_Request) (*tfplugin5.ValidateProvisionerConfig_Response, error) { 46 resp := &tfplugin5.ValidateProvisionerConfig_Response{} 47 ty := p.schema.ImpliedType() 48 49 configVal, err := decodeDynamicValue(req.Config, ty) 50 if err != nil { 51 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 52 return resp, nil 53 } 54 55 validateResp := p.provisioner.ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ 56 Config: configVal, 57 }) 58 59 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics) 60 return resp, nil 61 } 62 63 func (p *provisioner) ProvisionResource(req *tfplugin5.ProvisionResource_Request, srv tfplugin5.Provisioner_ProvisionResourceServer) error { 64 // We send back a diagnostics over the stream if there was a 65 // provisioner-side problem. 66 srvResp := &tfplugin5.ProvisionResource_Response{} 67 68 ty := p.schema.ImpliedType() 69 configVal, err := decodeDynamicValue(req.Config, ty) 70 if err != nil { 71 srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err) 72 srv.Send(srvResp) 73 return nil 74 } 75 76 connVal, err := decodeDynamicValue(req.Connection, shared.ConnectionBlockSupersetSchema.ImpliedType()) 77 if err != nil { 78 srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err) 79 srv.Send(srvResp) 80 return nil 81 } 82 83 resp := p.provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{ 84 Config: configVal, 85 Connection: connVal, 86 UIOutput: uiOutput{srv}, 87 }) 88 89 srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, resp.Diagnostics) 90 srv.Send(srvResp) 91 return nil 92 } 93 94 func (p *provisioner) Stop(context.Context, *tfplugin5.Stop_Request) (*tfplugin5.Stop_Response, error) { 95 resp := &tfplugin5.Stop_Response{} 96 err := p.provisioner.Stop() 97 if err != nil { 98 resp.Error = err.Error() 99 } 100 return resp, nil 101 } 102 103 // uiOutput implements the durgaform.UIOutput interface to adapt the grpc 104 // stream to the legacy Provisioner.Apply method. 105 type uiOutput struct { 106 srv tfplugin5.Provisioner_ProvisionResourceServer 107 } 108 109 func (o uiOutput) Output(s string) { 110 err := o.srv.Send(&tfplugin5.ProvisionResource_Response{ 111 Output: strings.ToValidUTF8(s, string(utf8.RuneError)), 112 }) 113 if err != nil { 114 log.Printf("[ERROR] %s", err) 115 } 116 }