Skip to content

Commit 9b3e7d8

Browse files
committed
correct code as code review suggested
1 parent 15c428b commit 9b3e7d8

File tree

4 files changed

+116
-38
lines changed

4 files changed

+116
-38
lines changed

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ All rules are enabled by default, but by setting `preset = "recommended"`, you c
1515
|[terraform_module_pinned_source](terraform_module_pinned_source.md)|Disallow specifying a git or mercurial repository as a module source without pinning to a version||
1616
|[terraform_module_version](terraform_module_version.md)|Checks that Terraform modules sourced from a registry specify a version||
1717
|[terraform_naming_convention](terraform_naming_convention.md)|Enforces naming conventions for resources, data sources, etc||
18+
|[terraform_ordered_variables](terraform_ordered_variables.md)|Recommend proper order for variable blocks||
1819
|[terraform_required_providers](terraform_required_providers.md)|Require that all providers have version constraints through required_providers||
1920
|[terraform_required_version](terraform_required_version.md)|Disallow `terraform` declarations without require_version||
2021
|[terraform_standard_module_structure](terraform_standard_module_structure.md)|Ensure that a module complies with the Terraform Standard Module Structure||

docs/rules/terraform_ordered_variables.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,32 @@ It helps to improve the readability of terraform code by sorting variable blocks
4949

5050
## How To Fix
5151

