github.com/jsoriano/terraform@v0.6.7-0.20151026070445-8b70867fdd95/builtin/providers/aws/resource_aws_security_group_rule_test.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "testing" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/ec2" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/terraform" 13 ) 14 15 func TestIpPermissionIDHash(t *testing.T) { 16 simple := &ec2.IpPermission{ 17 IpProtocol: aws.String("tcp"), 18 FromPort: aws.Int64(int64(80)), 19 ToPort: aws.Int64(int64(8000)), 20 IpRanges: []*ec2.IpRange{ 21 &ec2.IpRange{ 22 CidrIp: aws.String("10.0.0.0/8"), 23 }, 24 }, 25 } 26 27 egress := &ec2.IpPermission{ 28 IpProtocol: aws.String("tcp"), 29 FromPort: aws.Int64(int64(80)), 30 ToPort: aws.Int64(int64(8000)), 31 IpRanges: []*ec2.IpRange{ 32 &ec2.IpRange{ 33 CidrIp: aws.String("10.0.0.0/8"), 34 }, 35 }, 36 } 37 38 egress_all := &ec2.IpPermission{ 39 IpProtocol: aws.String("-1"), 40 IpRanges: []*ec2.IpRange{ 41 &ec2.IpRange{ 42 CidrIp: aws.String("10.0.0.0/8"), 43 }, 44 }, 45 } 46 47 vpc_security_group_source := &ec2.IpPermission{ 48 IpProtocol: aws.String("tcp"), 49 FromPort: aws.Int64(int64(80)), 50 ToPort: aws.Int64(int64(8000)), 51 UserIdGroupPairs: []*ec2.UserIdGroupPair{ 52 &ec2.UserIdGroupPair{ 53 UserId: aws.String("987654321"), 54 GroupId: aws.String("sg-12345678"), 55 }, 56 &ec2.UserIdGroupPair{ 57 UserId: aws.String("123456789"), 58 GroupId: aws.String("sg-987654321"), 59 }, 60 &ec2.UserIdGroupPair{ 61 UserId: aws.String("123456789"), 62 GroupId: aws.String("sg-12345678"), 63 }, 64 }, 65 } 66 67 security_group_source := &ec2.IpPermission{ 68 IpProtocol: aws.String("tcp"), 69 FromPort: aws.Int64(int64(80)), 70 ToPort: aws.Int64(int64(8000)), 71 UserIdGroupPairs: []*ec2.UserIdGroupPair{ 72 &ec2.UserIdGroupPair{ 73 UserId: aws.String("987654321"), 74 GroupName: aws.String("my-security-group"), 75 }, 76 &ec2.UserIdGroupPair{ 77 UserId: aws.String("123456789"), 78 GroupName: aws.String("my-security-group"), 79 }, 80 &ec2.UserIdGroupPair{ 81 UserId: aws.String("123456789"), 82 GroupName: aws.String("my-other-security-group"), 83 }, 84 }, 85 } 86 87 // hardcoded hashes, to detect future change 88 cases := []struct { 89 Input *ec2.IpPermission 90 Type string 91 Output string 92 }{ 93 {simple, "ingress", "sgrule-3403497314"}, 94 {egress, "egress", "sgrule-1173186295"}, 95 {egress_all, "egress", "sgrule-766323498"}, 96 {vpc_security_group_source, "egress", "sgrule-351225364"}, 97 {security_group_source, "egress", "sgrule-2198807188"}, 98 } 99 100 for _, tc := range cases { 101 actual := ipPermissionIDHash("sg-12345", tc.Type, tc.Input) 102 if actual != tc.Output { 103 t.Errorf("input: %s - %s\noutput: %s", tc.Type, tc.Input, actual) 104 } 105 } 106 } 107 108 func TestAccAWSSecurityGroupRule_Ingress_VPC(t *testing.T) { 109 var group ec2.SecurityGroup 110 111 testRuleCount := func(*terraform.State) error { 112 if len(group.IpPermissions) != 1 { 113 return fmt.Errorf("Wrong Security Group rule count, expected %d, got %d", 114 1, len(group.IpPermissions)) 115 } 116 117 rule := group.IpPermissions[0] 118 if *rule.FromPort != int64(80) { 119 return fmt.Errorf("Wrong Security Group port setting, expected %d, got %d", 120 80, int(*rule.FromPort)) 121 } 122 123 return nil 124 } 125 126 resource.Test(t, resource.TestCase{ 127 PreCheck: func() { testAccPreCheck(t) }, 128 Providers: testAccProviders, 129 CheckDestroy: testAccCheckAWSSecurityGroupRuleDestroy, 130 Steps: []resource.TestStep{ 131 resource.TestStep{ 132 Config: testAccAWSSecurityGroupRuleIngressConfig, 133 Check: resource.ComposeTestCheckFunc( 134 testAccCheckAWSSecurityGroupRuleExists("aws_security_group.web", &group), 135 testAccCheckAWSSecurityGroupRuleAttributes("aws_security_group_rule.ingress_1", &group, nil, "ingress"), 136 resource.TestCheckResourceAttr( 137 "aws_security_group_rule.ingress_1", "from_port", "80"), 138 testRuleCount, 139 ), 140 }, 141 }, 142 }) 143 } 144 145 func TestAccAWSSecurityGroupRule_Ingress_Classic(t *testing.T) { 146 var group ec2.SecurityGroup 147 148 testRuleCount := func(*terraform.State) error { 149 if len(group.IpPermissions) != 1 { 150 return fmt.Errorf("Wrong Security Group rule count, expected %d, got %d", 151 1, len(group.IpPermissions)) 152 } 153 154 rule := group.IpPermissions[0] 155 if *rule.FromPort != int64(80) { 156 return fmt.Errorf("Wrong Security Group port setting, expected %d, got %d", 157 80, int(*rule.FromPort)) 158 } 159 160 return nil 161 } 162 163 resource.Test(t, resource.TestCase{ 164 PreCheck: func() { testAccPreCheck(t) }, 165 Providers: testAccProviders, 166 CheckDestroy: testAccCheckAWSSecurityGroupRuleDestroy, 167 Steps: []resource.TestStep{ 168 resource.TestStep{ 169 Config: testAccAWSSecurityGroupRuleIngressClassicConfig, 170 Check: resource.ComposeTestCheckFunc( 171 testAccCheckAWSSecurityGroupRuleExists("aws_security_group.web", &group), 172 testAccCheckAWSSecurityGroupRuleAttributes("aws_security_group_rule.ingress_1", &group, nil, "ingress"), 173 resource.TestCheckResourceAttr( 174 "aws_security_group_rule.ingress_1", "from_port", "80"), 175 testRuleCount, 176 ), 177 }, 178 }, 179 }) 180 } 181 182 func TestAccAWSSecurityGroupRule_MultiIngress(t *testing.T) { 183 var group ec2.SecurityGroup 184 185 testMultiRuleCount := func(*terraform.State) error { 186 if len(group.IpPermissions) != 2 { 187 return fmt.Errorf("Wrong Security Group rule count, expected %d, got %d", 188 2, len(group.IpPermissions)) 189 } 190 191 var rule *ec2.IpPermission 192 for _, r := range group.IpPermissions { 193 if *r.FromPort == int64(80) { 194 rule = r 195 } 196 } 197 198 if *rule.ToPort != int64(8000) { 199 return fmt.Errorf("Wrong Security Group port 2 setting, expected %d, got %d", 200 8000, int(*rule.ToPort)) 201 } 202 203 return nil 204 } 205 206 resource.Test(t, resource.TestCase{ 207 PreCheck: func() { testAccPreCheck(t) }, 208 Providers: testAccProviders, 209 CheckDestroy: testAccCheckAWSSecurityGroupRuleDestroy, 210 Steps: []resource.TestStep{ 211 resource.TestStep{ 212 Config: testAccAWSSecurityGroupRuleConfigMultiIngress, 213 Check: resource.ComposeTestCheckFunc( 214 testAccCheckAWSSecurityGroupRuleExists("aws_security_group.web", &group), 215 testMultiRuleCount, 216 ), 217 }, 218 }, 219 }) 220 } 221 222 func TestAccAWSSecurityGroupRule_Egress(t *testing.T) { 223 var group ec2.SecurityGroup 224 225 resource.Test(t, resource.TestCase{ 226 PreCheck: func() { testAccPreCheck(t) }, 227 Providers: testAccProviders, 228 CheckDestroy: testAccCheckAWSSecurityGroupRuleDestroy, 229 Steps: []resource.TestStep{ 230 resource.TestStep{ 231 Config: testAccAWSSecurityGroupRuleEgressConfig, 232 Check: resource.ComposeTestCheckFunc( 233 testAccCheckAWSSecurityGroupRuleExists("aws_security_group.web", &group), 234 testAccCheckAWSSecurityGroupRuleAttributes("aws_security_group_rule.egress_1", &group, nil, "egress"), 235 ), 236 }, 237 }, 238 }) 239 } 240 241 func TestAccAWSSecurityGroupRule_SelfReference(t *testing.T) { 242 var group ec2.SecurityGroup 243 244 resource.Test(t, resource.TestCase{ 245 PreCheck: func() { testAccPreCheck(t) }, 246 Providers: testAccProviders, 247 CheckDestroy: testAccCheckAWSSecurityGroupRuleDestroy, 248 Steps: []resource.TestStep{ 249 resource.TestStep{ 250 Config: testAccAWSSecurityGroupRuleConfigSelfReference, 251 Check: resource.ComposeTestCheckFunc( 252 testAccCheckAWSSecurityGroupRuleExists("aws_security_group.web", &group), 253 ), 254 }, 255 }, 256 }) 257 } 258 259 // testing partial match implementation 260 func TestAccAWSSecurityGroupRule_PartialMatching_basic(t *testing.T) { 261 var group ec2.SecurityGroup 262 263 p := ec2.IpPermission{ 264 FromPort: aws.Int64(80), 265 ToPort: aws.Int64(80), 266 IpProtocol: aws.String("tcp"), 267 IpRanges: []*ec2.IpRange{ 268 &ec2.IpRange{CidrIp: aws.String("10.0.2.0/24")}, 269 &ec2.IpRange{CidrIp: aws.String("10.0.3.0/24")}, 270 &ec2.IpRange{CidrIp: aws.String("10.0.4.0/24")}, 271 }, 272 } 273 274 o := ec2.IpPermission{ 275 FromPort: aws.Int64(80), 276 ToPort: aws.Int64(80), 277 IpProtocol: aws.String("tcp"), 278 IpRanges: []*ec2.IpRange{ 279 &ec2.IpRange{CidrIp: aws.String("10.0.5.0/24")}, 280 }, 281 } 282 283 resource.Test(t, resource.TestCase{ 284 PreCheck: func() { testAccPreCheck(t) }, 285 Providers: testAccProviders, 286 CheckDestroy: testAccCheckAWSSecurityGroupRuleDestroy, 287 Steps: []resource.TestStep{ 288 resource.TestStep{ 289 Config: testAccAWSSecurityGroupRulePartialMatching, 290 Check: resource.ComposeTestCheckFunc( 291 testAccCheckAWSSecurityGroupRuleExists("aws_security_group.web", &group), 292 testAccCheckAWSSecurityGroupRuleAttributes("aws_security_group_rule.ingress", &group, &p, "ingress"), 293 testAccCheckAWSSecurityGroupRuleAttributes("aws_security_group_rule.other", &group, &o, "ingress"), 294 testAccCheckAWSSecurityGroupRuleAttributes("aws_security_group_rule.nat_ingress", &group, &o, "ingress"), 295 ), 296 }, 297 }, 298 }) 299 } 300 301 func TestAccAWSSecurityGroupRule_PartialMatching_Source(t *testing.T) { 302 var group ec2.SecurityGroup 303 var nat ec2.SecurityGroup 304 var p ec2.IpPermission 305 306 // This function creates the expected IPPermission with the group id from an 307 // external security group, needed because Security Group IDs are generated on 308 // AWS side and can't be known ahead of time. 309 setupSG := func(*terraform.State) error { 310 if nat.GroupId == nil { 311 return fmt.Errorf("Error: nat group has nil GroupID") 312 } 313 314 p = ec2.IpPermission{ 315 FromPort: aws.Int64(80), 316 ToPort: aws.Int64(80), 317 IpProtocol: aws.String("tcp"), 318 UserIdGroupPairs: []*ec2.UserIdGroupPair{ 319 &ec2.UserIdGroupPair{GroupId: nat.GroupId}, 320 }, 321 } 322 323 return nil 324 } 325 326 resource.Test(t, resource.TestCase{ 327 PreCheck: func() { testAccPreCheck(t) }, 328 Providers: testAccProviders, 329 CheckDestroy: testAccCheckAWSSecurityGroupRuleDestroy, 330 Steps: []resource.TestStep{ 331 resource.TestStep{ 332 Config: testAccAWSSecurityGroupRulePartialMatching_Source, 333 Check: resource.ComposeTestCheckFunc( 334 testAccCheckAWSSecurityGroupRuleExists("aws_security_group.web", &group), 335 testAccCheckAWSSecurityGroupRuleExists("aws_security_group.nat", &nat), 336 setupSG, 337 testAccCheckAWSSecurityGroupRuleAttributes("aws_security_group_rule.source_ingress", &group, &p, "ingress"), 338 ), 339 }, 340 }, 341 }) 342 343 } 344 345 func testAccCheckAWSSecurityGroupRuleDestroy(s *terraform.State) error { 346 conn := testAccProvider.Meta().(*AWSClient).ec2conn 347 348 for _, rs := range s.RootModule().Resources { 349 if rs.Type != "aws_security_group" { 350 continue 351 } 352 353 // Retrieve our group 354 req := &ec2.DescribeSecurityGroupsInput{ 355 GroupIds: []*string{aws.String(rs.Primary.ID)}, 356 } 357 resp, err := conn.DescribeSecurityGroups(req) 358 if err == nil { 359 if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == rs.Primary.ID { 360 return fmt.Errorf("Security Group (%s) still exists.", rs.Primary.ID) 361 } 362 363 return nil 364 } 365 366 ec2err, ok := err.(awserr.Error) 367 if !ok { 368 return err 369 } 370 // Confirm error code is what we want 371 if ec2err.Code() != "InvalidGroup.NotFound" { 372 return err 373 } 374 } 375 376 return nil 377 } 378 379 func testAccCheckAWSSecurityGroupRuleExists(n string, group *ec2.SecurityGroup) resource.TestCheckFunc { 380 return func(s *terraform.State) error { 381 rs, ok := s.RootModule().Resources[n] 382 if !ok { 383 return fmt.Errorf("Not found: %s", n) 384 } 385 386 if rs.Primary.ID == "" { 387 return fmt.Errorf("No Security Group is set") 388 } 389 390 conn := testAccProvider.Meta().(*AWSClient).ec2conn 391 req := &ec2.DescribeSecurityGroupsInput{ 392 GroupIds: []*string{aws.String(rs.Primary.ID)}, 393 } 394 resp, err := conn.DescribeSecurityGroups(req) 395 if err != nil { 396 return err 397 } 398 399 if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupId == rs.Primary.ID { 400 *group = *resp.SecurityGroups[0] 401 return nil 402 } 403 404 return fmt.Errorf("Security Group not found") 405 } 406 } 407 408 func testAccCheckAWSSecurityGroupRuleAttributes(n string, group *ec2.SecurityGroup, p *ec2.IpPermission, ruleType string) resource.TestCheckFunc { 409 return func(s *terraform.State) error { 410 rs, ok := s.RootModule().Resources[n] 411 if !ok { 412 return fmt.Errorf("Security Group Rule Not found: %s", n) 413 } 414 415 if rs.Primary.ID == "" { 416 return fmt.Errorf("No Security Group Rule is set") 417 } 418 419 if p == nil { 420 p = &ec2.IpPermission{ 421 FromPort: aws.Int64(80), 422 ToPort: aws.Int64(8000), 423 IpProtocol: aws.String("tcp"), 424 IpRanges: []*ec2.IpRange{&ec2.IpRange{CidrIp: aws.String("10.0.0.0/8")}}, 425 } 426 } 427 428 var matchingRule *ec2.IpPermission 429 var rules []*ec2.IpPermission 430 if ruleType == "ingress" { 431 rules = group.IpPermissions 432 } else { 433 rules = group.IpPermissionsEgress 434 } 435 436 if len(rules) == 0 { 437 return fmt.Errorf("No IPPerms") 438 } 439 440 for _, r := range rules { 441 if r.ToPort != nil && *p.ToPort != *r.ToPort { 442 continue 443 } 444 445 if r.FromPort != nil && *p.FromPort != *r.FromPort { 446 continue 447 } 448 449 if r.IpProtocol != nil && *p.IpProtocol != *r.IpProtocol { 450 continue 451 } 452 453 remaining := len(p.IpRanges) 454 for _, ip := range p.IpRanges { 455 for _, rip := range r.IpRanges { 456 if *ip.CidrIp == *rip.CidrIp { 457 remaining-- 458 } 459 } 460 } 461 462 if remaining > 0 { 463 continue 464 } 465 466 remaining = len(p.UserIdGroupPairs) 467 for _, ip := range p.UserIdGroupPairs { 468 for _, rip := range r.UserIdGroupPairs { 469 if *ip.GroupId == *rip.GroupId { 470 remaining-- 471 } 472 } 473 } 474 475 if remaining > 0 { 476 continue 477 } 478 matchingRule = r 479 } 480 481 if matchingRule != nil { 482 log.Printf("[DEBUG] Matching rule found : %s", matchingRule) 483 return nil 484 } 485 486 return fmt.Errorf("Error here\n\tlooking for %s, wasn't found in %s", p, rules) 487 } 488 } 489 490 const testAccAWSSecurityGroupRuleIngressConfig = ` 491 resource "aws_security_group" "web" { 492 name = "terraform_acceptance_test_example" 493 description = "Used in the terraform acceptance tests" 494 495 tags { 496 Name = "tf-acc-test" 497 } 498 } 499 500 resource "aws_security_group_rule" "ingress_1" { 501 type = "ingress" 502 protocol = "tcp" 503 from_port = 80 504 to_port = 8000 505 cidr_blocks = ["10.0.0.0/8"] 506 507 security_group_id = "${aws_security_group.web.id}" 508 } 509 ` 510 511 const testAccAWSSecurityGroupRuleIngressClassicConfig = ` 512 provider "aws" { 513 region = "us-east-1" 514 } 515 516 resource "aws_security_group" "web" { 517 name = "terraform_acceptance_test_example" 518 description = "Used in the terraform acceptance tests" 519 520 tags { 521 Name = "tf-acc-test" 522 } 523 } 524 525 resource "aws_security_group_rule" "ingress_1" { 526 type = "ingress" 527 protocol = "tcp" 528 from_port = 80 529 to_port = 8000 530 cidr_blocks = ["10.0.0.0/8"] 531 532 security_group_id = "${aws_security_group.web.id}" 533 } 534 ` 535 536 const testAccAWSSecurityGroupRuleEgressConfig = ` 537 resource "aws_security_group" "web" { 538 name = "terraform_acceptance_test_example" 539 description = "Used in the terraform acceptance tests" 540 541 tags { 542 Name = "tf-acc-test" 543 } 544 } 545 546 resource "aws_security_group_rule" "egress_1" { 547 type = "egress" 548 protocol = "tcp" 549 from_port = 80 550 to_port = 8000 551 cidr_blocks = ["10.0.0.0/8"] 552 553 security_group_id = "${aws_security_group.web.id}" 554 } 555 ` 556 557 const testAccAWSSecurityGroupRuleConfigMultiIngress = ` 558 resource "aws_security_group" "web" { 559 name = "terraform_acceptance_test_example_2" 560 description = "Used in the terraform acceptance tests" 561 } 562 563 resource "aws_security_group" "worker" { 564 name = "terraform_acceptance_test_example_worker" 565 description = "Used in the terraform acceptance tests" 566 } 567 568 569 resource "aws_security_group_rule" "ingress_1" { 570 type = "ingress" 571 protocol = "tcp" 572 from_port = 22 573 to_port = 22 574 cidr_blocks = ["10.0.0.0/8"] 575 576 security_group_id = "${aws_security_group.web.id}" 577 } 578 579 resource "aws_security_group_rule" "ingress_2" { 580 type = "ingress" 581 protocol = "tcp" 582 from_port = 80 583 to_port = 8000 584 self = true 585 586 security_group_id = "${aws_security_group.web.id}" 587 } 588 ` 589 590 // check for GH-1985 regression 591 const testAccAWSSecurityGroupRuleConfigSelfReference = ` 592 provider "aws" { 593 region = "us-west-2" 594 } 595 596 resource "aws_vpc" "main" { 597 cidr_block = "10.0.0.0/16" 598 tags { 599 Name = "sg-self-test" 600 } 601 } 602 603 resource "aws_security_group" "web" { 604 name = "main" 605 vpc_id = "${aws_vpc.main.id}" 606 tags { 607 Name = "sg-self-test" 608 } 609 } 610 611 resource "aws_security_group_rule" "self" { 612 type = "ingress" 613 protocol = "-1" 614 from_port = 0 615 to_port = 0 616 self = true 617 security_group_id = "${aws_security_group.web.id}" 618 } 619 ` 620 621 const testAccAWSSecurityGroupRulePartialMatching = ` 622 resource "aws_vpc" "default" { 623 cidr_block = "10.0.0.0/16" 624 tags { 625 Name = "tf-sg-rule-bug" 626 } 627 } 628 629 resource "aws_security_group" "web" { 630 name = "tf-other" 631 vpc_id = "${aws_vpc.default.id}" 632 tags { 633 Name = "tf-other-sg" 634 } 635 } 636 637 resource "aws_security_group" "nat" { 638 name = "tf-nat" 639 vpc_id = "${aws_vpc.default.id}" 640 tags { 641 Name = "tf-nat-sg" 642 } 643 } 644 645 resource "aws_security_group_rule" "ingress" { 646 type = "ingress" 647 from_port = 80 648 to_port = 80 649 protocol = "tcp" 650 cidr_blocks = ["10.0.2.0/24", "10.0.3.0/24", "10.0.4.0/24"] 651 652 security_group_id = "${aws_security_group.web.id}" 653 } 654 655 resource "aws_security_group_rule" "other" { 656 type = "ingress" 657 from_port = 80 658 to_port = 80 659 protocol = "tcp" 660 cidr_blocks = ["10.0.5.0/24"] 661 662 security_group_id = "${aws_security_group.web.id}" 663 } 664 665 // same a above, but different group, to guard against bad hashing 666 resource "aws_security_group_rule" "nat_ingress" { 667 type = "ingress" 668 from_port = 80 669 to_port = 80 670 protocol = "tcp" 671 cidr_blocks = ["10.0.2.0/24", "10.0.3.0/24", "10.0.4.0/24"] 672 673 security_group_id = "${aws_security_group.nat.id}" 674 } 675 ` 676 677 const testAccAWSSecurityGroupRulePartialMatching_Source = ` 678 resource "aws_vpc" "default" { 679 cidr_block = "10.0.0.0/16" 680 tags { 681 Name = "tf-sg-rule-bug" 682 } 683 } 684 685 resource "aws_security_group" "web" { 686 name = "tf-other" 687 vpc_id = "${aws_vpc.default.id}" 688 tags { 689 Name = "tf-other-sg" 690 } 691 } 692 693 resource "aws_security_group" "nat" { 694 name = "tf-nat" 695 vpc_id = "${aws_vpc.default.id}" 696 tags { 697 Name = "tf-nat-sg" 698 } 699 } 700 701 resource "aws_security_group_rule" "source_ingress" { 702 type = "ingress" 703 from_port = 80 704 to_port = 80 705 protocol = "tcp" 706 707 source_security_group_id = "${aws_security_group.nat.id}" 708 security_group_id = "${aws_security_group.web.id}" 709 } 710 711 resource "aws_security_group_rule" "other_ingress" { 712 type = "ingress" 713 from_port = 80 714 to_port = 80 715 protocol = "tcp" 716 cidr_blocks = ["10.0.2.0/24", "10.0.3.0/24", "10.0.4.0/24"] 717 718 security_group_id = "${aws_security_group.web.id}" 719 } 720 `