Skip to content

Commit

Permalink
Introduce autofix API
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 committed May 6, 2023
1 parent b8f431f commit 4f23c16
Show file tree
Hide file tree
Showing 16 changed files with 1,732 additions and 472 deletions.
12 changes: 12 additions & 0 deletions helper/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,18 @@ func (r *Runner) EmitIssue(rule tflint.Rule, message string, location hcl.Range)
return nil
}

func (r *Runner) EmitIssueWithFix(rule tflint.Rule, message string, location hcl.Range, fixFunc func(f *tflint.Fixer) error) error {
return r.EmitIssue(rule, message, location)
}

func (r *Runner) HasChanges() bool {
return false
}

func (r *Runner) ApplyChanges() error {
return nil
}

// EnsureNoError is a method that simply runs a function if there is no error.
//
// Deprecated: Use EvaluateExpr with a function callback. e.g. EvaluateExpr(expr, func (val T) error {}, ...)
Expand Down
1 change: 1 addition & 0 deletions plugin/fromproto/fromproto.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ func Config(config *proto.ApplyGlobalConfig_Config) *tflint.Config {
Rules: rules,
DisabledByDefault: config.DisabledByDefault,
Only: config.Only,
Fix: config.Fix,
}
}

Expand Down
6 changes: 5 additions & 1 deletion plugin/host2plugin/host2plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,11 @@ func (s *mockServer) EvaluateExpr(expr hcl.Expression, opts tflint.EvaluateExprO
return cty.Value{}, nil
}

