From 622adcf7cbcf9d85fef51aa4d23d868dbddcf3c7 Mon Sep 17 00:00:00 2001 From: wata_mac Date: Mon, 12 Dec 2016 23:36:01 +0900 Subject: [PATCH] add invalid subnet deep detector --- detector/aws_instance_invalid_subnet.go | 61 ++++++++++++++ detector/aws_instance_invalid_subnet_test.go | 89 ++++++++++++++++++++ detector/detector.go | 1 + detector/response_cache.go | 1 + docs/AWS_Instance_Invalid_Subnet.md | 33 ++++++++ docs/README.md | 1 + 6 files changed, 186 insertions(+) create mode 100644 detector/aws_instance_invalid_subnet.go create mode 100644 detector/aws_instance_invalid_subnet_test.go create mode 100644 docs/AWS_Instance_Invalid_Subnet.md diff --git a/detector/aws_instance_invalid_subnet.go b/detector/aws_instance_invalid_subnet.go new file mode 100644 index 000000000..3761f221a --- /dev/null +++ b/detector/aws_instance_invalid_subnet.go @@ -0,0 +1,61 @@ +package detector + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/wata727/tflint/issue" +) + +type AwsInstanceInvalidSubnetDetector struct { + *Detector +} + +func (d *Detector) CreateAwsInstanceInvalidSubnetDetector() *AwsInstanceInvalidSubnetDetector { + return &AwsInstanceInvalidSubnetDetector{d} +} + +func (d *AwsInstanceInvalidSubnetDetector) Detect(issues *[]*issue.Issue) { + if !d.Config.DeepCheck { + d.Logger.Info("skip this rule.") + 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_instance").Items { + subnetToken, err := hclLiteralToken(item, "subnet_id") + if err != nil { + d.Logger.Error(err) + continue + } + 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_instance_invalid_subnet_test.go b/detector/aws_instance_invalid_subnet_test.go new file mode 100644 index 000000000..9a6b37ac5 --- /dev/null +++ b/detector/aws_instance_invalid_subnet_test.go @@ -0,0 +1,89 @@ +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 TestDetectAwsInstanceInvalidSubnet(t *testing.T) { + cases := []struct { + Name string + Src string + Response []*ec2.Subnet + Issues []*issue.Issue + }{ + { + Name: "Subnet ID is invalid", + Src: ` +resource "aws_instance" "web" { + subnet_id = "subnet-1234abcd" +}`, + 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: 3, + File: "test.tf", + }, + }, + }, + { + Name: "key name is valid", + Src: ` +resource "aws_instance" "web" { + subnet_id = "subnet-1234abcd" +}`, + 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( + "CreateAwsInstanceInvalidSubnetDetector", + 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 de1521fa0..111b94051 100644 --- a/detector/detector.go +++ b/detector/detector.go @@ -33,6 +33,7 @@ var detectors = map[string]string{ "aws_instance_invalid_iam_profile": "CreateAwsInstanceInvalidIAMProfileDetector", "aws_instance_invalid_ami": "CreateAwsInstanceInvalidAMIDetector", "aws_instance_invalid_key_name": "CreateAwsInstanceInvalidKeyNameDetector", + "aws_instance_invalid_subnet": "CreateAwsInstanceInvalidSubnetDetector", } func NewDetector(listMap map[string]*ast.ObjectList, c *config.Config) (*Detector, error) { diff --git a/detector/response_cache.go b/detector/response_cache.go index 9aba36527..8385f5098 100644 --- a/detector/response_cache.go +++ b/detector/response_cache.go @@ -8,5 +8,6 @@ import ( type ResponseCache struct { DescribeImagesOutput *ec2.DescribeImagesOutput DescribeKeyPairsOutput *ec2.DescribeKeyPairsOutput + DescribeSubnetsOutput *ec2.DescribeSubnetsOutput ListInstanceProfilesOutput *iam.ListInstanceProfilesOutput } diff --git a/docs/AWS_Instance_Invalid_Subnet.md b/docs/AWS_Instance_Invalid_Subnet.md new file mode 100644 index 000000000..412595bf2 --- /dev/null +++ b/docs/AWS_Instance_Invalid_Subnet.md @@ -0,0 +1,33 @@ +# AWS Instance 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_instance" "web" { + ami = "ami-1234abcd" + instance_type = "t2.micro" + iam_instance_profile = "app-user" + key_name = "secret" + subnet_id = "subnet-1234abcd" # This subnet ID does not exists + + tags { + Name = "HelloWorld" + } +} +``` + +The following is the execution result of TFLint: + +``` +$ tflint --deep +template.tf + ERROR:6 "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 bc015ff3e..13a86f5fc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,6 +19,7 @@ Issues are classified into the following three types. - [Default Standard Volume](AWS_Instance_Default_Standard_Volume.md) - [Invalid AMI](AWS_Instance_Invalid_AMI.md) - [Invalid Key Name](AWS_Instance_Invalid_Key_Name.md) + - [Invalid Subnet](AWS_Instance_Invalid_Subnet.md) - AWS DB Instance - [Default Parameter Group](AWS_DB_Instance_Default_Parameter_Group.md) - AWS Elasticache Cluster