From eaa469f7b0b1cddcdb34ae1fce6848e86990ce0e Mon Sep 17 00:00:00 2001 From: James Ray Date: Mon, 19 Jul 2021 15:34:06 -0400 Subject: [PATCH] aws_elasticache_replication_group - fixes terraform-linters/tflint-ruleset-aws#140 Add tests fir aws_elasticache_replication_group # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Mon Jul 19 15:34:06 2021 -0400 # # On branch aws_elasticache_replication_group # Changes to be committed: # modified: docs/rules/aws_elasticache_cluster_default_parameter_group.md # new file: docs/rules/aws_elasticache_replication_group_default_parameter_group.md # new file: rules/aws_elasticache_replication_group_default_parameter_group.go # new file: rules/aws_elasticache_replication_group_default_parameter_group_test.go # new file: rules/aws_elasticache_replication_group_invalid_type.go # new file: rules/aws_elasticache_replication_group_invalid_type_test.go # --- ...ticache_cluster_default_parameter_group.md | 4 +- ...plication_group_default_parameter_group.md | 39 ++++++ ...plication_group_default_parameter_group.go | 65 +++++++++ ...tion_group_default_parameter_group_test.go | 55 ++++++++ ...sticache_replication_group_invalid_type.go | 126 ++++++++++++++++++ ...che_replication_group_invalid_type_test.go | 55 ++++++++ 6 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 docs/rules/aws_elasticache_replication_group_default_parameter_group.md create mode 100644 rules/aws_elasticache_replication_group_default_parameter_group.go create mode 100644 rules/aws_elasticache_replication_group_default_parameter_group_test.go create mode 100644 rules/aws_elasticache_replication_group_invalid_type.go create mode 100644 rules/aws_elasticache_replication_group_invalid_type_test.go diff --git a/docs/rules/aws_elasticache_cluster_default_parameter_group.md b/docs/rules/aws_elasticache_cluster_default_parameter_group.md index 56411c2d..7251046b 100644 --- a/docs/rules/aws_elasticache_cluster_default_parameter_group.md +++ b/docs/rules/aws_elasticache_cluster_default_parameter_group.md @@ -26,8 +26,8 @@ $ tflint Notice: "default.redis3.2" is default parameter group. You cannot edit it. (aws_elasticache_cluster_default_parameter_group) on template.tf line 9: - 9: parameter_group_name = "default.redis3.2" // default paramete group! - + 9: parameter_group_name = "default.redis3.2" // default parameter group! + ``` ## Why diff --git a/docs/rules/aws_elasticache_replication_group_default_parameter_group.md b/docs/rules/aws_elasticache_replication_group_default_parameter_group.md new file mode 100644 index 00000000..9e68ddf3 --- /dev/null +++ b/docs/rules/aws_elasticache_replication_group_default_parameter_group.md @@ -0,0 +1,39 @@ +# aws_elasticache_replication_group_default_parameter_group + +Disallow using default parameter group. + +## Example + +```hcl +resource "aws_elasticache_replication_group" "redis" { + cluster_id = "app" + engine = "redis" + engine_version = "3.2.4" + maintenance_window = "sun:00:00-sun:06:00" + node_type = "cache.m4.large" + num_cache_nodes = 1 + port = 6379 + parameter_group_name = "default.redis3.2" // default paramete group! + subnet_group_name = "app-subnet-group" + security_group_ids = ["${aws_security_group.redis.id}"] +} +``` + +``` +$ tflint +1 issue(s) found: + +Notice: "default.redis3.2" is default parameter group. You cannot edit it. (aws_elasticache_replication_group_default_parameter_group) + + on template.tf line 9: + 9: parameter_group_name = "default.redis3.2" // default parameter group! + +``` + +## Why + +You can modify parameter values in a custom parameter group, but you can't change the parameter values in a default parameter group. + +## How To Fix + +Create a new parameter group, and change the `parameter_group_name` to that. diff --git a/rules/aws_elasticache_replication_group_default_parameter_group.go b/rules/aws_elasticache_replication_group_default_parameter_group.go new file mode 100644 index 00000000..30ea1033 --- /dev/null +++ b/rules/aws_elasticache_replication_group_default_parameter_group.go @@ -0,0 +1,65 @@ +package rules + +import ( + "fmt" + "regexp" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + "github.com/terraform-linters/tflint-ruleset-aws/project" +) + +// AwsElastiCacheReplicationGroupDefaultParameterGroupRule checks whether the cluster use default parameter group +type AwsElastiCacheReplicationGroupDefaultParameterGroupRule struct { + resourceType string + attributeName string +} + +// NewAwsElastiCacheReplicationGroupDefaultParameterGroupRule returns new rule with default attributes +func NewAwsElastiCacheReplicationGroupDefaultParameterGroupRule() *AwsElastiCacheReplicationGroupDefaultParameterGroupRule { + return &AwsElastiCacheReplicationGroupDefaultParameterGroupRule{ + resourceType: "aws_elasticache_replication_group", + attributeName: "parameter_group_name", + } +} + +// Name returns the rule name +func (r *AwsElastiCacheReplicationGroupDefaultParameterGroupRule) Name() string { + return "aws_elasticache_replication_group_default_parameter_group" +} + +// Enabled returns whether the rule is enabled by default +func (r *AwsElastiCacheReplicationGroupDefaultParameterGroupRule) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AwsElastiCacheReplicationGroupDefaultParameterGroupRule) Severity() string { + return tflint.NOTICE +} + +// Link returns the rule reference link +func (r *AwsElastiCacheReplicationGroupDefaultParameterGroupRule) Link() string { + return project.ReferenceLink(r.Name()) +} + +var defaultElastiCacheParameterGroupRegexp = regexp.MustCompile("^default") + +// Check checks the parameter group name starts with `default` +func (r *AwsElastiCacheReplicationGroupDefaultParameterGroupRule) Check(runner tflint.Runner) error { + return runner.WalkResourceAttributes(r.resourceType, r.attributeName, func(attribute *hcl.Attribute) error { + var parameterGroup string + err := runner.EvaluateExpr(attribute.Expr, ¶meterGroup, nil) + + return runner.EnsureNoError(err, func() error { + if defaultElastiCacheParameterGroupRegexp.Match([]byte(parameterGroup)) { + runner.EmitIssueOnExpr( + r, + fmt.Sprintf("\"%s\" is default parameter group. You cannot edit it.", parameterGroup), + attribute.Expr, + ) + } + return nil + }) + }) +} diff --git a/rules/aws_elasticache_replication_group_default_parameter_group_test.go b/rules/aws_elasticache_replication_group_default_parameter_group_test.go new file mode 100644 index 00000000..e815d5a7 --- /dev/null +++ b/rules/aws_elasticache_replication_group_default_parameter_group_test.go @@ -0,0 +1,55 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AwsElastiCacheReplicationGroupDefaultParameterGroup(t *testing.T) { + cases := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "default.redis3.2 is default parameter group", + Content: ` +resource "aws_elasticache_replication_group" "cache" { + parameter_group_name = "default.redis3.2" +}`, + Expected: helper.Issues{ + { + Rule: NewAwsElastiCacheReplicationGroupDefaultParameterGroupRule(), + Message: "\"default.redis3.2\" is default parameter group. You cannot edit it.", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 3, Column: 28}, + End: hcl.Pos{Line: 3, Column: 46}, + }, + }, + }, + }, + { + Name: "application3.2 is not default parameter group", + Content: ` +resource "aws_elasticache_replication_group" "cache" { + parameter_group_name = "application3.2" +}`, + Expected: helper.Issues{}, + }, + } + + rule := NewAwsElastiCacheReplicationGroupDefaultParameterGroupRule() + + for _, tc := range cases { + runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content}) + + if err := rule.Check(runner); err != nil { + t.Fatalf("Unexpected error occurred: %s", err) + } + + helper.AssertIssues(t, tc.Expected, runner.Issues) + } +} diff --git a/rules/aws_elasticache_replication_group_invalid_type.go b/rules/aws_elasticache_replication_group_invalid_type.go new file mode 100644 index 00000000..f36c2ab0 --- /dev/null +++ b/rules/aws_elasticache_replication_group_invalid_type.go @@ -0,0 +1,126 @@ +package rules + +import ( + "fmt" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + "github.com/terraform-linters/tflint-ruleset-aws/project" +) + +// AwsElastiCacheReplicationGroupInvalidTypeRule checks whether "aws_elasticache_replication_group" has invalid node type. +type AwsElastiCacheReplicationGroupInvalidTypeRule struct { + resourceType string + attributeName string + nodeTypes map[string]bool +} + +// NewAwsElastiCacheReplicationGroupInvalidTypeRule returns new rule with default attributes +func NewAwsElastiCacheReplicationGroupInvalidTypeRule() *AwsElastiCacheReplicationGroupInvalidTypeRule { + return &AwsElastiCacheReplicationGroupInvalidTypeRule{ + resourceType: "aws_elasticache_replication_group", + attributeName: "node_type", + nodeTypes: map[string]bool{ + "cache.t2.micro": true, + "cache.t2.small": true, + "cache.t2.medium": true, + "cache.t3.micro": true, + "cache.t3.small": true, + "cache.t3.medium": true, + "cache.m3.medium": true, + "cache.m3.large": true, + "cache.m3.xlarge": true, + "cache.m3.2xlarge": true, + "cache.m4.large": true, + "cache.m4.xlarge": true, + "cache.m4.2xlarge": true, + "cache.m4.4xlarge": true, + "cache.m4.10xlarge": true, + "cache.m5.large": true, + "cache.m5.xlarge": true, + "cache.m5.2xlarge": true, + "cache.m5.4xlarge": true, + "cache.m5.12xlarge": true, + "cache.m5.24xlarge": true, + "cache.m6g.large": true, + "cache.m6g.xlarge": true, + "cache.m6g.2xlarge": true, + "cache.m6g.4xlarge": true, + "cache.m6g.8xlarge": true, + "cache.m6g.12xlarge": true, + "cache.m6g.16xlarge": true, + "cache.r3.large": true, + "cache.r3.xlarge": true, + "cache.r3.2xlarge": true, + "cache.r3.4xlarge": true, + "cache.r3.8xlarge": true, + "cache.r4.large": true, + "cache.r4.xlarge": true, + "cache.r4.2xlarge": true, + "cache.r4.4xlarge": true, + "cache.r4.8xlarge": true, + "cache.r4.16xlarge": true, + "cache.r5.large": true, + "cache.r5.xlarge": true, + "cache.r5.2xlarge": true, + "cache.r5.4xlarge": true, + "cache.r5.12xlarge": true, + "cache.r5.24xlarge": true, + "cache.r6g.large": true, + "cache.r6g.xlarge": true, + "cache.r6g.2xlarge": true, + "cache.r6g.4xlarge": true, + "cache.r6g.8xlarge": true, + "cache.r6g.12xlarge": true, + "cache.r6g.16xlarge": true, + "cache.m1.small": true, + "cache.m1.medium": true, + "cache.m1.large": true, + "cache.m1.xlarge": true, + "cache.m2.xlarge": true, + "cache.m2.2xlarge": true, + "cache.m2.4xlarge": true, + "cache.c1.xlarge": true, + "cache.t1.micro": true, + }, + } +} + +// Name returns the rule name +func (r *AwsElastiCacheReplicationGroupInvalidTypeRule) Name() string { + return "aws_elasticache_replication_group_invalid_type" +} + +// Enabled returns whether the rule is enabled by default +func (r *AwsElastiCacheReplicationGroupInvalidTypeRule) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AwsElastiCacheReplicationGroupInvalidTypeRule) Severity() string { + return tflint.ERROR +} + +// Link returns the rule reference link +func (r *AwsElastiCacheReplicationGroupInvalidTypeRule) Link() string { + return project.ReferenceLink(r.Name()) +} + +// Check checks whether "aws_elasticache_replication_group" has invalid node type. +func (r *AwsElastiCacheReplicationGroupInvalidTypeRule) Check(runner tflint.Runner) error { + return runner.WalkResourceAttributes(r.resourceType, r.attributeName, func(attribute *hcl.Attribute) error { + var nodeType string + err := runner.EvaluateExpr(attribute.Expr, &nodeType, nil) + + return runner.EnsureNoError(err, func() error { + if !r.nodeTypes[nodeType] { + runner.EmitIssueOnExpr( + r, + fmt.Sprintf("\"%s\" is invalid node type.", nodeType), + attribute.Expr, + ) + } + return nil + }) + }) +} diff --git a/rules/aws_elasticache_replication_group_invalid_type_test.go b/rules/aws_elasticache_replication_group_invalid_type_test.go new file mode 100644 index 00000000..6aca2c68 --- /dev/null +++ b/rules/aws_elasticache_replication_group_invalid_type_test.go @@ -0,0 +1,55 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AwsElastiCacheReplicationGroupInvalidType(t *testing.T) { + cases := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "t2.micro is invalid", + Content: ` +resource "aws_elasticache_replication_group" "redis" { + node_type = "t2.micro" +}`, + Expected: helper.Issues{ + { + Rule: NewAwsElastiCacheReplicationGroupInvalidTypeRule(), + Message: "\"t2.micro\" is invalid node type.", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 3, Column: 17}, + End: hcl.Pos{Line: 3, Column: 27}, + }, + }, + }, + }, + { + Name: "cache.t2.micro is valid", + Content: ` +resource "aws_elasticache_replication_group" "redis" { + node_type = "cache.t2.micro" +}`, + Expected: helper.Issues{}, + }, + } + + rule := NewAwsElastiCacheReplicationGroupInvalidTypeRule() + + for _, tc := range cases { + runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content}) + + if err := rule.Check(runner); err != nil { + t.Fatalf("Unexpected error occurred: %s", err) + } + + helper.AssertIssues(t, tc.Expected, runner.Issues) + } +}