github.com/terraform-linters/tflint-plugin-sdk@v0.22.0/plugin/internal/host2plugin/client.go (about) 1 package host2plugin 2 3 import ( 4 "context" 5 "os/exec" 6 7 "github.com/hashicorp/go-plugin" 8 "github.com/hashicorp/go-version" 9 "github.com/terraform-linters/tflint-plugin-sdk/hclext" 10 "github.com/terraform-linters/tflint-plugin-sdk/logger" 11 "github.com/terraform-linters/tflint-plugin-sdk/plugin/internal/fromproto" 12 "github.com/terraform-linters/tflint-plugin-sdk/plugin/internal/interceptor" 13 "github.com/terraform-linters/tflint-plugin-sdk/plugin/internal/plugin2host" 14 "github.com/terraform-linters/tflint-plugin-sdk/plugin/internal/proto" 15 "github.com/terraform-linters/tflint-plugin-sdk/plugin/internal/toproto" 16 "github.com/terraform-linters/tflint-plugin-sdk/tflint" 17 "google.golang.org/grpc" 18 ) 19 20 // GRPCClient is a host-side implementation. Host can send requests through the client to plugin's gRPC server. 21 type GRPCClient struct { 22 broker *plugin.GRPCBroker 23 client proto.RuleSetClient 24 } 25 26 // ClientOpts is an option for initializing a Client. 27 type ClientOpts struct { 28 Cmd *exec.Cmd 29 } 30 31 // NewClient is a wrapper of plugin.NewClient. 32 func NewClient(opts *ClientOpts) *plugin.Client { 33 return plugin.NewClient(&plugin.ClientConfig{ 34 HandshakeConfig: handshakeConfig, 35 Plugins: map[string]plugin.Plugin{ 36 "ruleset": &RuleSetPlugin{}, 37 }, 38 Cmd: opts.Cmd, 39 AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, 40 Logger: logger.Logger(), 41 }) 42 } 43 44 // RuleSetName returns the name of a plugin. 45 func (c *GRPCClient) RuleSetName() (string, error) { 46 resp, err := c.client.GetName(context.Background(), &proto.GetName_Request{}) 47 if err != nil { 48 return "", fromproto.Error(err) 49 } 50 return resp.Name, nil 51 } 52 53 // RuleSetVersion returns the version of a plugin. 54 func (c *GRPCClient) RuleSetVersion() (string, error) { 55 resp, err := c.client.GetVersion(context.Background(), &proto.GetVersion_Request{}) 56 if err != nil { 57 return "", fromproto.Error(err) 58 } 59 return resp.Version, nil 60 } 61 62 // RuleNames returns the list of rule names provided by a plugin. 63 func (c *GRPCClient) RuleNames() ([]string, error) { 64 resp, err := c.client.GetRuleNames(context.Background(), &proto.GetRuleNames_Request{}) 65 if err != nil { 66 return []string{}, fromproto.Error(err) 67 } 68 return resp.Names, nil 69 } 70 71 // VersionConstraints returns constraints of TFLint versions. 72 func (c *GRPCClient) VersionConstraints() (version.Constraints, error) { 73 resp, err := c.client.GetVersionConstraint(context.Background(), &proto.GetVersionConstraint_Request{}) 74 if err != nil { 75 return nil, fromproto.Error(err) 76 } 77 78 if resp.Constraint == "" { 79 return version.Constraints{}, nil 80 } 81 return version.NewConstraint(resp.Constraint) 82 } 83 84 // SDKVersion returns the SDK version. 85 func (c *GRPCClient) SDKVersion() (*version.Version, error) { 86 resp, err := c.client.GetSDKVersion(context.Background(), &proto.GetSDKVersion_Request{}) 87 if err != nil { 88 return nil, fromproto.Error(err) 89 } 90 return version.NewVersion(resp.Version) 91 } 92 93 // ConfigSchema fetches the config schema from a plugin. 94 func (c *GRPCClient) ConfigSchema() (*hclext.BodySchema, error) { 95 resp, err := c.client.GetConfigSchema(context.Background(), &proto.GetConfigSchema_Request{}) 96 if err != nil { 97 return nil, fromproto.Error(err) 98 } 99 return fromproto.BodySchema(resp.Schema), nil 100 } 101 102 // ApplyGlobalConfig applies a common config to a plugin. 103 func (c *GRPCClient) ApplyGlobalConfig(config *tflint.Config) error { 104 _, err := c.client.ApplyGlobalConfig(context.Background(), &proto.ApplyGlobalConfig_Request{Config: toproto.Config(config)}) 105 if err != nil { 106 return fromproto.Error(err) 107 } 108 return nil 109 } 110 111 // ApplyConfig applies the config to a plugin. 112 func (c *GRPCClient) ApplyConfig(content *hclext.BodyContent, sources map[string][]byte) error { 113 _, err := c.client.ApplyConfig(context.Background(), &proto.ApplyConfig_Request{Content: toproto.BodyContent(content, sources)}) 114 if err != nil { 115 return fromproto.Error(err) 116 } 117 return nil 118 } 119 120 // Check calls its own plugin implementation with an gRPC client that can send 121 // requests to the host process. 122 func (c *GRPCClient) Check(runner plugin2host.Server) error { 123 brokerID := c.broker.NextId() 124 logger.Debug("starting host-side gRPC server") 125 go c.broker.AcceptAndServe(brokerID, func(opts []grpc.ServerOption) *grpc.Server { 126 opts = append(opts, grpc.UnaryInterceptor(interceptor.RequestLogging("plugin2host"))) 127 server := grpc.NewServer(opts...) 128 proto.RegisterRunnerServer(server, &plugin2host.GRPCServer{Impl: runner}) 129 return server 130 }) 131 132 _, err := c.client.Check(context.Background(), &proto.Check_Request{Runner: brokerID}) 133 134 if err != nil { 135 return fromproto.Error(err) 136 } 137 return nil 138 }