Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aws_elasticache_replication_group #143

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/rules/aws_elasticache_cluster_default_parameter_group.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
65 changes: 65 additions & 0 deletions rules/aws_elasticache_replication_group_default_parameter_group.go
Original file line number Diff line number Diff line change
@@ -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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaultElastiCacheParameterGroupRegexp is already declared in here:

var defaultElastiCacheParameterGroupRegexp = regexp.MustCompile("^default")

Perhaps it's better to declare a variable name for this rule here.


// 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, &parameterGroup, 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
})
})
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
126 changes: 126 additions & 0 deletions rules/aws_elasticache_replication_group_invalid_type.go
Original file line number Diff line number Diff line change
@@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list could probably be shared with the same list here:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"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
})
})
}
55 changes: 55 additions & 0 deletions rules/aws_elasticache_replication_group_invalid_type_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}