Skip to content

Commit

Permalink
Support text-based expression sending and receiving on the server
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 committed Jun 13, 2020
1 parent 74f42bc commit 1fb380a
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 88 deletions.
2 changes: 1 addition & 1 deletion cmd/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (cli *CLI) inspect(opts Options, dir string, filterFiles []string) int {
cli.formatter.Print(tflint.Issues{}, tflint.NewContextError("Failed to apply config to plugins", err), cli.loader.Sources())
}
for _, runner := range runners {
err = ruleset.Check(tfplugin.NewServer(runner))
err = ruleset.Check(tfplugin.NewServer(runner, cli.loader.Sources()))
if err != nil {
cli.formatter.Print(tflint.Issues{}, tflint.NewContextError("Failed to check ruleset", err), cli.loader.Sources())
return ExitCodeError
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ require (
github.com/sourcegraph/go-lsp v0.0.0-20181119182933-0c7d621186c1
github.com/sourcegraph/jsonrpc2 v0.0.0-20190106185902-35a74f039c6a
github.com/spf13/afero v1.2.2
github.com/terraform-linters/tflint-plugin-sdk v0.1.1
github.com/terraform-linters/tflint-plugin-sdk v0.1.2-0.20200613135440-8f8f107dab08
github.com/zclconf/go-cty v1.4.2
)
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws=
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=
github.com/hashicorp/hcl/v2 v2.3.0 h1:iRly8YaMwTBAKhn1Ybk7VSdzbnopghktCD031P8ggUE=
github.com/hashicorp/hcl/v2 v2.3.0/go.mod h1:d+FwDBbOLvpAM3Z6J7gPj/VoAGkNe/gm352ZhjJ/Zv8=
github.com/hashicorp/hcl/v2 v2.5.1 h1:5ytFZykUu2/4U59ogd2f+XZdi9+6oC/Tv5WzsH6fIDA=
github.com/hashicorp/hcl/v2 v2.5.1/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
Expand Down Expand Up @@ -403,8 +402,8 @@ github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d h1:Z4EH+5Effv
github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=
github.com/terraform-linters/tflint-plugin-sdk v0.1.1 h1:5Y5FKoVWcmla//mkwz8E0k/bTcw86HMqDOMS8zDlGWY=
github.com/terraform-linters/tflint-plugin-sdk v0.1.1/go.mod h1:MW/OviPYdTTq/aqzGkQW5vQ/zLF3F/LSrWTCTHFyt0o=
github.com/terraform-linters/tflint-plugin-sdk v0.1.2-0.20200613135440-8f8f107dab08 h1:dUDhTQ3HFFIAV8/KecDj7z85BPlnN+FHf4fhhBk7l4U=
github.com/terraform-linters/tflint-plugin-sdk v0.1.2-0.20200613135440-8f8f107dab08/go.mod h1:MW/OviPYdTTq/aqzGkQW5vQ/zLF3F/LSrWTCTHFyt0o=
github.com/terraform-providers/terraform-provider-openstack v1.15.0 h1:adpjqej+F8BAX9dHmuPF47sUIkgifeqBu6p7iCsyj0Y=
github.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
Expand Down
2 changes: 1 addition & 1 deletion langserver/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (h *handler) inspect() (map[string][]lsp.Diagnostic, error) {
return ret, fmt.Errorf("Failed to apply config to plugins: %s", err)
}
for _, runner := range runners {
err = ruleset.Check(tfplugin.NewServer(runner))
err = ruleset.Check(tfplugin.NewServer(runner, loader.Sources()))
if err != nil {
return ret, fmt.Errorf("Failed to check ruleset: %s", err)
}
Expand Down
31 changes: 24 additions & 7 deletions plugin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,26 @@ import (

// Server is a RPC server for responding to requests from plugins
type Server struct {
runner *tflint.Runner
runner *tflint.Runner
sources map[string][]byte
}

// NewServer initializes a RPC server for plugins
func NewServer(runner *tflint.Runner) *Server {
return &Server{runner: runner}
func NewServer(runner *tflint.Runner, sources map[string][]byte) *Server {
return &Server{runner: runner, sources: sources}
}

// Attributes returns corresponding hcl.Attributes
func (s *Server) Attributes(req *tfplugin.AttributesRequest, resp *tfplugin.AttributesResponse) error {
ret := []*hcl.Attribute{}
ret := []*tfplugin.Attribute{}
err := s.runner.WalkResourceAttributes(req.Resource, req.AttributeName, func(attr *hcl.Attribute) error {
ret = append(ret, attr)
ret = append(ret, &tfplugin.Attribute{
Name: attr.Name,
Expr: attr.Expr.Range().SliceBytes(s.sources[attr.Expr.Range().Filename]),
ExprRange: attr.Expr.Range(),
Range: attr.Range,
NameRange: attr.NameRange,
})
return nil
})
*resp = tfplugin.AttributesResponse{Attributes: ret, Err: err}
Expand All @@ -30,7 +37,12 @@ func (s *Server) Attributes(req *tfplugin.AttributesRequest, resp *tfplugin.Attr

// EvalExpr returns a value of the evaluated expression
func (s *Server) EvalExpr(req *tfplugin.EvalExprRequest, resp *tfplugin.EvalExprResponse) error {
val, err := s.runner.EvalExpr(req.Expr, req.Ret, cty.Type{})
expr, diags := tflint.ParseExpression(req.Expr, req.ExprRange.Filename, req.ExprRange.Start)
if diags.HasErrors() {
return diags
}

val, err := s.runner.EvalExpr(expr, req.Ret, cty.Type{})
if err != nil {
if appErr, ok := err.(*tflint.Error); ok {
err = tfplugin.Error(*appErr)
Expand All @@ -42,7 +54,12 @@ func (s *Server) EvalExpr(req *tfplugin.EvalExprRequest, resp *tfplugin.EvalExpr

// EmitIssue reflects a issue to the Runner
func (s *Server) EmitIssue(req *tfplugin.EmitIssueRequest, resp *interface{}) error {
s.runner.WithExpressionContext(req.Meta.Expr, func() error {
expr, diags := tflint.ParseExpression(req.Expr, req.ExprRange.Filename, req.ExprRange.Start)
if diags.HasErrors() {
return diags
}

s.runner.WithExpressionContext(expr, func() error {
s.runner.EmitIssue(req.Rule, req.Message, req.Location)
return nil
})
Expand Down
69 changes: 30 additions & 39 deletions plugin/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
hcl "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
tfplugin "github.com/terraform-linters/tflint-plugin-sdk/tflint"
"github.com/terraform-linters/tflint/tflint"
"github.com/zclconf/go-cty/cty"
Expand All @@ -18,7 +17,7 @@ resource "aws_instance" "foo" {
instance_type = "t2.micro"
}`

server := NewServer(tflint.TestRunner(t, map[string]string{"main.tf": source}))
server := NewServer(tflint.TestRunner(t, map[string]string{"main.tf": source}), map[string][]byte{"main.tf": []byte(source)})
req := &tfplugin.AttributesRequest{
Resource: "aws_instance",
AttributeName: "instance_type",
Expand All @@ -33,25 +32,14 @@ resource "aws_instance" "foo" {
if resp.Err != nil {
t.Fatalf("The response has an unexpected error: %s", resp.Err)
}
expected := []*hcl.Attribute{
expected := []*tfplugin.Attribute{
{
Name: "instance_type",
Expr: &hclsyntax.TemplateExpr{
Parts: []hclsyntax.Expression{
&hclsyntax.LiteralValueExpr{
Val: cty.StringVal("t2.micro"),
SrcRange: hcl.Range{
Filename: "main.tf",
Start: hcl.Pos{Line: 3, Column: 20},
End: hcl.Pos{Line: 3, Column: 28},
},
},
},
SrcRange: hcl.Range{
Filename: "main.tf",
Start: hcl.Pos{Line: 3, Column: 19},
End: hcl.Pos{Line: 3, Column: 29},
},
Expr: []byte(`"t2.micro"`),
ExprRange: hcl.Range{
Filename: "main.tf",
Start: hcl.Pos{Line: 3, Column: 19},
End: hcl.Pos{Line: 3, Column: 29},
},
Range: hcl.Range{
Filename: "main.tf",
Expand All @@ -65,12 +53,9 @@ resource "aws_instance" "foo" {
},
},
}
opts := []cmp.Option{
cmpopts.IgnoreUnexported(cty.Type{}, cty.Value{}),
cmpopts.IgnoreFields(hcl.Pos{}, "Byte"),
}
if !cmp.Equal(expected, resp.Attributes, opts...) {
t.Fatalf("Attributes are not matched: %s", cmp.Diff(expected, resp.Attributes, opts...))
opt := cmpopts.IgnoreFields(hcl.Pos{}, "Byte")
if !cmp.Equal(expected, resp.Attributes, opt) {
t.Fatalf("Attributes are not matched: %s", cmp.Diff(expected, resp.Attributes, opt))
}
}

Expand All @@ -80,13 +65,13 @@ variable "instance_type" {
default = "t2.micro"
}`

server := NewServer(tflint.TestRunner(t, map[string]string{"main.tf": source}))
server := NewServer(tflint.TestRunner(t, map[string]string{"main.tf": source}), map[string][]byte{"main.tf": []byte(source)})
req := &tfplugin.EvalExprRequest{
Expr: &hclsyntax.ScopeTraversalExpr{
Traversal: hcl.Traversal{
hcl.TraverseRoot{Name: "var"},
hcl.TraverseAttr{Name: "instance_type"},
},
Expr: []byte(`var.instance_type`),
ExprRange: hcl.Range{
Filename: "template.tf",
Start: hcl.Pos{Line: 1, Column: 1},
End: hcl.Pos{Line: 1, Column: 1},
},
Ret: "", // string value
}
Expand All @@ -113,13 +98,13 @@ variable "instance_type" {
func Test_EvalExpr_errors(t *testing.T) {
source := `variable "instance_type" {}`

server := NewServer(tflint.TestRunner(t, map[string]string{"main.tf": source}))
server := NewServer(tflint.TestRunner(t, map[string]string{"main.tf": source}), map[string][]byte{"main.tf": []byte(source)})
req := &tfplugin.EvalExprRequest{
Expr: &hclsyntax.ScopeTraversalExpr{
Traversal: hcl.Traversal{
hcl.TraverseRoot{Name: "var"},
hcl.TraverseAttr{Name: "instance_type"},
},
Expr: []byte(`var.instance_type`),
ExprRange: hcl.Range{
Filename: "template.tf",
Start: hcl.Pos{Line: 1, Column: 1},
End: hcl.Pos{Line: 1, Column: 1},
},
Ret: "", // string value
}
Expand All @@ -133,7 +118,7 @@ func Test_EvalExpr_errors(t *testing.T) {
expected := tfplugin.Error{
Code: tfplugin.UnknownValueError,
Level: tfplugin.WarningLevel,
Message: "Unknown value found in :0; Please use environment variables or tfvars to set the value",
Message: "Unknown value found in template.tf:1; Please use environment variables or tfvars to set the value",
Cause: nil,
}
if !cmp.Equal(expected, resp.Err) {
Expand All @@ -150,7 +135,7 @@ func Test_EmitIssue(t *testing.T) {
},
}

server := NewServer(runner)
server := NewServer(runner, map[string][]byte{})
req := &tfplugin.EmitIssueRequest{
Rule: rule,
Message: "This is test rule",
Expand All @@ -159,6 +144,12 @@ func Test_EmitIssue(t *testing.T) {
Start: hcl.Pos{Line: 3, Column: 3},
End: hcl.Pos{Line: 3, Column: 30},
},
Expr: []byte("1"),
ExprRange: hcl.Range{
Filename: "template.tf",
Start: hcl.Pos{Line: 1, Column: 1},
End: hcl.Pos{Line: 1, Column: 1},
},
}
var resp interface{}

Expand Down
21 changes: 21 additions & 0 deletions tflint/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"path/filepath"
"strings"

hcl "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/terraform"
"github.com/zclconf/go-cty/cty"
Expand Down Expand Up @@ -49,6 +51,25 @@ func ParseTFVariables(vars []string, declVars map[string]*configs.Variable) (ter
return variables, nil
}

// ParseExpression is a wrapper for a function that parses JSON and HCL expressions
func ParseExpression(src []byte, filename string, start hcl.Pos) (hcl.Expression, hcl.Diagnostics) {
if strings.HasSuffix(filename, ".tf") {
return hclsyntax.ParseExpression(src, filename, start)
}

if strings.HasSuffix(filename, ".tf.json") {
return nil, hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "JSON configuration syntax is not supported",
Subject: &hcl.Range{Filename: filename, Start: start, End: start},
},
}
}

panic(fmt.Sprintf("Unexpected file: %s", filename))
}

func getTFDataDir() string {
dir := os.Getenv("TF_DATA_DIR")
if dir != "" {
Expand Down
2 changes: 1 addition & 1 deletion tools/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/hashicorp/hcl/v2 v2.5.1
github.com/hashicorp/terraform-plugin-sdk v1.13.1
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516
github.com/terraform-linters/tflint-plugin-sdk v0.1.1
github.com/terraform-linters/tflint-plugin-sdk v0.1.2-0.20200613135440-8f8f107dab08
github.com/terraform-providers/terraform-provider-aws v2.65.0+incompatible
)

Expand Down
Loading

0 comments on commit 1fb380a

Please sign in to comment.