github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/plugin/server.go (about)

     1  package plugin
     2  
     3  import (
     4  	hcl "github.com/hashicorp/hcl/v2"
     5  	"github.com/hashicorp/terraform/configs"
     6  	client "github.com/terraform-linters/tflint-plugin-sdk/tflint"
     7  	tfplugin "github.com/terraform-linters/tflint-plugin-sdk/tflint/client"
     8  	"github.com/terraform-linters/tflint/tflint"
     9  	"github.com/zclconf/go-cty/cty"
    10  )
    11  
    12  // Server is a RPC server for responding to requests from plugins
    13  type Server struct {
    14  	runner  *tflint.Runner
    15  	sources map[string][]byte
    16  }
    17  
    18  // NewServer initializes a RPC server for plugins
    19  func NewServer(runner *tflint.Runner, sources map[string][]byte) *Server {
    20  	return &Server{runner: runner, sources: sources}
    21  }
    22  
    23  // Attributes returns corresponding hcl.Attributes
    24  func (s *Server) Attributes(req *tfplugin.AttributesRequest, resp *tfplugin.AttributesResponse) error {
    25  	ret := []*tfplugin.Attribute{}
    26  	err := s.runner.WalkResourceAttributes(req.Resource, req.AttributeName, func(attr *hcl.Attribute) error {
    27  		ret = append(ret, &tfplugin.Attribute{
    28  			Name:      attr.Name,
    29  			Expr:      attr.Expr.Range().SliceBytes(s.sources[attr.Expr.Range().Filename]),
    30  			ExprRange: attr.Expr.Range(),
    31  			Range:     attr.Range,
    32  			NameRange: attr.NameRange,
    33  		})
    34  		return nil
    35  	})
    36  	*resp = tfplugin.AttributesResponse{Attributes: ret, Err: err}
    37  	return nil
    38  }
    39  
    40  // Blocks returns corresponding hcl.Blocks
    41  func (s *Server) Blocks(req *tfplugin.BlocksRequest, resp *tfplugin.BlocksResponse) error {
    42  	ret := []*tfplugin.Block{}
    43  	err := s.runner.WalkResourceBlocks(req.Resource, req.BlockType, func(block *hcl.Block) error {
    44  		bodyRange := tflint.HCLBodyRange(block.Body, block.DefRange)
    45  		ret = append(ret, &tfplugin.Block{
    46  			Type:        block.Type,
    47  			Labels:      block.Labels,
    48  			Body:        bodyRange.SliceBytes(s.runner.File(block.DefRange.Filename).Bytes),
    49  			BodyRange:   bodyRange,
    50  			DefRange:    block.DefRange,
    51  			TypeRange:   block.TypeRange,
    52  			LabelRanges: block.LabelRanges,
    53  		})
    54  		return nil
    55  	})
    56  	*resp = tfplugin.BlocksResponse{Blocks: ret, Err: err}
    57  	return nil
    58  }
    59  
    60  // Resources returns corresponding configs.Resource as tfplugin.Resource
    61  func (s *Server) Resources(req *tfplugin.ResourcesRequest, resp *tfplugin.ResourcesResponse) error {
    62  	var ret []*tfplugin.Resource
    63  	err := s.runner.WalkResources(req.Name, func(resource *configs.Resource) error {
    64  		ret = append(ret, s.encodeResource(resource))
    65  		return nil
    66  	})
    67  	*resp = tfplugin.ResourcesResponse{Resources: ret, Err: err}
    68  	return nil
    69  }
    70  
    71  // ModuleCalls returns all configs.ModuleCall as tfplugin.ModuleCall
    72  func (s *Server) ModuleCalls(req *tfplugin.ModuleCallsRequest, resp *tfplugin.ModuleCallsResponse) error {
    73  	ret := []*tfplugin.ModuleCall{}
    74  	err := s.runner.WalkModuleCalls(func(call *configs.ModuleCall) error {
    75  		ret = append(ret, s.encodeModuleCall(call))
    76  		return nil
    77  	})
    78  	*resp = tfplugin.ModuleCallsResponse{ModuleCalls: ret, Err: err}
    79  	return nil
    80  }
    81  
    82  // Backend returns corresponding configs.Backend as tfplugin.Backend
    83  func (s *Server) Backend(req *tfplugin.BackendRequest, resp *tfplugin.BackendResponse) error {
    84  	backend := s.runner.Backend()
    85  	if backend == nil {
    86  		return nil
    87  	}
    88  
    89  	*resp = tfplugin.BackendResponse{
    90  		Backend: s.encodeBackend(backend),
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  // EvalExpr returns a value of the evaluated expression
    97  func (s *Server) EvalExpr(req *tfplugin.EvalExprRequest, resp *tfplugin.EvalExprResponse) error {
    98  	expr, diags := tflint.ParseExpression(req.Expr, req.ExprRange.Filename, req.ExprRange.Start)
    99  	if diags.HasErrors() {
   100  		return diags
   101  	}
   102  
   103  	val, err := s.runner.EvalExpr(expr, req.Ret, cty.Type{})
   104  	if err != nil {
   105  		if appErr, ok := err.(*tflint.Error); ok {
   106  			err = client.Error(*appErr)
   107  		}
   108  	}
   109  	*resp = tfplugin.EvalExprResponse{Val: val, Err: err}
   110  	return nil
   111  }
   112  
   113  // EmitIssue reflects a issue to the Runner
   114  func (s *Server) EmitIssue(req *tfplugin.EmitIssueRequest, resp *interface{}) error {
   115  	if req.Expr != nil {
   116  		expr, diags := tflint.ParseExpression(req.Expr, req.ExprRange.Filename, req.ExprRange.Start)
   117  		if diags.HasErrors() {
   118  			return diags
   119  		}
   120  
   121  		s.runner.WithExpressionContext(expr, func() error {
   122  			s.runner.EmitIssue(req.Rule, req.Message, req.Location)
   123  			return nil
   124  		})
   125  	} else {
   126  		s.runner.EmitIssue(req.Rule, req.Message, req.Location)
   127  		return nil
   128  	}
   129  	return nil
   130  }