52-
Sort variables in the following order: required(without default value) variables in alphabetical order, optional variables in alphabetical order.
52+
Sort variables in the following order: required(without default value) variables in alphabetical order, optional variables in alphabetical order.
53+
54+
For the code in [example](#Example), it should be sorted as the following order:
55+
56+
```hcl
57+
variable "image_id" {
58+
type = string
59+
}
60+
61+
variable "availability_zone_names" {
62+
type = list(string)
63+
default = ["us-west-1a"]
64+
}
65+
66+
variable "docker_ports" {
67+
type = list(object({
68+
internal = number
69+
external = number
70+
protocol = string
71+
}))
72+
default = [
73+
{
74+
internal = 8300
75+
external = 8300
76+
protocol = "tcp"
77+
}
78+
]
79+
}
80+
```

rules/terraform_ordered_variable.go

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package rules
22

33
import (
4-
"reflect"
4+
"github.com/google/go-cmp/cmp"
55
"sort"
66

77
"github.com/hashicorp/hcl/v2"
@@ -10,38 +10,38 @@ import (
1010
"github.com/terraform-linters/tflint-ruleset-terraform/project"
1111
)
1212

13-
// TerraformVariableOrderRule checks whether the variables are sorted in expected order
14-
type TerraformVariableOrderRule struct {
13+
// TerraformOrderedVariablesRule checks whether the variables are sorted in expected order
14+
type TerraformOrderedVariablesRule struct {
1515
tflint.DefaultRule
1616
}
1717

1818
// NewTerraformOrderedVariablesRule returns a new rule
19-
func NewTerraformOrderedVariablesRule() *TerraformVariableOrderRule {
20-
return &TerraformVariableOrderRule{}
19+
func NewTerraformOrderedVariablesRule() *TerraformOrderedVariablesRule {
20+
return &TerraformOrderedVariablesRule{}
2121
}
2222

2323
// Name returns the rule name
24-
func (r *TerraformVariableOrderRule) Name() string {
24+
func (r *TerraformOrderedVariablesRule) Name() string {
2525
return "terraform_ordered_variables"
2626
}
2727

2828
// Enabled returns whether the rule is enabled by default
29-
func (r *TerraformVariableOrderRule) Enabled() bool {
29+
func (r *TerraformOrderedVariablesRule) Enabled() bool {
3030
return true
3131
}
3232

3333
// Severity returns the rule severity
34-
func (r *TerraformVariableOrderRule) Severity() tflint.Severity {
34+
func (r *TerraformOrderedVariablesRule) Severity() tflint.Severity {
3535
return tflint.NOTICE
3636
}
3737

3838
// Link returns the rule reference link
39-
func (r *TerraformVariableOrderRule) Link() string {
39+
func (r *TerraformOrderedVariablesRule) Link() string {
4040
return project.ReferenceLink(r.Name())
4141
}
4242

4343
// Check checks whether the variables are sorted in expected order
44-
func (r *TerraformVariableOrderRule) Check(runner tflint.Runner) error {
44+
func (r *TerraformOrderedVariablesRule) Check(runner tflint.Runner) error {
4545
path, err := runner.GetModulePath()
4646
if err != nil {
4747
return err
@@ -75,7 +75,7 @@ func (r *TerraformVariableOrderRule) Check(runner tflint.Runner) error {
7575
return nil
7676
}
7777

78-
func (r *TerraformVariableOrderRule) variablesGroupByFile(body *hclext.BodyContent) map[string]hclext.Blocks {
78+
func (r *TerraformOrderedVariablesRule) variablesGroupByFile(body *hclext.BodyContent) map[string]hclext.Blocks {
7979
variables := make(map[string]hclext.Blocks)
8080
for _, b := range body.Blocks {
8181
variables[b.DefRange.Filename] = append(variables[b.DefRange.Filename], b)
@@ -88,13 +88,13 @@ func (r *TerraformVariableOrderRule) variablesGroupByFile(body *hclext.BodyConte
8888
return variables
8989
}
9090

91-
func (r *TerraformVariableOrderRule) checkVariableOrder(runner tflint.Runner, blocks []*hclext.Block) error {
91+
func (r *TerraformOrderedVariablesRule) checkVariableOrder(runner tflint.Runner, blocks []*hclext.Block) error {
9292
requiredVars := r.getSortedVariableNames(blocks, false)
9393
optionalVars := r.getSortedVariableNames(blocks, true)
9494
sortedVariableNames := append(requiredVars, optionalVars...)
9595

9696
variableNames := r.getVariableNames(blocks)
97-
if reflect.DeepEqual(variableNames, sortedVariableNames) {
97+
if cmp.Equal(variableNames, sortedVariableNames) {
9898
return nil
9999
}
100100

@@ -105,40 +105,59 @@ func (r *TerraformVariableOrderRule) checkVariableOrder(runner tflint.Runner, bl
105105
)
106106
}
107107

108-
func (r *TerraformVariableOrderRule) issueRange(blocks hclext.Blocks) *hcl.Range {
108+
func (r *TerraformOrderedVariablesRule) issueRange(blocks hclext.Blocks) *hcl.Range {
109109
requiredVariables := r.getVariables(blocks, false)
110110
optionalVariables := r.getVariables(blocks, true)
111111

112-
for i, b := range requiredVariables {
113-
if i > 0 && (b.Labels[0] < requiredVariables[i-1].Labels[0]) {
114-
return &b.DefRange
115-
}
112+
if r.overlapped(requiredVariables, optionalVariables) {
113+
return &optionalVariables[0].DefRange
116114
}
117-
for i, b := range optionalVariables {
118-
if i > 0 && (b.Labels[0] < optionalVariables[i-1].Labels[0]) {
119-
return &b.DefRange
120-
}
115+
116+
firstRange := r.firstNonSortedBlockRange(requiredVariables)
117+
if firstRange != nil {
118+
return firstRange
119+
}
120+
firstRange = r.firstNonSortedBlockRange(optionalVariables)
121+
if firstRange != nil {
122+
return firstRange
123+
}
124+
panic("expected issue not found")
125+
}
126+
127+
func (r *TerraformOrderedVariablesRule) overlapped(requiredVariables, optionalVariables hclext.Blocks) bool {
128+
if len(requiredVariables) == 0 || len(optionalVariables) == 0 {
129+
return false
121130
}
122131

123132
firstOptional := optionalVariables[0].DefRange
124133
lastRequired := requiredVariables[len(requiredVariables)-1].DefRange
125134

126-
if firstOptional.Start.Line < lastRequired.Start.Line {
127-
return &lastRequired
128-
}
135+
return firstOptional.Start.Line < lastRequired.Start.Line
136+
}
129137

138+
func (r *TerraformOrderedVariablesRule) firstNonSortedBlockRange(blocks hclext.Blocks) *hcl.Range {
139+
for i, b := range blocks {
140+
if i == 0 {
141+
continue
142+
}
143+
previousVariableName := blocks[i-1].Labels[0]
144+
currentVariableName := b.Labels[0]
145+
if currentVariableName < previousVariableName {
146+
return &b.DefRange
147+
}
148+
}
130149
return nil
131150
}
132151

133-
func (r *TerraformVariableOrderRule) getVariableNames(blocks hclext.Blocks) []string {
152+
func (r *TerraformOrderedVariablesRule) getVariableNames(blocks hclext.Blocks) []string {
134153
var variableNames []string
135154
for _, b := range blocks {
136155
variableNames = append(variableNames, b.Labels[0])
137156
}
138157
return variableNames
139158
}
140159

141-
func (r *TerraformVariableOrderRule) getSortedVariableNames(blocks hclext.Blocks, defaultWanted bool) []string {
160+
func (r *TerraformOrderedVariablesRule) getSortedVariableNames(blocks hclext.Blocks, defaultWanted bool) []string {
142161
var names []string
143162
filteredBlocks := r.getVariables(blocks, defaultWanted)
144163
for _, b := range filteredBlocks {
@@ -148,7 +167,7 @@ func (r *TerraformVariableOrderRule) getSortedVariableNames(blocks hclext.Blocks
148167
return names
149168
}
150169

151-
func (r *TerraformVariableOrderRule) getVariables(blocks hclext.Blocks, defaultWanted bool) hclext.Blocks {
170+
func (r *TerraformOrderedVariablesRule) getVariables(blocks hclext.Blocks, defaultWanted bool) hclext.Blocks {
152171
var c hclext.Blocks
153172
for _, b := range blocks {
154173
if _, hasDefault := b.Body.Attributes["default"]; hasDefault == defaultWanted {

rules/terraform_ordered_variable_test.go

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package rules
22

33
import (
4+
"github.com/hashicorp/hcl/v2"
45
"testing"
56

67
"github.com/terraform-linters/tflint-plugin-sdk/helper"
78
)
89

910
func Test_TerraformVariableOrderRule(t *testing.T) {
10-
expectedIssue := &helper.Issue{
11-
Rule: NewTerraformOrderedVariablesRule(),
12-
Message: `Variables should be sorted in the following order: required(without default value) variables in alphabetical order, optional variables in alphabetical order.`,
11+
var expectedVariableOrderIssue = func(r hcl.Range) *helper.Issue {
12+
r.Filename = "config.tf"
13+
return &helper.Issue{
14+
Rule: NewTerraformOrderedVariablesRule(),
15+
Message: `Variables should be sorted in the following order: required(without default value) variables in alphabetical order, optional variables in alphabetical order.`,
16+
Range: r,
17+
}
1318
}
1419
cases := []struct {
1520
Name string
@@ -62,7 +67,12 @@ variable "availability_zone_names" {
6267
variable "image_id" {
6368
type = string
6469
}`,
65-
Expected: helper.Issues{expectedIssue},
70+
Expected: helper.Issues{
71+
expectedVariableOrderIssue(hcl.Range{
72+
Start: hcl.Pos{Line: 2, Column: 1},
73+
End: hcl.Pos{Line: 2, Column: 35},
74+
}),
75+
},
6676
},
6777
{
6878
Name: "4. sorting in alphabetic order",
@@ -86,7 +96,12 @@ variable "availability_zone_names" {
8696
type = list(string)
8797
default = ["us-west-1a"]
8898
}`,
89-
Expected: helper.Issues{expectedIssue},
99+
Expected: helper.Issues{
100+
expectedVariableOrderIssue(hcl.Range{
101+
Start: hcl.Pos{Line: 17, Column: 1},
102+
End: hcl.Pos{Line: 17, Column: 35},
103+
}),
104+
},
90105
},
91106
{
92107
Name: "5. mixed",
@@ -114,7 +129,12 @@ variable "availability_zone_names" {
114129
variable "image_id" {
115130
type = string
116131
}`,
117-
Expected: helper.Issues{expectedIssue},
132+
Expected: helper.Issues{
133+
expectedVariableOrderIssue(hcl.Range{
134+
Start: hcl.Pos{Line: 2, Column: 1},
135+
End: hcl.Pos{Line: 2, Column: 24},
136+
}),
137+
},
118138
},
119139
{
120140
Name: "6. required only",
@@ -153,7 +173,12 @@ variable "availability_zone_names" {
153173
type = list(string)
154174
}
155175
`,
156-
Expected: helper.Issues{expectedIssue},
176+
Expected: helper.Issues{
177+
expectedVariableOrderIssue(hcl.Range{
178+
Start: hcl.Pos{Line: 6, Column: 1},
179+
End: hcl.Pos{Line: 6, Column: 35},
180+
}),
181+
},
157182
},
158183
{
159184
Name: "9. incorrect optional only",
@@ -168,7 +193,12 @@ variable "availability_zone_names" {
168193
default = ["ap-northeast-1"]
169194
}
170195
`,
171-
Expected: helper.Issues{expectedIssue},
196+
Expected: helper.Issues{
197+
expectedVariableOrderIssue(hcl.Range{
198+
Start: hcl.Pos{Line: 7, Column: 1},
199+
End: hcl.Pos{Line: 7, Column: 35},
200+
}),
201+
},
172202
},
173203
}
174204
rule := NewTerraformOrderedVariablesRule()
@@ -185,7 +215,7 @@ variable "availability_zone_names" {
185215
t.Fatalf("Unexpected error occurred: %s", err)
186216
}
187217

188-
helper.AssertIssuesWithoutRange(t, tc.Expected, runner.Issues)
218+
helper.AssertIssues(t, tc.Expected, runner.Issues)
189219
})
190220
}
191221
}

0 commit comments

Comments
 (0)