From 4c3b343bf86df359225af3a8dc3edf9a1b27ecb3 Mon Sep 17 00:00:00 2001 From: wata_mac Date: Sat, 17 Dec 2016 23:40:39 +0900 Subject: [PATCH] add invalid subnet detector for ELB --- detector/aws_elb_invalid_subnet.go | 63 +++++++++++++++ detector/aws_elb_invalid_subnet_test.go | 101 ++++++++++++++++++++++++ detector/detector.go | 1 + docs/AWS_ELB_Invalid_Subnet.md | 42 ++++++++++ docs/README.md | 1 + 5 files changed, 208 insertions(+) create mode 100644 detector/aws_elb_invalid_subnet.go create mode 100644 detector/aws_elb_invalid_subnet_test.go create mode 100644 docs/AWS_ELB_Invalid_Subnet.md diff --git a/detector/aws_elb_invalid_subnet.go b/detector/aws_elb_invalid_subnet.go new file mode 100644 index 000000000..2238c6d00 --- /dev/null +++ b/detector/aws_elb_invalid_subnet.go @@ -0,0 +1,63 @@ +package detector + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/wata727/tflint/issue" +) + +type AwsELBInvalidSubnetDetector struct { + *Detector +} + +func (d *Detector) CreateAwsELBInvalidSubnetDetector() *AwsELBInvalidSubnetDetector { + return &AwsELBInvalidSubnetDetector{d} +} + +func (d *AwsELBInvalidSubnetDetector) Detect(issues *[]*issue.Issue) { + if !d.isDeepCheck("resource", "aws_elb") { + return + } + + validSubnets := map[string]bool{} + if d.ResponseCache.DescribeSubnetsOutput == nil { + resp, err := d.AwsClient.Ec2.DescribeSubnets(&ec2.DescribeSubnetsInput{}) + if err != nil { + d.Logger.Error(err) + d.Error = true + } + d.ResponseCache.DescribeSubnetsOutput = resp + } + for _, subnet := range d.ResponseCache.DescribeSubnetsOutput.Subnets { + validSubnets[*subnet.SubnetId] = true + } + + for filename, list := range d.ListMap { + for _, item := range list.Filter("resource", "aws_elb").Items { + subnetTokens, err := hclLiteralListToken(item, "subnets") + if err != nil { + d.Logger.Error(err) + continue + } + + for _, subnetToken := range subnetTokens { + subnet, err := d.evalToString(subnetToken.Text) + if err != nil { + d.Logger.Error(err) + continue + } + + if !validSubnets[subnet] { + issue := &issue.Issue{ + Type: "ERROR", + Message: fmt.Sprintf("\"%s\" is invalid subnet ID.", subnet), + Line: subnetToken.Pos.Line, + File: filename, + } + *issues = append(*issues, issue) + } + } + } + } +} diff --git a/detector/aws_elb_invalid_subnet_test.go b/detector/aws_elb_invalid_subnet_test.go new file mode 100644 index 000000000..99205715b --- /dev/null +++ b/detector/aws_elb_invalid_subnet_test.go @@ -0,0 +1,101 @@ +package detector + +import ( + "reflect" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/golang/mock/gomock" + "github.com/wata727/tflint/awsmock" + "github.com/wata727/tflint/config" + "github.com/wata727/tflint/issue" +) + +func TestDetectAwsELBInvalidSubnet(t *testing.T) { + cases := []struct { + Name string + Src string + Response []*ec2.Subnet + Issues []*issue.Issue + }{ + { + Name: "Subnet ID is invalid", + Src: ` +resource "aws_elb" "balancer" { + subnets = [ + "subnet-1234abcd", + "subnet-abcd1234", + ] +}`, + Response: []*ec2.Subnet{ + &ec2.Subnet{ + SubnetId: aws.String("subnet-12345678"), + }, + &ec2.Subnet{ + SubnetId: aws.String("subnet-abcdefgh"), + }, + }, + Issues: []*issue.Issue{ + &issue.Issue{ + Type: "ERROR", + Message: "\"subnet-1234abcd\" is invalid subnet ID.", + Line: 4, + File: "test.tf", + }, + &issue.Issue{ + Type: "ERROR", + Message: "\"subnet-abcd1234\" is invalid subnet ID.", + Line: 5, + File: "test.tf", + }, + }, + }, + { + Name: "Subnet ID is valid", + Src: ` +resource "aws_elb" "balancer" { + subnets = [ + "subnet-1234abcd", + "subnet-abcd1234", + ] +}`, + Response: []*ec2.Subnet{ + &ec2.Subnet{ + SubnetId: aws.String("subnet-1234abcd"), + }, + &ec2.Subnet{ + SubnetId: aws.String("subnet-abcd1234"), + }, + }, + Issues: []*issue.Issue{}, + }, + } + + for _, tc := range cases { + c := config.Init() + c.DeepCheck = true + + awsClient := c.NewAwsClient() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ec2mock := awsmock.NewMockEC2API(ctrl) + ec2mock.EXPECT().DescribeSubnets(&ec2.DescribeSubnetsInput{}).Return(&ec2.DescribeSubnetsOutput{ + Subnets: tc.Response, + }, nil) + awsClient.Ec2 = ec2mock + + var issues = []*issue.Issue{} + TestDetectByCreatorName( + "CreateAwsELBInvalidSubnetDetector", + tc.Src, + c, + awsClient, + &issues, + ) + + if !reflect.DeepEqual(issues, tc.Issues) { + t.Fatalf("Bad: %s\nExpected: %s\n\ntestcase: %s", issues, tc.Issues, tc.Name) + } + } +} diff --git a/detector/detector.go b/detector/detector.go index c25382ff5..a8cf3fa79 100644 --- a/detector/detector.go +++ b/detector/detector.go @@ -36,6 +36,7 @@ var detectors = map[string]string{ "aws_instance_invalid_subnet": "CreateAwsInstanceInvalidSubnetDetector", "aws_instance_invalid_vpc_security_group": "CreateAwsInstanceInvalidVPCSecurityGroupDetector", "aws_elb_invalid_security_group": "CreateAwsELBInvalidSecurityGroupDetector", + "aws_elb_invalid_subnet": "CreateAwsELBInvalidSubnetDetector", } func NewDetector(listMap map[string]*ast.ObjectList, c *config.Config) (*Detector, error) { diff --git a/docs/AWS_ELB_Invalid_Subnet.md b/docs/AWS_ELB_Invalid_Subnet.md new file mode 100644 index 000000000..69ec37138 --- /dev/null +++ b/docs/AWS_ELB_Invalid_Subnet.md @@ -0,0 +1,42 @@ +# AWS ELB Invalid Subnet +Report this issue if you have specified the invalid subnet ID. This issue type is ERROR. This issue is enable only with deep check. + +## Example +``` +resource "aws_elb" "balancer" { + name = "foobar-terraform-elb" + subnets = ["subnet-1234abcd"] # This subnet ID does not exists + security_groups = [ + "sg-12345678" + ] + + access_logs { + bucket = "foo" + bucket_prefix = "bar" + interval = 60 + } + + listener { + instance_port = 8000 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } +} +``` + +The following is the execution result of TFLint: + +``` +$ tflint --deep +template.tf + ERROR:3 "subnet-1234abcd" is invalid subnet ID. + +Result: 1 issues (1 errors , 0 warnings , 0 notices) +``` + +## Why +If an invalid subnet ID is specified, an error will occur at `terraform apply`. + +## How to fix +Check your subnets and select a valid subnet ID again. diff --git a/docs/README.md b/docs/README.md index ca1ccdf36..e2b1a9cfc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,6 +23,7 @@ Issues are classified into the following three types. - [Invalid VPC Security Group](AWS_Instance_Invalid_VPC_Security_Group.md) - AWS ELB - [Invalid Security Group](AWS_ELB_Invalid_Security_Group.md) + - [Invalid Subnet](AWS_ELB_Invalid_Subnet.md) - AWS DB Instance - [Default Parameter Group](AWS_DB_Instance_Default_Parameter_Group.md) - AWS Elasticache Cluster