Skip to content

Commit a0b477e

Browse files
authored
Merge pull request #82 from terraform-linters/add_support_for_decode_rule_config
tflint: Add support for fetching rule config
2 parents d49361e + 1caa135 commit a0b477e

File tree

5 files changed

+97
-15
lines changed

5 files changed

+97
-15
lines changed

helper/runner.go

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,25 @@ type Runner struct {
1919
Files map[string]*hcl.File
2020
Issues Issues
2121

22-
config *configs.Config
22+
tfconfig *configs.Config
23+
config Config
24+
}
25+
26+
// Config is a pseudo TFLint config file object for testing from plugins.
27+
type Config struct {
28+
Rules []RuleConfig `hcl:"rule,block"`
29+
}
30+
31+
// RuleConfig is a pseudo TFLint config file object for testing from plugins.
32+
type RuleConfig struct {
33+
Name string `hcl:"name,label"`
34+
Enabled bool `hcl:"enabled"`
35+
Body hcl.Body `hcl:",remain"`
2336
}
2437

2538
// WalkResourceAttributes visits all specified attributes from Files.
2639
func (r *Runner) WalkResourceAttributes(resourceType, attributeName string, walker func(*hcl.Attribute) error) error {
27-
for _, resource := range r.config.Module.ManagedResources {
40+
for _, resource := range r.tfconfig.Module.ManagedResources {
2841
if resource.Type != resourceType {
2942
continue
3043
}
@@ -53,7 +66,7 @@ func (r *Runner) WalkResourceAttributes(resourceType, attributeName string, walk
5366

5467
// WalkResourceBlocks visits all specified blocks from Files.
5568
func (r *Runner) WalkResourceBlocks(resourceType, blockType string, walker func(*hcl.Block) error) error {
56-
for _, resource := range r.config.Module.ManagedResources {
69+
for _, resource := range r.tfconfig.Module.ManagedResources {
5770
if resource.Type != resourceType {
5871
continue
5972
}
@@ -82,7 +95,7 @@ func (r *Runner) WalkResourceBlocks(resourceType, blockType string, walker func(
8295

8396
// WalkResources visits all specified resources from Files.
8497
func (r *Runner) WalkResources(resourceType string, walker func(*configs.Resource) error) error {
85-
for _, resource := range r.config.Module.ManagedResources {
98+
for _, resource := range r.tfconfig.Module.ManagedResources {
8699
if resource.Type != resourceType {
87100
continue
88101
}
@@ -98,7 +111,7 @@ func (r *Runner) WalkResources(resourceType string, walker func(*configs.Resourc
98111

99112
// WalkModuleCalls visits all module calls from Files.
100113
func (r *Runner) WalkModuleCalls(walker func(*configs.ModuleCall) error) error {
101-
for _, call := range r.config.Module.ModuleCalls {
114+
for _, call := range r.tfconfig.Module.ModuleCalls {
102115
err := walker(call)
103116
if err != nil {
104117
return err
@@ -110,18 +123,32 @@ func (r *Runner) WalkModuleCalls(walker func(*configs.ModuleCall) error) error {
110123

111124
// Backend returns the terraform backend configuration.
112125
func (r *Runner) Backend() (*configs.Backend, error) {
113-
return r.config.Module.Backend, nil
126+
return r.tfconfig.Module.Backend, nil
114127
}
115128

116129
// Config returns the Terraform configuration
117130
func (r *Runner) Config() (*configs.Config, error) {
118-
return r.config, nil
131+
return r.tfconfig, nil
119132
}
120133

121134
// RootProvider returns the provider configuration.
122135
// In the helper runner, it always returns its own provider.
123136
func (r *Runner) RootProvider(name string) (*configs.Provider, error) {
124-
return r.config.Module.ProviderConfigs[name], nil
137+
return r.tfconfig.Module.ProviderConfigs[name], nil
138+
}
139+
140+
// DecodeRuleConfig extracts the rule's configuration into the given value
141+
func (r *Runner) DecodeRuleConfig(name string, ret interface{}) error {
142+
for _, rule := range r.config.Rules {
143+
if rule.Name == name {
144+
if diags := gohcl.DecodeBody(rule.Body, nil, ret); diags.HasErrors() {
145+
return diags
146+
}
147+
return nil
148+
}
149+
}
150+
151+
return nil
125152
}
126153

127154
// EvaluateExpr returns a value of the passed expression.
@@ -146,7 +173,7 @@ func (r *Runner) EvaluateExpr(expr hcl.Expression, ret interface{}) error {
146173
}
147174

148175
variables := map[string]cty.Value{}
149-
for _, variable := range r.config.Module.Variables {
176+
for _, variable := range r.tfconfig.Module.Variables {
150177
variables[variable.Name] = variable.Default
151178
}
152179
rawVal, diags := expr.Value(&hcl.EvalContext{
@@ -200,7 +227,7 @@ func (r *Runner) EnsureNoError(err error, proc func() error) error {
200227
}
201228

202229
func (r *Runner) initFromFiles() error {
203-
r.config = &configs.Config{
230+
r.tfconfig = &configs.Config{
204231
Module: &configs.Module{
205232
ModuleCalls: map[string]*configs.ModuleCall{},
206233
ManagedResources: map[string]*configs.Resource{},
@@ -225,7 +252,7 @@ func (r *Runner) initFromFiles() error {
225252
for _, block := range content.Blocks {
226253
switch block.Type {
227254
case "backend":
228-
r.config.Module.Backend = &configs.Backend{
255+
r.tfconfig.Module.Backend = &configs.Backend{
229256
Type: block.Labels[0],
230257
TypeRange: block.LabelRanges[0],
231258
Config: block.Body,
@@ -246,7 +273,7 @@ func (r *Runner) initFromFiles() error {
246273
if diags.HasErrors() {
247274
return diags
248275
}
249-
r.config.Module.Variables[variable.Name] = variable
276+
r.tfconfig.Module.Variables[variable.Name] = variable
250277
case "locals":
251278
// TODO
252279
case "output":
@@ -256,13 +283,13 @@ func (r *Runner) initFromFiles() error {
256283
if diags.HasErrors() {
257284
return diags
258285
}
259-
r.config.Module.ModuleCalls[call.Name] = call
286+
r.tfconfig.Module.ModuleCalls[call.Name] = call
260287
case "resource":
261288
resource, diags := simpleDecodeResouceBlock(block)
262289
if diags.HasErrors() {
263290
return diags
264291
}
265-
r.config.Module.ManagedResources[fmt.Sprintf("%s.%s", resource.Type, resource.Name)] = resource
292+
r.tfconfig.Module.ManagedResources[fmt.Sprintf("%s.%s", resource.Type, resource.Name)] = resource
266293
case "data":
267294
// TODO
268295
default:

helper/testing.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/google/go-cmp/cmp"
99
"github.com/google/go-cmp/cmp/cmpopts"
1010
"github.com/hashicorp/hcl/v2"
11+
"github.com/hashicorp/hcl/v2/gohcl"
1112
"github.com/hashicorp/hcl/v2/hclparse"
1213
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
1314
)
@@ -23,7 +24,16 @@ func TestRunner(t *testing.T, files map[string]string) *Runner {
2324
if diags.HasErrors() {
2425
t.Fatal(diags)
2526
}
26-
runner.Files[name] = file
27+
28+
if name == ".tflint.hcl" {
29+
var config Config
30+
if diags := gohcl.DecodeBody(file.Body, nil, &config); diags.HasErrors() {
31+
t.Fatal(diags)
32+
}
33+
runner.config = config
34+
} else {
35+
runner.Files[name] = file
36+
}
2737
}
2838

2939
if err := runner.initFromFiles(); err != nil {

tflint/client/client.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"net/rpc"
99

1010
hcl "github.com/hashicorp/hcl/v2"
11+
"github.com/hashicorp/hcl/v2/gohcl"
12+
"github.com/hashicorp/hcl/v2/hclsyntax"
1113
"github.com/terraform-linters/tflint-plugin-sdk/terraform/configs"
1214
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
1315
"github.com/zclconf/go-cty/cty/gocty"
@@ -190,6 +192,33 @@ func (c *Client) RootProvider(name string) (*configs.Provider, error) {
190192
return provider, nil
191193
}
192194

195+
// DecodeRuleConfig calls the server-side RuleConfig method and reflects the response
196+
// in the passed argument.
197+
func (c *Client) DecodeRuleConfig(name string, ret interface{}) error {
198+
var response RuleConfigResponse
199+
200+
req := RuleConfigRequest{Name: name}
201+
if err := c.rpcClient.Call("Plugin.RuleConfig", req, &response); err != nil {
202+
return err
203+
}
204+
if response.Err != nil {
205+
return response.Err
206+
}
207+
208+
if !response.Exists {
209+
return nil
210+
}
211+
file, diags := hclsyntax.ParseConfig(response.Config, response.Range.Filename, response.Range.Start)
212+
if diags.HasErrors() {
213+
return diags
214+
}
215+
if diags = gohcl.DecodeBody(file.Body, nil, ret); diags.HasErrors() {
216+
return diags
217+
}
218+
219+
return nil
220+
}
221+
193222
// EvaluateExpr calls the server-side EvalExpr method and reflects the response
194223
// in the passed argument.
195224
func (c *Client) EvaluateExpr(expr hcl.Expression, ret interface{}) error {

tflint/client/rpc.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ type RootProviderResponse struct {
7878
Err error
7979
}
8080

81+
// RuleConfigRequest is a request to the server-side RuleConfig method.
82+
type RuleConfigRequest struct {
83+
Name string
84+
}
85+
86+
// RuleConfigResponse is a response to the server-side RuleConfig method.
87+
type RuleConfigResponse struct {
88+
Exists bool
89+
Config []byte
90+
Range hcl.Range
91+
Err error
92+
}
93+
8194
// EvalExprRequest is a request to the server-side EvalExpr method.
8295
type EvalExprRequest struct {
8396
Expr []byte

tflint/interface.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ type Runner interface {
5757
// It can be used by child modules to access the credentials defined in the root module.
5858
RootProvider(name string) (*configs.Provider, error)
5959

60+
// DecodeRuleConfig fetches the rule's configuration and reflects the result in ret.
61+
DecodeRuleConfig(name string, ret interface{}) error
62+
6063
// EvaluateExpr evaluates the passed expression and reflects the result in ret.
6164
// Since this function returns an application error, it is expected to use the EnsureNoError
6265
// to determine whether to continue processing.

0 commit comments

Comments
 (0)