func (s *mockServer) EmitIssue(rule tflint.Rule, message string, location hcl.Range) error {
func (s *mockServer) EmitIssue(rule tflint.Rule, message string, location hcl.Range, fixable bool) (bool, error) {
return true, nil
}

func (s *mockServer) ApplyChanges(sources map[string][]byte) error {
return nil
}

Expand Down
8 changes: 7 additions & 1 deletion plugin/host2plugin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,13 @@ func (s *GRPCServer) Check(ctx context.Context, req *proto.Check_Request) (*prot
}
defer conn.Close()

runner, err := s.impl.NewRunner(&plugin2host.GRPCClient{Client: proto.NewRunnerClient(conn)})
client := proto.NewRunnerClient(conn)
resp, err := client.GetFiles(ctx, &proto.GetFiles_Request{})
if err != nil {
return nil, toproto.Error(codes.FailedPrecondition, err)
}

runner, err := s.impl.NewRunner(&plugin2host.GRPCClient{Client: client, Fixer: tflint.NewFixer(resp.Files), FixEnabled: s.impl.FixEnabled()})
if err != nil {
return nil, toproto.Error(codes.FailedPrecondition, err)
}
Expand Down
29 changes: 28 additions & 1 deletion plugin/plugin2host/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import (

// GRPCClient is a plugin-side implementation. Plugin can send requests through the client to host's gRPC server.
type GRPCClient struct {
Client proto.RunnerClient
Client proto.RunnerClient
Fixer *tflint.Fixer
FixEnabled bool
}

var _ tflint.Runner = &GRPCClient{}
Expand Down Expand Up @@ -423,6 +425,31 @@ func (c *GRPCClient) EmitIssue(rule tflint.Rule, message string, location hcl.Ra
return nil
}

func (c *GRPCClient) EmitIssueWithFix(rule tflint.Rule, message string, location hcl.Range, fixFunc func(f *tflint.Fixer) error) error {
resp, err := c.Client.EmitIssue(context.Background(), &proto.EmitIssue_Request{Rule: toproto.Rule(rule), Message: message, Range: toproto.Range(location), Fixable: true})
if err != nil {
return fromproto.Error(err)
}

if c.FixEnabled && resp.Applied {
return fixFunc(c.Fixer)
}
return nil
}

func (c *GRPCClient) HasChanges() bool {
return c.Fixer.HasChanges()
}

func (c *GRPCClient) ApplyChanges() error {
_, err := c.Client.ApplyChanges(context.Background(), &proto.ApplyChanges_Request{Changes: c.Fixer.Changes()})
if err != nil {
return fromproto.Error(err)
}
c.Fixer.ApplyChanges()
return nil
}

// EnsureNoError is a helper for error handling. Depending on the type of error generated by EvaluateExpr,
// determine whether to exit, skip, or continue. If it is continued, the passed function will be executed.
//
Expand Down
32 changes: 18 additions & 14 deletions plugin/plugin2host/plugin2host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type mockServerImpl struct {
getFiles func() map[string][]byte
getRuleConfigContent func(string, *hclext.BodySchema) (*hclext.BodyContent, map[string][]byte, error)
evaluateExpr func(hcl.Expression, tflint.EvaluateExprOption) (cty.Value, error)
emitIssue func(tflint.Rule, string, hcl.Range) error
emitIssue func(tflint.Rule, string, hcl.Range, bool) (bool, error)
}

func newMockServer(impl mockServerImpl) *mockServer {
Expand Down Expand Up @@ -99,10 +99,14 @@ func (s *mockServer) EvaluateExpr(expr hcl.Expression, opts tflint.EvaluateExprO
return cty.Value{}, nil
}

func (s *mockServer) EmitIssue(rule tflint.Rule, message string, location hcl.Range) error {
func (s *mockServer) EmitIssue(rule tflint.Rule, message string, location hcl.Range, fixable bool) (bool, error) {
if s.impl.emitIssue != nil {
return s.impl.emitIssue(rule, message, location)
return s.impl.emitIssue(rule, message, location, fixable)
}
return true, nil
}

func (s *mockServer) ApplyChanges(sources map[string][]byte) error {
return nil
}

Expand Down Expand Up @@ -2359,39 +2363,39 @@ func TestEmitIssue(t *testing.T) {
tests := []struct {
Name string
Args func() (tflint.Rule, string, hcl.Range)
ServerImpl func(tflint.Rule, string, hcl.Range) error
ServerImpl func(tflint.Rule, string, hcl.Range, bool) (bool, error)
ErrCheck func(error) bool
}{
{
Name: "emit issue",
Args: func() (tflint.Rule, string, hcl.Range) {
return &Rule{}, "this is test", hcl.Range{Filename: "test.tf", Start: hcl.Pos{Line: 2, Column: 2}, End: hcl.Pos{Line: 2, Column: 10}}
},
ServerImpl: func(rule tflint.Rule, message string, location hcl.Range) error {
ServerImpl: func(rule tflint.Rule, message string, location hcl.Range, fixable bool) (bool, error) {
if rule.Name() != "test_rule" {
return fmt.Errorf("rule.Name() should be test_rule, but %s", rule.Name())
return false, fmt.Errorf("rule.Name() should be test_rule, but %s", rule.Name())
}
if rule.Enabled() != true {
return fmt.Errorf("rule.Enabled() should be true, but %t", rule.Enabled())
return false, fmt.Errorf("rule.Enabled() should be true, but %t", rule.Enabled())
}
if rule.Severity() != tflint.ERROR {
return fmt.Errorf("rule.Severity() should be ERROR, but %s", rule.Severity())
return false, fmt.Errorf("rule.Severity() should be ERROR, but %s", rule.Severity())
}
if rule.Link() != "https://example.com" {
return fmt.Errorf("rule.Link() should be https://example.com, but %s", rule.Link())
return false, fmt.Errorf("rule.Link() should be https://example.com, but %s", rule.Link())
}
if message != "this is test" {
return fmt.Errorf("message should be `this is test`, but %s", message)
return false, fmt.Errorf("message should be `this is test`, but %s", message)
}
want := hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 2, Column: 2},
End: hcl.Pos{Line: 2, Column: 10},
}
if diff := cmp.Diff(location, want); diff != "" {
return fmt.Errorf("diff: %s", diff)
return false, fmt.Errorf("diff: %s", diff)
}
return nil
return true, nil
},
ErrCheck: neverHappend,
},
Expand All @@ -2400,8 +2404,8 @@ func TestEmitIssue(t *testing.T) {
Args: func() (tflint.Rule, string, hcl.Range) {
return &Rule{}, "this is test", hcl.Range{Filename: "test.tf", Start: hcl.Pos{Line: 2, Column: 2}, End: hcl.Pos{Line: 2, Column: 10}}
},
ServerImpl: func(tflint.Rule, string, hcl.Range) error {
return errors.New("unexpected error")
ServerImpl: func(tflint.Rule, string, hcl.Range, bool) (bool, error) {
return false, errors.New("unexpected error")
},
ErrCheck: func(err error) bool {
return err == nil || err.Error() != "unexpected error"
Expand Down
15 changes: 12 additions & 3 deletions plugin/plugin2host/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ type Server interface {
GetFiles(tflint.ModuleCtxType) map[string][]byte
GetRuleConfigContent(string, *hclext.BodySchema) (*hclext.BodyContent, map[string][]byte, error)
EvaluateExpr(hcl.Expression, tflint.EvaluateExprOption) (cty.Value, error)
EmitIssue(rule tflint.Rule, message string, location hcl.Range) error
EmitIssue(rule tflint.Rule, message string, location hcl.Range, fixable bool) (bool, error)
ApplyChanges(map[string][]byte) error
}

// GetOriginalwd gets the original working directory.
Expand Down Expand Up @@ -172,9 +173,17 @@ func (s *GRPCServer) EmitIssue(ctx context.Context, req *proto.EmitIssue_Request
return nil, status.Error(codes.InvalidArgument, "range should not be null")
}

err := s.Impl.EmitIssue(fromproto.Rule(req.Rule), req.Message, fromproto.Range(req.Range))
applied, err := s.Impl.EmitIssue(fromproto.Rule(req.Rule), req.Message, fromproto.Range(req.Range), req.Fixable)
if err != nil {
return nil, toproto.Error(codes.FailedPrecondition, err)
}
return &proto.EmitIssue_Response{}, nil
return &proto.EmitIssue_Response{Applied: applied}, nil
}

func (s *GRPCServer) ApplyChanges(ctx context.Context, req *proto.ApplyChanges_Request) (*proto.ApplyChanges_Response, error) {
err := s.Impl.ApplyChanges(req.Changes)
if err != nil {
return nil, toproto.Error(codes.InvalidArgument, err)
}
return &proto.ApplyChanges_Response{}, nil
}
Loading

0 comments on commit 4f23c16

Please sign in to comment.