Skip to content

Commit 30fdec9

Browse files
DENA-969: Implemented rule for checking the backend (#5)
* Removed sample rules * Add CODEOWNERS * Updated plugin name * Updated dependabot * Added golangci-lint with pre-commit * Update GH workflow * Add yaml check * Use credentials * Inlined go mod tidy * Add golangci-lint * Run pre-commit on PR * Skip golang-ci-full from pre-commit * Run tests only for linux * Added linting * Removed the last sample rule * Updated readme * Revert "Removed the last sample rule" This reverts commit 43ba7da. * Updated to latest libs * Update .github/dependabot.yml Co-authored-by: Matt Hughes <[email protected]> * Update .golangci.yaml Co-authored-by: Matt Hughes <[email protected]> * Update .golangci.yaml Co-authored-by: Matt Hughes <[email protected]> * Update .golangci.yaml Co-authored-by: Matt Hughes <[email protected]> * Added gitignore * Fixed linting * Updated name * Added implementation for msk backend check. * Removed sample rules * Add documentation for the msk module backend rule * Fixed anchor * Added the implemented rule in the table * Fixed main build. * Added helper struct for overwriting the OriginalWd * Added helper struct for overwriting the OriginalWd * Update README.md Co-authored-by: Matt Hughes <[email protected]> * Update rules/msk_module_backend.go Co-authored-by: Matt Hughes <[email protected]> * Remove duplicated example * Rearranged the test cases * Implemented link to rule --------- Co-authored-by: Matt Hughes <[email protected]>
1 parent 3386b87 commit 30fdec9

File tree

10 files changed

+376
-132
lines changed

10 files changed

+376
-132
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ plugin "kafka-config" {
1717

1818
## Rules
1919

20-
|Name|Description|Severity|Enabled|Link|
21-
| --- | --- | --- | --- | --- |
20+
| Name | Description |
21+
|---------------------------------------------------|------------------------------------------------------------------------------------------|
22+
| [msk_module_backend](rules/msk_module_backend.md) | Requires an S3 backend to be defined, with a key that has as suffix the name of the team (taken from the current directory name) |
23+
2224

2325
## Building the plugin
2426

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ go 1.22.2
44

55
require (
66
github.com/hashicorp/hcl/v2 v2.22.0
7+
github.com/stretchr/testify v1.7.2
78
github.com/terraform-linters/tflint-plugin-sdk v0.21.0
89
)
910

1011
require (
1112
github.com/agext/levenshtein v1.2.3 // indirect
1213
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
14+
github.com/davecgh/go-spew v1.1.1 // indirect
1315
github.com/fatih/color v1.17.0 // indirect
1416
github.com/golang/protobuf v1.5.4 // indirect
1517
github.com/google/go-cmp v0.6.0 // indirect
@@ -22,6 +24,7 @@ require (
2224
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
2325
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
2426
github.com/oklog/run v1.1.0 // indirect
27+
github.com/pmezard/go-difflib v1.0.0 // indirect
2528
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
2629
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
2730
github.com/zclconf/go-cty v1.15.0 // indirect
@@ -34,4 +37,5 @@ require (
3437
google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect
3538
google.golang.org/grpc v1.67.1 // indirect
3639
google.golang.org/protobuf v1.34.2 // indirect
40+
gopkg.in/yaml.v3 v3.0.1 // indirect
3741
)

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
8383
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
8484
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
8585
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
86+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
8687
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
8788
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
8889
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func main() {
1313
Name: "kafka-config",
1414
Version: "0.1.0",
1515
Rules: []tflint.Rule{
16-
rules.NewTerraformBackendTypeRule(),
16+
rules.NewMskModuleBackendRule(),
1717
},
1818
},
1919
})

rules/msk_module_backend.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package rules
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
"strings"
7+
8+
"github.com/hashicorp/hcl/v2"
9+
"github.com/hashicorp/hcl/v2/gohcl"
10+
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
11+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
12+
)
13+
14+
// MskModuleBackendRule checks whether an MSK module has an S3 backend defined with a key that has as suffix the name of the team.
15+
type MskModuleBackendRule struct {
16+
tflint.DefaultRule
17+
}
18+
19+
// NewMskModuleBackendRule returns a new rule.
20+
func NewMskModuleBackendRule() *MskModuleBackendRule {
21+
return &MskModuleBackendRule{}
22+
}
23+
24+
// Name returns the rule name.
25+
func (r *MskModuleBackendRule) Name() string {
26+
return "msk_module_backend"
27+
}
28+
29+
// Enabled returns whether the rule is enabled by default.
30+
func (r *MskModuleBackendRule) Enabled() bool {
31+
return true
32+
}
33+
34+
// Severity returns the rule severity.
35+
func (r *MskModuleBackendRule) Severity() tflint.Severity {
36+
return tflint.ERROR
37+
}
38+
39+
// Link returns the rule reference link.
40+
func (r *MskModuleBackendRule) Link() string {
41+
return ReferenceLink(r.Name())
42+
}
43+
44+
func (r *MskModuleBackendRule) Check(runner tflint.Runner) error {
45+
path, err := runner.GetModulePath()
46+
if err != nil {
47+
return fmt.Errorf("getting module path: %w", err)
48+
}
49+
if !path.IsRoot() {
50+
// This rule does not evaluate child modules.
51+
return nil
52+
}
53+
54+
// This rule is an example to get attributes of blocks other than resources.
55+
content, err := runner.GetModuleContent(&hclext.BodySchema{
56+
Blocks: []hclext.BlockSchema{
57+
{
58+
Type: "terraform",
59+
Body: &hclext.BodySchema{
60+
Blocks: []hclext.BlockSchema{
61+
{
62+
Type: "backend",
63+
LabelNames: []string{"type"},
64+
Body: &hclext.BodySchema{
65+
Attributes: []hclext.AttributeSchema{
66+
{Name: "key"},
67+
},
68+
},
69+
},
70+
},
71+
},
72+
},
73+
},
74+
}, nil)
75+
if err != nil {
76+
return fmt.Errorf("getting module content: %w", err)
77+
}
78+
79+
backend := findBackendDef(content)
80+
if backend == nil {
81+
err := runner.EmitIssue(r, "an s3 backend should be configured for a kafka MSK module", hcl.Range{})
82+
if err != nil {
83+
return fmt.Errorf("emitting issue: backend missing: %w", err)
84+
}
85+
return nil
86+
}
87+
88+
backendType := backend.Labels[0]
89+
if backendType != "s3" {
90+
err := runner.EmitIssue(r, "backend should always be s3 for a kafka MSK module", backend.DefRange)
91+
if err != nil {
92+
return fmt.Errorf("emitting issue: always s3: %w", err)
93+
}
94+
return nil
95+
}
96+
97+
keyAttr, keyExists := backend.Body.Attributes["key"]
98+
if !keyExists {
99+
err := runner.EmitIssue(r, "the s3 backend should specify the details inside the kafka MSK module", backend.DefRange)
100+
if err != nil {
101+
return fmt.Errorf("emitting issue: no s3 details: %w", err)
102+
}
103+
return nil
104+
}
105+
106+
return r.checkKeyHasTeamSuffix(runner, keyAttr)
107+
}
108+
109+
func findBackendDef(content *hclext.BodyContent) *hclext.Block {
110+
if content.IsEmpty() {
111+
return nil
112+
}
113+
for _, tfConfig := range content.Blocks {
114+
if len(tfConfig.Body.Blocks) > 0 {
115+
return tfConfig.Body.Blocks[0]
116+
}
117+
}
118+
return nil
119+
}
120+
121+
func (r *MskModuleBackendRule) checkKeyHasTeamSuffix(runner tflint.Runner, keyAttr *hclext.Attribute) error {
122+
var key string
123+
diags := gohcl.DecodeExpression(keyAttr.Expr, nil, &key)
124+
if diags.HasErrors() {
125+
return diags
126+
}
127+
128+
modulePath, err := runner.GetOriginalwd()
129+
if err != nil {
130+
return fmt.Errorf("failed getting module path: %w", err)
131+
}
132+
133+
teamName := filepath.Base(modulePath)
134+
135+
if !strings.HasSuffix(key, teamName) {
136+
err = runner.EmitIssue(
137+
r,
138+
fmt.Sprintf("backend key must have the team's name '%s' as a suffix. Current value is: %s", teamName, key),
139+
keyAttr.Range,
140+
)
141+
if err != nil {
142+
return fmt.Errorf("emitting issue: no team suffix: %w", err)
143+
}
144+
}
145+
146+
return nil
147+
}

rules/msk_module_backend.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# msk_module_backend
2+
3+
Requires an S3 backend to be defined, with a key that has as suffix the name of the team.
4+
5+
## Example
6+
7+
### Bad examples
8+
```hcl
9+
// no s3 backend
10+
terraform {
11+
12+
}
13+
14+
// backend is not S3
15+
terraform {
16+
backend "local" {
17+
}
18+
}
19+
20+
// s3 backend doesn't have details
21+
terraform {
22+
backend "s3" {
23+
}
24+
}
25+
26+
// backend key doesn't have the team's suffix
27+
terraform {
28+
backend "s3" {
29+
bucket = "mybucket"
30+
key = "key-without-team-suffix"
31+
region = "us-east-1"
32+
}
33+
}
34+
```
35+
36+
### Good example
37+
38+
Good for team `pubsub`:
39+
```hcl
40+
terraform {
41+
backend "s3" {
42+
bucket = "mybucket"
43+
key = "dev-aws/msk-pubsub"
44+
region = "us-east-1"
45+
}
46+
}
47+
```
48+
49+
## Why
50+
51+
We want to avoid team mixing their states due to copy/paste issues.
52+
With this rule, all the details for the bucket will always be specified in the kafka-cluster-config repository where we have a module per team and each team has a unique name.
53+
54+
## How To Fix
55+
56+
Define the S3 backend in the team's module, having the key as the team's suffix.
57+
58+
See [good example](#good-example)

0 commit comments

Comments
 (0)