Skip to content

Commit

Permalink
plugin: Allow to declare custom attributes in config files
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 committed Nov 3, 2020
1 parent 19683cc commit 9c0fc38
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 40 deletions.
5 changes: 3 additions & 2 deletions cmd/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,11 @@ func (cli *CLI) inspect(opts Options, dir string, filterFiles []string) int {
}
}

for _, ruleset := range plugin.RuleSets {
err = ruleset.ApplyConfig(cfg.ToPluginConfig())
for name, ruleset := range plugin.RuleSets {
err = ruleset.ApplyConfig(cfg.ToPluginConfig(name))
if err != nil {
cli.formatter.Print(tflint.Issues{}, tflint.NewContextError("Failed to apply config to plugins", err), cli.loader.Sources())
return ExitCodeError
}
for _, runner := range runners {
err = ruleset.Check(tfplugin.NewServer(runner, cli.loader.Sources()))
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ 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.4.1
github.com/terraform-linters/tflint-plugin-sdk v0.5.1-0.20201024120523-69843955cc45
github.com/terraform-linters/tflint-plugin-sdk v0.5.1-0.20201031131801-2f5c943768d5
github.com/terraform-providers/terraform-provider-aws v1.60.1-0.20201015205411-546f68d4a935
github.com/zclconf/go-cty v1.7.0
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,8 @@ github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible h1:5Td2b0yfaOvw
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 h1:iRD1CqtWUjgEVEmjwTMbP1DMzz1HRytOsgx/rlw/vNs=
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=
github.com/terraform-linters/tflint-plugin-sdk v0.5.1-0.20201024120523-69843955cc45 h1:FHFTMNt+RyONj0NoDznEuMwxaE1ZKp+KjwgZiW9+6JA=
github.com/terraform-linters/tflint-plugin-sdk v0.5.1-0.20201024120523-69843955cc45/go.mod h1:Ho5IxOfE18lhc4KeXSfSxnoZWev34G2617ke+GA+Frc=
github.com/terraform-linters/tflint-plugin-sdk v0.5.1-0.20201031131801-2f5c943768d5 h1:vmcJ+Ie9avZ1gTrV5i7ikFp8l4Y43id0KWBUybELK6w=
github.com/terraform-linters/tflint-plugin-sdk v0.5.1-0.20201031131801-2f5c943768d5/go.mod h1:Ho5IxOfE18lhc4KeXSfSxnoZWev34G2617ke+GA+Frc=
github.com/terraform-providers/terraform-provider-aws v1.60.1-0.20201015205411-546f68d4a935 h1:PbobnAeVvdzE1/qqTYxaB9h/YIpHCZXbCRBaXNIi0qA=
github.com/terraform-providers/terraform-provider-aws v1.60.1-0.20201015205411-546f68d4a935/go.mod h1:DdjydHaAmjsZl+uZ4QLwfx9iP+trTBMjEqLeAV9/OFE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
Expand Down
4 changes: 2 additions & 2 deletions langserver/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ func (h *handler) inspect() (map[string][]lsp.Diagnostic, error) {
}
}

for _, ruleset := range h.plugin.RuleSets {
err = ruleset.ApplyConfig(h.config.ToPluginConfig())
for name, ruleset := range h.plugin.RuleSets {
err = ruleset.ApplyConfig(h.config.ToPluginConfig(name))
if err != nil {
return ret, fmt.Errorf("Failed to apply config to plugins: %s", err)
}
Expand Down
8 changes: 4 additions & 4 deletions plugin/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func Discovery(config *tflint.Config) (*Plugin, error) {
}

func findPlugins(config *tflint.Config, dir string) (*Plugin, error) {
clients := []*plugin.Client{}
rulesets := []*tfplugin.Client{}
clients := map[string]*plugin.Client{}
rulesets := map[string]*tfplugin.Client{}

for _, cfg := range config.Plugins {
pluginPath, err := getPluginPath(dir, cfg.Name)
Expand All @@ -68,8 +68,8 @@ func findPlugins(config *tflint.Config, dir string) (*Plugin, error) {
}
ruleset := raw.(*tfplugin.Client)

clients = append(clients, client)
rulesets = append(rulesets, ruleset)
clients[cfg.Name] = client
rulesets[cfg.Name] = ruleset
} else {
log.Printf("[INFO] Plugin `%s` found, but the plugin is disabled", cfg.Name)
}
Expand Down
4 changes: 2 additions & 2 deletions plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ var localPluginRoot = "./.tflint.d/plugins"
// Plugin is an object handling plugins
// Basically, it is a wrapper for go-plugin and provides an API to handle them collectively.
type Plugin struct {
RuleSets []*tfplugin.Client
RuleSets map[string]*tfplugin.Client

clients []*plugin.Client
clients map[string]*plugin.Client
}

// Clean is a helper for ending plugin processes
Expand Down
2 changes: 1 addition & 1 deletion plugin/stub-generator/sources/bar/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

func main() {
plugin.Serve(&plugin.ServeOpts{
RuleSet: tflint.RuleSet{
RuleSet: &tflint.BuiltinRuleSet{
Name: "bar",
Version: "0.1.0",
Rules: []tflint.Rule{},
Expand Down
2 changes: 1 addition & 1 deletion plugin/stub-generator/sources/example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

func main() {
plugin.Serve(&plugin.ServeOpts{
RuleSet: tflint.RuleSet{
RuleSet: &tflint.BuiltinRuleSet{
Name: "example",
Version: "0.1.0",
Rules: []tflint.Rule{
Expand Down
2 changes: 1 addition & 1 deletion plugin/stub-generator/sources/foo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

func main() {
plugin.Serve(&plugin.ServeOpts{
RuleSet: tflint.RuleSet{
RuleSet: &tflint.BuiltinRuleSet{
Name: "foo",
Version: "0.1.0",
Rules: []tflint.Rule{},
Expand Down
44 changes: 40 additions & 4 deletions tflint/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
hcl "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/hashicorp/hcl/v2/hclsyntax"
homedir "github.com/mitchellh/go-homedir"
tfplugin "github.com/terraform-linters/tflint-plugin-sdk/tflint"
"github.com/terraform-linters/tflint/client"
Expand Down Expand Up @@ -65,8 +66,12 @@ type RuleConfig struct {

// PluginConfig is a TFLint's plugin config
type PluginConfig struct {
Name string `hcl:"name,label"`
Enabled bool `hcl:"enabled"`
Name string `hcl:"name,label"`
Enabled bool `hcl:"enabled"`
Body hcl.Body `hcl:",remain"`

// file is the result of parsing the HCL file that declares the plugin configuration.
file *hcl.File
}

// EmptyConfig returns default config
Expand Down Expand Up @@ -155,10 +160,15 @@ func (c *Config) Merge(other *Config) *Config {
}

// ToPluginConfig converts self into the plugin configuration format
func (c *Config) ToPluginConfig() *tfplugin.Config {
cfg := &tfplugin.Config{
func (c *Config) ToPluginConfig(name string) *tfplugin.MarshalledConfig {
pluginCfg := c.Plugins[name]
cfgRange := configBodyRange(pluginCfg.Body)

cfg := &tfplugin.MarshalledConfig{
Rules: map[string]*tfplugin.RuleConfig{},
DisabledByDefault: c.DisabledByDefault,
BodyBytes: cfgRange.SliceBytes(pluginCfg.file.Bytes),
BodyRange: cfgRange,
}
for _, rule := range c.Rules {
cfg.Rules[rule.Name] = &tfplugin.RuleConfig{
Expand Down Expand Up @@ -274,6 +284,10 @@ func loadConfigFromFile(file string) (*Config, error) {
}

cfg := raw.toConfig()
for _, plugin := range cfg.Plugins {
plugin.file = f
}

log.Printf("[DEBUG] Config loaded")
log.Printf("[DEBUG] Module: %t", cfg.Module)
log.Printf("[DEBUG] DeepCheck: %t", cfg.DeepCheck)
Expand Down Expand Up @@ -396,3 +410,25 @@ func (raw *rawConfig) toConfig() *Config {

return ret
}

func configBodyRange(body hcl.Body) hcl.Range {
var bodyRange hcl.Range

// Estimate the range of the body from the range of all attributes and blocks.
hclBody := body.(*hclsyntax.Body)
for _, attr := range hclBody.Attributes {
if bodyRange.Empty() {
bodyRange = attr.Range()
} else {
bodyRange = hcl.RangeOver(bodyRange, attr.Range())
}
}
for _, block := range hclBody.Blocks {
if bodyRange.Empty() {
bodyRange = block.Range()
} else {
bodyRange = hcl.RangeOver(bodyRange, block.Range())
}
}
return bodyRange
}
47 changes: 27 additions & 20 deletions tflint/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,12 @@ func Test_LoadConfig(t *testing.T) {
}

opts := []cmp.Option{
cmpopts.IgnoreUnexported(PluginConfig{}),
cmpopts.IgnoreFields(PluginConfig{}, "Body"),
cmpopts.IgnoreFields(RuleConfig{}, "Body"),
}
if !cmp.Equal(tc.Expected, ret, opts...) {
t.Fatalf("Failed `%s` test: diff=%s", tc.Name, cmp.Diff(tc.Expected, ret))
t.Fatalf("Failed `%s` test: diff=%s", tc.Name, cmp.Diff(tc.Expected, ret, opts...))
}

defaultConfigFile = originalDefault
Expand Down Expand Up @@ -576,6 +578,7 @@ func Test_Merge(t *testing.T) {
ret := tc.Base.Merge(tc.Other)

opts := []cmp.Option{
cmpopts.IgnoreUnexported(PluginConfig{}),
cmpopts.IgnoreUnexported(hclsyntax.Body{}),
cmpopts.IgnoreFields(hclsyntax.Body{}, "Attributes", "Blocks"),
}
Expand All @@ -586,22 +589,18 @@ func Test_Merge(t *testing.T) {
}

func Test_ToPluginConfig(t *testing.T) {
config := &Config{
Rules: map[string]*RuleConfig{
"aws_instance_invalid_type": {
Name: "aws_instance_invalid_type",
Enabled: false,
},
"aws_instance_invalid_ami": {
Name: "aws_instance_invalid_ami",
Enabled: true,
},
},
DisabledByDefault: true,
currentDir, err := os.Getwd()
if err != nil {
t.Fatal(err)
}

ret := config.ToPluginConfig()
expected := &tfplugin.Config{
config, err := LoadConfig(filepath.Join(currentDir, "test-fixtures", "config", "plugin.hcl"))
if err != nil {
t.Fatalf("Failed: Unexpected error occurred: %s", err)
}

ret := config.ToPluginConfig("foo")
expected := &tfplugin.MarshalledConfig{
Rules: map[string]*tfplugin.RuleConfig{
"aws_instance_invalid_type": {
Name: "aws_instance_invalid_type",
Expand All @@ -613,9 +612,16 @@ func Test_ToPluginConfig(t *testing.T) {
},
},
DisabledByDefault: true,
BodyRange: hcl.Range{Start: hcl.Pos{Line: 14, Column: 3}, End: hcl.Pos{Line: 16, Column: 17}},
}
opts := cmp.Options{
cmpopts.IgnoreUnexported(PluginConfig{}),
cmpopts.IgnoreFields(hcl.Range{}, "Filename"),
cmpopts.IgnoreFields(hcl.Pos{}, "Byte"),
cmpopts.IgnoreFields(tfplugin.MarshalledConfig{}, "BodyBytes"),
}
if !cmp.Equal(expected, ret) {
t.Fatalf("Failed to match: %s", cmp.Diff(expected, ret))
if !cmp.Equal(expected, ret, opts...) {
t.Fatalf("Failed to match: %s", cmp.Diff(expected, ret, opts...))
}
}

Expand Down Expand Up @@ -822,14 +828,15 @@ func Test_copy(t *testing.T) {
},
}

opt := cmpopts.IgnoreUnexported(PluginConfig{})
for _, tc := range cases {
ret := cfg.copy()
if !cmp.Equal(cfg, ret) {
t.Fatalf("The copied config doesn't match original: Diff=%s", cmp.Diff(cfg, ret))
if !cmp.Equal(cfg, ret, opt) {
t.Fatalf("The copied config doesn't match original: Diff=%s", cmp.Diff(cfg, ret, opt))
}

tc.SideEffect(ret)
if cmp.Equal(cfg, ret) {
if cmp.Equal(cfg, ret, opt) {
t.Fatalf("The original was changed when updating `%s`", tc.Name)
}
}
Expand Down
21 changes: 21 additions & 0 deletions tflint/test-fixtures/config/plugin.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
config {
disabled_by_default = true
}

rule "aws_instance_invalid_type" {
enabled = false
}

rule "aws_instance_invalid_ami" {
enabled = true
}

plugin "foo" {
enabled = true

custom = "foo"
}

plugin "bar" {
enabled = false
}

0 comments on commit 9c0fc38

Please sign in to comment.