Merge pull request #663 from svanharmelen/f-fix/change-set-logic

core: refactoring the way sets work internally v2
This commit is contained in:
Mitchell Hashimoto 2014-12-15 13:38:00 -08:00
commit 6a663796d5
17 changed files with 891 additions and 440 deletions

View File

@ -16,7 +16,7 @@ testacc: config/y.go
echo "ERROR: Set TEST to a specific package"; \ echo "ERROR: Set TEST to a specific package"; \
exit 1; \ exit 1; \
fi fi
TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 30m TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 45m
testrace: config/y.go testrace: config/y.go
TF_ACC= go test -race $(TEST) $(TESTARGS) TF_ACC= go test -race $(TEST) $(TESTARGS)

View File

@ -3,6 +3,7 @@ package aws
import ( import (
"fmt" "fmt"
"log" "log"
"strings"
"time" "time"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
@ -195,7 +196,7 @@ func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) e
d.Set("min_size", g.MinSize) d.Set("min_size", g.MinSize)
d.Set("max_size", g.MaxSize) d.Set("max_size", g.MaxSize)
d.Set("name", g.Name) d.Set("name", g.Name)
d.Set("vpc_zone_identifier", g.VPCZoneIdentifier) d.Set("vpc_zone_identifier", strings.Split(g.VPCZoneIdentifier, ","))
return nil return nil
} }

View File

@ -23,7 +23,7 @@ func TestAccAWSAutoScalingGroup_basic(t *testing.T) {
testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group), testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group),
testAccCheckAWSAutoScalingGroupAttributes(&group), testAccCheckAWSAutoScalingGroupAttributes(&group),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "availability_zones.0", "us-west-2a"), "aws_autoscaling_group.bar", "availability_zones.2487133097", "us-west-2a"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "name", "foobar3-terraform-test"), "aws_autoscaling_group.bar", "name", "foobar3-terraform-test"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
@ -39,7 +39,7 @@ func TestAccAWSAutoScalingGroup_basic(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "force_delete", "true"), "aws_autoscaling_group.bar", "force_delete", "true"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "termination_policies.0", "OldestInstance"), "aws_autoscaling_group.bar", "termination_policies.912102603", "OldestInstance"),
), ),
}, },
@ -205,7 +205,7 @@ resource "aws_autoscaling_group" "bar" {
desired_capacity = 4 desired_capacity = 4
force_delete = true force_delete = true
termination_policies = ["OldestInstance"] termination_policies = ["OldestInstance"]
launch_configuration = "${aws_launch_configuration.foobar.name}" launch_configuration = "${aws_launch_configuration.foobar.name}"
} }
` `

View File

@ -29,17 +29,17 @@ func TestAccAWSDBParameterGroup(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "description", "Test parameter group for terraform"), "aws_db_parameter_group.bar", "description", "Test parameter group for terraform"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.0.name", "character_set_results"), "aws_db_parameter_group.bar", "parameter.1708034931.name", "character_set_results"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.0.value", "utf8"), "aws_db_parameter_group.bar", "parameter.1708034931.value", "utf8"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.1.name", "character_set_server"), "aws_db_parameter_group.bar", "parameter.2421266705.name", "character_set_server"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.1.value", "utf8"), "aws_db_parameter_group.bar", "parameter.2421266705.value", "utf8"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.2.name", "character_set_client"), "aws_db_parameter_group.bar", "parameter.2478663599.name", "character_set_client"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.2.value", "utf8"), "aws_db_parameter_group.bar", "parameter.2478663599.value", "utf8"),
), ),
}, },
resource.TestStep{ resource.TestStep{
@ -54,25 +54,25 @@ func TestAccAWSDBParameterGroup(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "description", "Test parameter group for terraform"), "aws_db_parameter_group.bar", "description", "Test parameter group for terraform"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.0.name", "collation_connection"), "aws_db_parameter_group.bar", "parameter.1706463059.name", "collation_connection"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.0.value", "utf8_unicode_ci"), "aws_db_parameter_group.bar", "parameter.1706463059.value", "utf8_unicode_ci"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.1.name", "character_set_results"), "aws_db_parameter_group.bar", "parameter.1708034931.name", "character_set_results"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.1.value", "utf8"), "aws_db_parameter_group.bar", "parameter.1708034931.value", "utf8"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.2.name", "character_set_server"), "aws_db_parameter_group.bar", "parameter.2421266705.name", "character_set_server"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.2.value", "utf8"), "aws_db_parameter_group.bar", "parameter.2421266705.value", "utf8"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.3.name", "collation_server"), "aws_db_parameter_group.bar", "parameter.2475805061.name", "collation_server"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.3.value", "utf8_unicode_ci"), "aws_db_parameter_group.bar", "parameter.2475805061.value", "utf8_unicode_ci"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.4.name", "character_set_client"), "aws_db_parameter_group.bar", "parameter.2478663599.name", "character_set_client"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_db_parameter_group.bar", "parameter.4.value", "utf8"), "aws_db_parameter_group.bar", "parameter.2478663599.value", "utf8"),
), ),
}, },
}, },

View File

@ -34,15 +34,15 @@ func TestAccAWSELB_basic(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "availability_zones.2", "us-west-2c"), "aws_elb.bar", "availability_zones.2", "us-west-2c"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "listener.0.instance_port", "8000"), "aws_elb.bar", "listener.206423021.instance_port", "8000"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "listener.0.instance_protocol", "http"), "aws_elb.bar", "listener.206423021.instance_protocol", "http"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "listener.0.ssl_certificate_id", ssl_certificate_id), "aws_elb.bar", "listener.206423021.ssl_certificate_id", ssl_certificate_id),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "listener.0.lb_port", "80"), "aws_elb.bar", "listener.206423021.lb_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "listener.0.lb_protocol", "http"), "aws_elb.bar", "listener.206423021.lb_protocol", "http"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "cross_zone_load_balancing", "true"), "aws_elb.bar", "cross_zone_load_balancing", "true"),
), ),
@ -101,15 +101,15 @@ func TestAccAWSELB_HealthCheck(t *testing.T) {
testAccCheckAWSELBExists("aws_elb.bar", &conf), testAccCheckAWSELBExists("aws_elb.bar", &conf),
testAccCheckAWSELBAttributesHealthCheck(&conf), testAccCheckAWSELBAttributesHealthCheck(&conf),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "health_check.0.healthy_threshold", "5"), "aws_elb.bar", "health_check.3484319807.healthy_threshold", "5"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "health_check.0.unhealthy_threshold", "5"), "aws_elb.bar", "health_check.3484319807.unhealthy_threshold", "5"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "health_check.0.target", "HTTP:8000/"), "aws_elb.bar", "health_check.3484319807.target", "HTTP:8000/"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "health_check.0.timeout", "30"), "aws_elb.bar", "health_check.3484319807.timeout", "30"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elb.bar", "health_check.0.interval", "60"), "aws_elb.bar", "health_check.3484319807.interval", "60"),
), ),
}, },
}, },
@ -257,7 +257,6 @@ resource "aws_elb" "bar" {
lb_protocol = "http" lb_protocol = "http"
} }
instances = []
cross_zone_load_balancing = true cross_zone_load_balancing = true
} }
` `

View File

@ -24,29 +24,29 @@ func TestAccAWSNetworkAclsWithEgressAndIngressRules(t *testing.T) {
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckAWSNetworkAclExists("aws_network_acl.bar", &networkAcl), testAccCheckAWSNetworkAclExists("aws_network_acl.bar", &networkAcl),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.0.protocol", "tcp"), "aws_network_acl.bar", "ingress.580214135.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.0.rule_no", "1"), "aws_network_acl.bar", "ingress.580214135.rule_no", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.0.from_port", "80"), "aws_network_acl.bar", "ingress.580214135.from_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.0.to_port", "80"), "aws_network_acl.bar", "ingress.580214135.to_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.0.action", "allow"), "aws_network_acl.bar", "ingress.580214135.action", "allow"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.0.cidr_block", "10.3.10.3/18"), "aws_network_acl.bar", "ingress.580214135.cidr_block", "10.3.10.3/18"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.0.protocol", "tcp"), "aws_network_acl.bar", "egress.1730430240.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.0.rule_no", "2"), "aws_network_acl.bar", "egress.1730430240.rule_no", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.0.from_port", "443"), "aws_network_acl.bar", "egress.1730430240.from_port", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.0.to_port", "443"), "aws_network_acl.bar", "egress.1730430240.to_port", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.0.cidr_block", "10.3.2.3/18"), "aws_network_acl.bar", "egress.1730430240.cidr_block", "10.3.2.3/18"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.0.action", "allow"), "aws_network_acl.bar", "egress.1730430240.action", "allow"),
), ),
}, },
}, },
@ -67,17 +67,17 @@ func TestAccAWSNetworkAclsOnlyIngressRules(t *testing.T) {
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
// testAccCheckSubnetAssociation("aws_network_acl.foos", "aws_subnet.blob"), // testAccCheckSubnetAssociation("aws_network_acl.foos", "aws_subnet.blob"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.protocol", "tcp"), "aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.rule_no", "2"), "aws_network_acl.foos", "ingress.3697634361.rule_no", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.from_port", "443"), "aws_network_acl.foos", "ingress.3697634361.from_port", "0"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.to_port", "443"), "aws_network_acl.foos", "ingress.3697634361.to_port", "22"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.action", "deny"), "aws_network_acl.foos", "ingress.3697634361.action", "deny"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.cidr_block", "10.2.2.3/18"), "aws_network_acl.foos", "ingress.3697634361.cidr_block", "10.2.2.3/18"),
), ),
}, },
}, },
@ -98,23 +98,21 @@ func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) {
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
testIngressRuleLength(&networkAcl, 2), testIngressRuleLength(&networkAcl, 2),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.protocol", "tcp"), "aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.rule_no", "2"), "aws_network_acl.foos", "ingress.3697634361.rule_no", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.from_port", "443"), "aws_network_acl.foos", "ingress.3697634361.from_port", "0"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.to_port", "443"), "aws_network_acl.foos", "ingress.3697634361.to_port", "22"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.action", "deny"), "aws_network_acl.foos", "ingress.3697634361.action", "deny"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.cidr_block", "10.2.2.3/18"), "aws_network_acl.foos", "ingress.3697634361.cidr_block", "10.2.2.3/18"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.1.rule_no", "1"), "aws_network_acl.foos", "ingress.2438803013.from_port", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.1.from_port", "0"), "aws_network_acl.foos", "ingress.2438803013.rule_no", "2"),
resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.1.to_port", "22"),
), ),
}, },
resource.TestStep{ resource.TestStep{
@ -123,17 +121,17 @@ func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) {
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
testIngressRuleLength(&networkAcl, 1), testIngressRuleLength(&networkAcl, 1),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.protocol", "tcp"), "aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.rule_no", "1"), "aws_network_acl.foos", "ingress.3697634361.rule_no", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.from_port", "443"), "aws_network_acl.foos", "ingress.3697634361.from_port", "0"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.to_port", "443"), "aws_network_acl.foos", "ingress.3697634361.to_port", "22"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.action", "deny"), "aws_network_acl.foos", "ingress.3697634361.action", "deny"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.0.cidr_block", "10.2.2.3/18"), "aws_network_acl.foos", "ingress.3697634361.cidr_block", "10.2.2.3/18"),
), ),
}, },
}, },
@ -349,8 +347,8 @@ resource "aws_network_acl" "foos" {
rule_no = 1 rule_no = 1
action = "deny" action = "deny"
cidr_block = "10.2.2.3/18" cidr_block = "10.2.2.3/18"
from_port = 443 from_port = 0
to_port = 443 to_port = 22
} }
subnet_id = "${aws_subnet.blob.id}" subnet_id = "${aws_subnet.blob.id}"
} }

View File

@ -28,15 +28,15 @@ func TestAccAWSSecurityGroup_normal(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "description", "Used in the terraform acceptance tests"), "aws_security_group.web", "description", "Used in the terraform acceptance tests"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.protocol", "tcp"), "aws_security_group.web", "ingress.332851786.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.from_port", "80"), "aws_security_group.web", "ingress.332851786.from_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.to_port", "8000"), "aws_security_group.web", "ingress.332851786.to_port", "8000"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.cidr_blocks.#", "1"), "aws_security_group.web", "ingress.332851786.cidr_blocks.#", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.cidr_blocks.0", "10.0.0.0/8"), "aws_security_group.web", "ingress.332851786.cidr_blocks.0", "10.0.0.0/8"),
), ),
}, },
}, },
@ -74,13 +74,13 @@ func TestAccAWSSecurityGroup_self(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "description", "Used in the terraform acceptance tests"), "aws_security_group.web", "description", "Used in the terraform acceptance tests"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.protocol", "tcp"), "aws_security_group.web", "ingress.3128515109.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.from_port", "80"), "aws_security_group.web", "ingress.3128515109.from_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.to_port", "8000"), "aws_security_group.web", "ingress.3128515109.to_port", "8000"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.self", "true"), "aws_security_group.web", "ingress.3128515109.self", "true"),
checkSelf, checkSelf,
), ),
}, },
@ -114,15 +114,15 @@ func TestAccAWSSecurityGroup_vpc(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "description", "Used in the terraform acceptance tests"), "aws_security_group.web", "description", "Used in the terraform acceptance tests"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.protocol", "tcp"), "aws_security_group.web", "ingress.332851786.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.from_port", "80"), "aws_security_group.web", "ingress.332851786.from_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.to_port", "8000"), "aws_security_group.web", "ingress.332851786.to_port", "8000"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.cidr_blocks.#", "1"), "aws_security_group.web", "ingress.332851786.cidr_blocks.#", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_security_group.web", "ingress.0.cidr_blocks.0", "10.0.0.0/8"), "aws_security_group.web", "ingress.332851786.cidr_blocks.0", "10.0.0.0/8"),
testCheck, testCheck,
), ),
}, },
@ -355,124 +355,144 @@ func testAccCheckAWSSecurityGroupAttributesChanged(group *ec2.SecurityGroupInfo)
const testAccAWSSecurityGroupConfig = ` const testAccAWSSecurityGroupConfig = `
resource "aws_security_group" "web" { resource "aws_security_group" "web" {
name = "terraform_acceptance_test_example" name = "terraform_acceptance_test_example"
description = "Used in the terraform acceptance tests" description = "Used in the terraform acceptance tests"
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 80 from_port = 80
to_port = 8000 to_port = 8000
cidr_blocks = ["10.0.0.0/8"] cidr_blocks = ["10.0.0.0/8"]
} }
} }
` `
const testAccAWSSecurityGroupConfigChange = ` const testAccAWSSecurityGroupConfigChange = `
resource "aws_security_group" "web" { resource "aws_security_group" "web" {
name = "terraform_acceptance_test_example" name = "terraform_acceptance_test_example"
description = "Used in the terraform acceptance tests" description = "Used in the terraform acceptance tests"
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 80 from_port = 80
to_port = 9000 to_port = 9000
cidr_blocks = ["10.0.0.0/8"] cidr_blocks = ["10.0.0.0/8"]
} }
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 80 from_port = 80
to_port = 8000 to_port = 8000
cidr_blocks = ["10.0.0.0/8", "0.0.0.0/0"] cidr_blocks = ["0.0.0.0/0", "10.0.0.0/8"]
} }
} }
` `
const testAccAWSSecurityGroupConfigSelf = ` const testAccAWSSecurityGroupConfigSelf = `
resource "aws_security_group" "web" { resource "aws_security_group" "web" {
name = "terraform_acceptance_test_example" name = "terraform_acceptance_test_example"
description = "Used in the terraform acceptance tests" description = "Used in the terraform acceptance tests"
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 80 from_port = 80
to_port = 8000 to_port = 8000
self = true self = true
} }
} }
` `
const testAccAWSSecurityGroupConfigVpc = ` const testAccAWSSecurityGroupConfigVpc = `
resource "aws_vpc" "foo" { resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
} }
resource "aws_security_group" "web" { resource "aws_security_group" "web" {
name = "terraform_acceptance_test_example" name = "terraform_acceptance_test_example"
description = "Used in the terraform acceptance tests" description = "Used in the terraform acceptance tests"
vpc_id = "${aws_vpc.foo.id}" vpc_id = "${aws_vpc.foo.id}"
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 80 from_port = 80
to_port = 8000 to_port = 8000
cidr_blocks = ["10.0.0.0/8"] cidr_blocks = ["10.0.0.0/8"]
} }
} }
` `
const testAccAWSSecurityGroupConfigMultiIngress = ` const testAccAWSSecurityGroupConfigMultiIngress = `
resource "aws_security_group" "worker" { resource "aws_security_group" "worker" {
name = "terraform_acceptance_test_example_1" name = "terraform_acceptance_test_example_1"
description = "Used in the terraform acceptance tests" description = "Used in the terraform acceptance tests"
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 80 from_port = 80
to_port = 8000 to_port = 8000
cidr_blocks = ["10.0.0.0/8"] cidr_blocks = ["10.0.0.0/8"]
} }
} }
resource "aws_security_group" "web" { resource "aws_security_group" "web" {
name = "terraform_acceptance_test_example_2" name = "terraform_acceptance_test_example_2"
description = "Used in the terraform acceptance tests" description = "Used in the terraform acceptance tests"
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 22 from_port = 22
to_port = 22 to_port = 22
cidr_blocks = ["10.0.0.0/8"] cidr_blocks = ["10.0.0.0/8"]
} }
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 800 from_port = 800
to_port = 800 to_port = 800
cidr_blocks = ["10.0.0.0/8"] cidr_blocks = ["10.0.0.0/8"]
} }
ingress { ingress {
protocol = "tcp" protocol = "tcp"
from_port = 80 from_port = 80
to_port = 8000 to_port = 8000
security_groups = ["${aws_security_group.worker.id}"] security_groups = ["${aws_security_group.worker.id}"]
} }
} }
` `
const testAccAWSSecurityGroupConfigTags = ` const testAccAWSSecurityGroupConfigTags = `
resource "aws_security_group" "foo" { resource "aws_security_group" "foo" {
tags { name = "terraform_acceptance_test_example"
foo = "bar" description = "Used in the terraform acceptance tests"
}
ingress {
protocol = "tcp"
from_port = 80
to_port = 8000
cidr_blocks = ["10.0.0.0/8"]
}
tags {
foo = "bar"
}
} }
` `
const testAccAWSSecurityGroupConfigTagsUpdate = ` const testAccAWSSecurityGroupConfigTagsUpdate = `
resource "aws_security_group" "foo" { resource "aws_security_group" "foo" {
tags { name = "terraform_acceptance_test_example"
bar = "baz" description = "Used in the terraform acceptance tests"
}
ingress {
protocol = "tcp"
from_port = 80
to_port = 8000
cidr_blocks = ["10.0.0.0/8"]
}
tags {
bar = "baz"
}
} }
` `

View File

@ -23,22 +23,21 @@ func TestAccCloudStackFirewall_basic(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "ipaddress", CLOUDSTACK_PUBLIC_IPADDRESS), "cloudstack_firewall.foo", "ipaddress", CLOUDSTACK_PUBLIC_IPADDRESS),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.source_cidr", "10.0.0.0/24"), "cloudstack_firewall.foo", "rule.1702320581.source_cidr", "10.0.0.0/24"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.protocol", "tcp"), "cloudstack_firewall.foo", "rule.1702320581.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.#", "2"), "cloudstack_firewall.foo", "rule.1702320581.ports.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.0", "1000-2000"), "cloudstack_firewall.foo", "rule.1702320581.ports.1209010669", "1000-2000"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.1", "80"), "cloudstack_firewall.foo", "rule.1702320581.ports.1889509032", "80"),
), ),
}, },
}, },
}) })
} }
/*
func TestAccCloudStackFirewall_update(t *testing.T) { func TestAccCloudStackFirewall_update(t *testing.T) {
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -54,15 +53,15 @@ func TestAccCloudStackFirewall_update(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.#", "1"), "cloudstack_firewall.foo", "rule.#", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.source_cidr", "10.0.0.0/24"), "cloudstack_firewall.foo", "rule.1702320581.source_cidr", "10.0.0.0/24"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.protocol", "tcp"), "cloudstack_firewall.foo", "rule.1702320581.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.#", "2"), "cloudstack_firewall.foo", "rule.1702320581.ports.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.0", "1000-2000"), "cloudstack_firewall.foo", "rule.1702320581.ports.1209010669", "1000-2000"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.1", "80"), "cloudstack_firewall.foo", "rule.1702320581.ports.1889509032", "80"),
), ),
}, },
@ -75,31 +74,30 @@ func TestAccCloudStackFirewall_update(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.#", "2"), "cloudstack_firewall.foo", "rule.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.source_cidr", "10.0.0.0/24"), "cloudstack_firewall.foo", "rule.1702320581.source_cidr", "10.0.0.0/24"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.protocol", "tcp"), "cloudstack_firewall.foo", "rule.1702320581.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.#", "2"), "cloudstack_firewall.foo", "rule.1702320581.ports.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.0", "1000-2000"), "cloudstack_firewall.foo", "rule.1702320581.ports.1209010669", "1000-2000"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.0.ports.1", "80"), "cloudstack_firewall.foo", "rule.1702320581.ports.1889509032", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.1.source_cidr", "172.16.100.0/24"), "cloudstack_firewall.foo", "rule.3779782959.source_cidr", "172.16.100.0/24"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.1.protocol", "tcp"), "cloudstack_firewall.foo", "rule.3779782959.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.1.ports.#", "2"), "cloudstack_firewall.foo", "rule.3779782959.ports.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.1.ports.0", "80"), "cloudstack_firewall.foo", "rule.3779782959.ports.1889509032", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_firewall.foo", "rule.1.ports.1", "443"), "cloudstack_firewall.foo", "rule.3779782959.ports.3638101695", "443"),
), ),
}, },
}, },
}) })
} }
*/
func testAccCheckCloudStackFirewallRulesExist(n string) resource.TestCheckFunc { func testAccCheckCloudStackFirewallRulesExist(n string) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {

View File

@ -23,26 +23,25 @@ func TestAccCloudStackNetworkACLRule_basic(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.#", "1"), "cloudstack_network_acl_rule.foo", "rule.#", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.action", "allow"), "cloudstack_network_acl_rule.foo", "rule.3247834462.action", "allow"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.source_cidr", "172.16.100.0/24"), "cloudstack_network_acl_rule.foo", "rule.3247834462.source_cidr", "172.16.100.0/24"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"), "cloudstack_network_acl_rule.foo", "rule.3247834462.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.0", "80"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.1889509032", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.1", "443"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.3638101695", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"), "cloudstack_network_acl_rule.foo", "rule.3247834462.traffic_type", "ingress"),
), ),
}, },
}, },
}) })
} }
/*
func TestAccCloudStackNetworkACLRule_update(t *testing.T) { func TestAccCloudStackNetworkACLRule_update(t *testing.T) {
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -56,19 +55,19 @@ func TestAccCloudStackNetworkACLRule_update(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.#", "1"), "cloudstack_network_acl_rule.foo", "rule.#", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.action", "allow"), "cloudstack_network_acl_rule.foo", "rule.3247834462.action", "allow"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.source_cidr", "172.16.100.0/24"), "cloudstack_network_acl_rule.foo", "rule.3247834462.source_cidr", "172.16.100.0/24"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"), "cloudstack_network_acl_rule.foo", "rule.3247834462.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.0", "80"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.1889509032", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.1", "443"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.3638101695", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"), "cloudstack_network_acl_rule.foo", "rule.3247834462.traffic_type", "ingress"),
), ),
}, },
@ -79,39 +78,38 @@ func TestAccCloudStackNetworkACLRule_update(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.#", "2"), "cloudstack_network_acl_rule.foo", "rule.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.action", "allow"), "cloudstack_network_acl_rule.foo", "rule.3247834462.action", "allow"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.source_cidr", "172.16.100.0/24"), "cloudstack_network_acl_rule.foo", "rule.3247834462.source_cidr", "172.16.100.0/24"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"), "cloudstack_network_acl_rule.foo", "rule.3247834462.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.0", "80"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.1889509032", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.ports.1", "443"), "cloudstack_network_acl_rule.foo", "rule.3247834462.ports.3638101695", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"), "cloudstack_network_acl_rule.foo", "rule.3247834462.traffic_type", "ingress"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.1.action", "deny"), "cloudstack_network_acl_rule.foo", "rule.4267872693.action", "deny"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.1.source_cidr", "10.0.0.0/24"), "cloudstack_network_acl_rule.foo", "rule.4267872693.source_cidr", "10.0.0.0/24"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.1.protocol", "tcp"), "cloudstack_network_acl_rule.foo", "rule.4267872693.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.1.ports.#", "2"), "cloudstack_network_acl_rule.foo", "rule.4267872693.ports.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.1.ports.0", "1000-2000"), "cloudstack_network_acl_rule.foo", "rule.4267872693.ports.1209010669", "1000-2000"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.1.ports.1", "80"), "cloudstack_network_acl_rule.foo", "rule.4267872693.ports.1889509032", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_network_acl_rule.foo", "rule.1.traffic_type", "engress"), "cloudstack_network_acl_rule.foo", "rule.4267872693.traffic_type", "engress"),
), ),
}, },
}, },
}) })
} }
*/
func testAccCheckCloudStackNetworkACLRulesExist(n string) resource.TestCheckFunc { func testAccCheckCloudStackNetworkACLRulesExist(n string) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {

View File

@ -23,20 +23,19 @@ func TestAccCloudStackPortForward_basic(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "ipaddress", CLOUDSTACK_PUBLIC_IPADDRESS), "cloudstack_port_forward.foo", "ipaddress", CLOUDSTACK_PUBLIC_IPADDRESS),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.protocol", "tcp"), "cloudstack_port_forward.foo", "forward.1537694805.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.private_port", "443"), "cloudstack_port_forward.foo", "forward.1537694805.private_port", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.public_port", "8443"), "cloudstack_port_forward.foo", "forward.1537694805.public_port", "8443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.virtual_machine", "terraform-test"), "cloudstack_port_forward.foo", "forward.1537694805.virtual_machine", "terraform-test"),
), ),
}, },
}, },
}) })
} }
/*
func TestAccCloudStackPortForward_update(t *testing.T) { func TestAccCloudStackPortForward_update(t *testing.T) {
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -52,13 +51,13 @@ func TestAccCloudStackPortForward_update(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.#", "1"), "cloudstack_port_forward.foo", "forward.#", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.protocol", "tcp"), "cloudstack_port_forward.foo", "forward.1537694805.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.private_port", "443"), "cloudstack_port_forward.foo", "forward.1537694805.private_port", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.public_port", "8443"), "cloudstack_port_forward.foo", "forward.1537694805.public_port", "8443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.virtual_machine", "terraform-test"), "cloudstack_port_forward.foo", "forward.1537694805.virtual_machine", "terraform-test"),
), ),
}, },
@ -71,27 +70,26 @@ func TestAccCloudStackPortForward_update(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.#", "2"), "cloudstack_port_forward.foo", "forward.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.protocol", "tcp"), "cloudstack_port_forward.foo", "forward.8416686.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.private_port", "80"), "cloudstack_port_forward.foo", "forward.8416686.private_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.public_port", "8080"), "cloudstack_port_forward.foo", "forward.8416686.public_port", "8080"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.0.virtual_machine", "terraform-test"), "cloudstack_port_forward.foo", "forward.8416686.virtual_machine", "terraform-test"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.1.protocol", "tcp"), "cloudstack_port_forward.foo", "forward.1537694805.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.1.private_port", "443"), "cloudstack_port_forward.foo", "forward.1537694805.private_port", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.1.public_port", "8443"), "cloudstack_port_forward.foo", "forward.1537694805.public_port", "8443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"cloudstack_port_forward.foo", "forward.1.virtual_machine", "terraform-test"), "cloudstack_port_forward.foo", "forward.1537694805.virtual_machine", "terraform-test"),
), ),
}, },
}, },
}) })
} }
*/
func testAccCheckCloudStackPortForwardsExist(n string) resource.TestCheckFunc { func testAccCheckCloudStackPortForwardsExist(n string) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
@ -157,12 +155,10 @@ func testAccCheckCloudStackPortForwardDestroy(s *terraform.State) error {
var testAccCloudStackPortForward_basic = fmt.Sprintf(` var testAccCloudStackPortForward_basic = fmt.Sprintf(`
resource "cloudstack_instance" "foobar" { resource "cloudstack_instance" "foobar" {
name = "terraform-test" name = "terraform-test"
display_name = "terraform"
service_offering= "%s" service_offering= "%s"
network = "%s" network = "%s"
template = "%s" template = "%s"
zone = "%s" zone = "%s"
user_data = "foobar\nfoo\nbar"
expunge = true expunge = true
} }
@ -185,12 +181,10 @@ resource "cloudstack_port_forward" "foo" {
var testAccCloudStackPortForward_update = fmt.Sprintf(` var testAccCloudStackPortForward_update = fmt.Sprintf(`
resource "cloudstack_instance" "foobar" { resource "cloudstack_instance" "foobar" {
name = "terraform-test" name = "terraform-test"
display_name = "terraform"
service_offering= "%s" service_offering= "%s"
network = "%s" network = "%s"
template = "%s" template = "%s"
zone = "%s" zone = "%s"
user_data = "foobar\nfoo\nbar"
expunge = true expunge = true
} }

View File

@ -113,6 +113,25 @@ func (d *ResourceData) HasChange(key string) bool {
return !reflect.DeepEqual(o, n) return !reflect.DeepEqual(o, n)
} }
// hasComputedSubKeys walks true a schema and returns whether or not the
// given key contains any subkeys that are computed.
func (d *ResourceData) hasComputedSubKeys(key string, schema *Schema) bool {
prefix := key + "."
switch t := schema.Elem.(type) {
case *Resource:
for k, schema := range t.Schema {
if d.config.IsComputed(prefix + k) {
return true
}
if d.hasComputedSubKeys(prefix+k, schema) {
return true
}
}
}
return false
}
// Partial turns partial state mode on/off. // Partial turns partial state mode on/off.
// //
// When partial state mode is enabled, then only key prefixes specified // When partial state mode is enabled, then only key prefixes specified
@ -285,101 +304,178 @@ func (d *ResourceData) getSet(
source getSource) getResult { source getSource) getResult {
s := &Set{F: schema.Set} s := &Set{F: schema.Set}
result := getResult{Schema: schema, Value: s} result := getResult{Schema: schema, Value: s}
prefix := k + "."
// Get the list. For sets, the entire source must be exact: the // Get the set. For sets, the entire source must be exact: the
// entire set must come from set, diff, state, etc. So we go backwards // entire set must come from set, diff, state, etc. So we go backwards
// and once we get a result, we take it. Or, we never get a result. // and once we get a result, we take it. Or, we never get a result.
var raw getResult var indexMap map[int]int
codes := make(map[string]int)
sourceLevel := source & getSourceLevelMask sourceLevel := source & getSourceLevelMask
sourceFlags := source & ^getSourceLevelMask sourceFlags := source & ^getSourceLevelMask
for listSource := sourceLevel; listSource > 0; listSource >>= 1 { sourceDiff := sourceFlags&getSourceDiff != 0
for setSource := sourceLevel; setSource > 0; setSource >>= 1 {
// If we're already asking for an exact source and it doesn't // If we're already asking for an exact source and it doesn't
// match, then leave since the original source was the match. // match, then leave since the original source was the match.
if sourceFlags&getSourceExact != 0 && listSource != sourceLevel { if sourceFlags&getSourceExact != 0 && setSource != sourceLevel {
break break
} }
// The source we get from is the level we're on, plus the flags if d.config != nil && setSource == getSourceConfig {
// we had, plus the exact flag. raw := d.getList(k, nil, schema, setSource)
getSource := listSource // If the entire list is computed, then the entire set is
getSource |= sourceFlags // necessarilly computed.
getSource |= getSourceExact if raw.Computed {
raw = d.getList(k, nil, schema, getSource) result.Computed = true
if raw.Exists { if len(parts) > 0 {
break
}
return result
}
if raw.Exists {
result.Exists = true
list := raw.Value.([]interface{})
indexMap = make(map[int]int, len(list))
// Build the set from all the items using the given hash code
for i, v := range list {
code := s.add(v)
// Check if any of the keys in this item are computed
computed := false
if len(d.config.ComputedKeys) > 0 {
prefix := fmt.Sprintf("%s.%d", k, i)
computed = d.hasComputedSubKeys(prefix, schema)
}
// Check if we are computed and if so negatate the hash to
// this is a approximate hash
if computed {
s.m[-code] = s.m[code]
delete(s.m, code)
code = -code
}
indexMap[code] = i
}
break
}
}
if d.state != nil && setSource == getSourceState {
for k, _ := range d.state.Attributes {
if !strings.HasPrefix(k, prefix) || strings.HasPrefix(k, prefix+"#") {
continue
}
parts := strings.Split(k[len(prefix):], ".")
idx := parts[0]
if _, ok := codes[idx]; ok {
continue
}
code, err := strconv.Atoi(strings.Replace(parts[0], "~", "-", -1))
if err != nil {
panic(fmt.Sprintf("unable to convert %s to int: %v", idx, err))
}
codes[idx] = code
}
}
if d.setMap != nil && setSource == getSourceSet {
for k, _ := range d.setMap {
if !strings.HasPrefix(k, prefix) || strings.HasPrefix(k, prefix+"#") {
continue
}
parts := strings.Split(k[len(prefix):], ".")
idx := parts[0]
if _, ok := codes[idx]; ok {
continue
}
code, err := strconv.Atoi(strings.Replace(parts[0], "~", "-", -1))
if err != nil {
panic(fmt.Sprintf("unable to convert %s to int: %v", idx, err))
}
codes[idx] = code
}
}
if d.diff != nil && sourceDiff {
for k, _ := range d.diff.Attributes {
if !strings.HasPrefix(k, prefix) || strings.HasPrefix(k, prefix+"#") {
continue
}
parts := strings.Split(k[len(prefix):], ".")
idx := parts[0]
if _, ok := codes[idx]; ok {
continue
}
code, err := strconv.Atoi(strings.Replace(parts[0], "~", "-", -1))
if err != nil {
panic(fmt.Sprintf("unable to convert %s to int: %v", idx, err))
}
codes[idx] = code
}
}
if len(codes) > 0 {
break break
} }
} }
if !raw.Exists {
if len(parts) > 0 { if indexMap == nil {
return d.getList(k, parts, schema, source) s.m = make(map[int]interface{})
for idx, code := range codes {
switch t := schema.Elem.(type) {
case *Schema:
// Get a single value
s.m[code] = d.get(prefix+idx, nil, t, source).Value
result.Exists = true
case *Resource:
// Get the entire object
m := make(map[string]interface{})
for field, _ := range t.Schema {
m[field] = d.getObject(prefix+idx, []string{field}, t.Schema, source).Value
}
s.m[code] = m
result.Exists = true
}
} }
return result
} }
// If the entire list is computed, then the entire set is
// necessarilly computed.
if raw.Computed {
result.Computed = true
return result
}
list := raw.Value.([]interface{})
if len(list) == 0 {
if len(parts) > 0 {
return d.getList(k, parts, schema, source)
}
result.Exists = raw.Exists
return result
}
// This is a reverse map of hash code => index in config used to
// resolve direct set item lookup for turning into state. Confused?
// Read on...
//
// To create the state (the state* functions), a Get call is done
// with a full key such as "ports.0". The index of a set ("0") doesn't
// make a lot of sense, but we need to deterministically list out
// elements of the set like this. Luckily, same sets have a deterministic
// List() output, so we can use that to look things up.
//
// This mapping makes it so that we can look up the hash code of an
// object back to its index in the REAL config.
var indexMap map[int]int
if len(parts) > 0 { if len(parts) > 0 {
indexMap = make(map[int]int) // We still have parts left over meaning we're accessing an
} // element of this set.
idx := parts[0]
parts = parts[1:]
// Build the set from all the items using the given hash code // Special case if we're accessing the count of the set
for i, v := range list { if idx == "#" {
code := s.add(v) schema := &Schema{Type: TypeInt}
if indexMap != nil { return d.get(prefix+"#", parts, schema, source)
indexMap[code] = i }
if source&getSourceLevelMask == getSourceConfig {
i, err := strconv.Atoi(strings.Replace(idx, "~", "-", -1))
if err != nil {
panic(fmt.Sprintf("unable to convert %s to int: %v", idx, err))
}
if i, ok := indexMap[i]; ok {
idx = strconv.Itoa(i)
}
}
switch t := schema.Elem.(type) {
case *Schema:
return d.get(prefix+idx, parts, t, source)
case *Resource:
return d.getObject(prefix+idx, parts, t.Schema, source)
} }
} }
// If we're trying to get a specific element, then rewrite the
// index to be just that, then jump direct to getList.
if len(parts) > 0 {
index := parts[0]
indexInt, err := strconv.ParseInt(index, 0, 0)
if err != nil {
return getResultEmpty
}
codes := s.listCode()
if int(indexInt) >= len(codes) {
return getResultEmpty
}
code := codes[indexInt]
realIndex := indexMap[code]
parts[0] = strconv.FormatInt(int64(realIndex), 10)
return d.getList(k, parts, schema, source)
}
result.Exists = true
return result return result
} }
@ -916,8 +1012,13 @@ func (d *ResourceData) setSet(
// If it is a slice, then we have to turn it into a *Set so that // If it is a slice, then we have to turn it into a *Set so that
// we get the proper order back based on the hash code. // we get the proper order back based on the hash code.
if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { if v := reflect.ValueOf(value); v.Kind() == reflect.Slice {
// Build a temp *ResourceData to use for the conversion
tempD := &ResourceData{
setMap: make(map[string]string),
}
// Set the entire list, this lets us get sane values out of it // Set the entire list, this lets us get sane values out of it
if err := d.setList(k, nil, schema, value); err != nil { if err := tempD.setList(k, nil, schema, value); err != nil {
return err return err
} }
@ -930,7 +1031,7 @@ func (d *ResourceData) setSet(
source := getSourceSet | getSourceExact source := getSourceSet | getSourceExact
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
is := strconv.FormatInt(int64(i), 10) is := strconv.FormatInt(int64(i), 10)
result := d.getList(k, []string{is}, schema, source) result := tempD.getList(k, []string{is}, schema, source)
if !result.Exists { if !result.Exists {
panic("just set item doesn't exist") panic("just set item doesn't exist")
} }
@ -941,11 +1042,32 @@ func (d *ResourceData) setSet(
value = s value = s
} }
if s, ok := value.(*Set); ok { switch t := schema.Elem.(type) {
value = s.List() case *Schema:
for code, elem := range value.(*Set).m {
subK := fmt.Sprintf("%s.%d", k, code)
err := d.set(subK, nil, t, elem)
if err != nil {
return err
}
}
case *Resource:
for code, elem := range value.(*Set).m {
for field, _ := range t.Schema {
subK := fmt.Sprintf("%s.%d", k, code)
err := d.setObject(
subK, []string{field}, t.Schema, elem.(map[string]interface{})[field])
if err != nil {
return err
}
}
}
default:
return fmt.Errorf("%s: unknown element type (internal)", k)
} }
return d.setList(k, nil, schema, value) d.setMap[k+".#"] = strconv.Itoa(value.(*Set).Len())
return nil
} }
func (d *ResourceData) stateList( func (d *ResourceData) stateList(
@ -1071,18 +1193,18 @@ func (d *ResourceData) stateSet(
} }
set := raw.Value.(*Set) set := raw.Value.(*Set)
list := set.List()
result := make(map[string]string) result := make(map[string]string)
result[prefix+".#"] = strconv.FormatInt(int64(len(list)), 10) result[prefix+".#"] = strconv.Itoa(set.Len())
for i := 0; i < len(list); i++ {
key := fmt.Sprintf("%s.%d", prefix, i) for _, idx := range set.listCode() {
key := fmt.Sprintf("%s.%d", prefix, idx)
var m map[string]string var m map[string]string
switch t := schema.Elem.(type) { switch t := schema.Elem.(type) {
case *Resource:
m = d.stateObject(key, t.Schema)
case *Schema: case *Schema:
m = d.stateSingle(key, t) m = d.stateSingle(key, t)
case *Resource:
m = d.stateObject(key, t.Schema)
} }
for k, v := range m { for k, v := range m {

View File

@ -15,6 +15,7 @@ func TestResourceDataGet(t *testing.T) {
Key string Key string
Value interface{} Value interface{}
}{ }{
// #0
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -41,6 +42,7 @@ func TestResourceDataGet(t *testing.T) {
Value: "", Value: "",
}, },
// #1
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -68,6 +70,7 @@ func TestResourceDataGet(t *testing.T) {
Value: "foo", Value: "foo",
}, },
// #2
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -94,6 +97,7 @@ func TestResourceDataGet(t *testing.T) {
Value: "foo", Value: "foo",
}, },
// #3
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -117,6 +121,7 @@ func TestResourceDataGet(t *testing.T) {
Value: "bar", Value: "bar",
}, },
// #4
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -147,6 +152,7 @@ func TestResourceDataGet(t *testing.T) {
Value: "", Value: "",
}, },
// #5
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"port": &Schema{ "port": &Schema{
@ -170,6 +176,7 @@ func TestResourceDataGet(t *testing.T) {
Value: 80, Value: 80,
}, },
// #6
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -193,6 +200,7 @@ func TestResourceDataGet(t *testing.T) {
Value: 2, Value: 2,
}, },
// #7
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -216,6 +224,7 @@ func TestResourceDataGet(t *testing.T) {
Value: 3, Value: 3,
}, },
// #8
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -232,6 +241,7 @@ func TestResourceDataGet(t *testing.T) {
Value: 0, Value: 0,
}, },
// #9
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -255,6 +265,7 @@ func TestResourceDataGet(t *testing.T) {
Value: []interface{}{1, 2, 5}, Value: []interface{}{1, 2, 5},
}, },
// #10
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ingress": &Schema{ "ingress": &Schema{
@ -293,6 +304,7 @@ func TestResourceDataGet(t *testing.T) {
}, },
}, },
// #11
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ingress": &Schema{ "ingress": &Schema{
@ -333,7 +345,7 @@ func TestResourceDataGet(t *testing.T) {
}, },
}, },
// Computed get // #12 Computed get
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -353,7 +365,7 @@ func TestResourceDataGet(t *testing.T) {
Value: "foo", Value: "foo",
}, },
// Full object // #13 Full object
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -383,7 +395,7 @@ func TestResourceDataGet(t *testing.T) {
}, },
}, },
// List of maps // #14 List of maps
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"config_vars": &Schema{ "config_vars": &Schema{
@ -427,7 +439,7 @@ func TestResourceDataGet(t *testing.T) {
}, },
}, },
// List of maps in state // #15 List of maps in state
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"config_vars": &Schema{ "config_vars": &Schema{
@ -462,7 +474,7 @@ func TestResourceDataGet(t *testing.T) {
}, },
}, },
// List of maps with removal in diff // #16 List of maps with removal in diff
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"config_vars": &Schema{ "config_vars": &Schema{
@ -500,7 +512,7 @@ func TestResourceDataGet(t *testing.T) {
Value: []interface{}{}, Value: []interface{}{},
}, },
// Sets // #17 Sets
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -516,8 +528,8 @@ func TestResourceDataGet(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "1", "ports.#": "1",
"ports.0": "80", "ports.80": "80",
}, },
}, },
@ -528,6 +540,7 @@ func TestResourceDataGet(t *testing.T) {
Value: []interface{}{80}, Value: []interface{}{80},
}, },
// #18
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"data": &Schema{ "data": &Schema{
@ -555,15 +568,15 @@ func TestResourceDataGet(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"data.#": "1", "data.#": "1",
"data.0.index": "10", "data.10.index": "10",
"data.0.value": "50", "data.10.value": "50",
}, },
}, },
Diff: &terraform.InstanceDiff{ Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{ Attributes: map[string]*terraform.ResourceAttrDiff{
"data.0.value": &terraform.ResourceAttrDiff{ "data.10.value": &terraform.ResourceAttrDiff{
Old: "50", Old: "50",
New: "80", New: "80",
}, },
@ -1397,10 +1410,10 @@ func TestResourceDataSet(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "3", "ports.#": "3",
"ports.0": "100", "ports.100": "100",
"ports.1": "80", "ports.80": "80",
"ports.2": "80", "ports.81": "81",
}, },
}, },
@ -1432,13 +1445,13 @@ func TestResourceDataSet(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "2", "ports.#": "2",
"ports.0": "100", "ports.100": "100",
"ports.1": "80", "ports.80": "80",
}, },
}, },
Key: "ports.0", Key: "ports.100",
Value: 256, Value: 256,
Err: true, Err: true,
@ -1821,10 +1834,10 @@ func TestResourceDataState(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "3", "ports.#": "3",
"ports.0": "100", "ports.100": "100",
"ports.1": "80", "ports.80": "80",
"ports.2": "80", "ports.81": "81",
}, },
}, },
@ -1832,9 +1845,10 @@ func TestResourceDataState(t *testing.T) {
Result: &terraform.InstanceState{ Result: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "2", "ports.#": "3",
"ports.0": "80", "ports.80": "80",
"ports.1": "100", "ports.81": "81",
"ports.100": "100",
}, },
}, },
}, },
@ -1862,9 +1876,9 @@ func TestResourceDataState(t *testing.T) {
Result: &terraform.InstanceState{ Result: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "2", "ports.#": "2",
"ports.0": "80", "ports.80": "80",
"ports.1": "100", "ports.100": "100",
}, },
}, },
}, },
@ -1901,13 +1915,13 @@ func TestResourceDataState(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "2", "ports.#": "2",
"ports.0.order": "10", "ports.10.order": "10",
"ports.0.a.#": "1", "ports.10.a.#": "1",
"ports.0.a.0": "80", "ports.10.a.0": "80",
"ports.1.order": "20", "ports.20.order": "20",
"ports.1.b.#": "1", "ports.20.b.#": "1",
"ports.1.b.0": "100", "ports.20.b.0": "100",
}, },
}, },
@ -1926,13 +1940,13 @@ func TestResourceDataState(t *testing.T) {
Result: &terraform.InstanceState{ Result: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "2", "ports.#": "2",
"ports.0.order": "10", "ports.10.order": "10",
"ports.0.a.#": "1", "ports.10.a.#": "1",
"ports.0.a.0": "80", "ports.10.a.0": "80",
"ports.1.order": "20", "ports.20.order": "20",
"ports.1.b.#": "1", "ports.20.b.#": "1",
"ports.1.b.0": "100", "ports.20.b.0": "100",
}, },
}, },
}, },
@ -2160,16 +2174,16 @@ func TestResourceDataState(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "3", "ports.#": "3",
"ports.0": "100", "ports.100": "100",
"ports.1": "80", "ports.80": "80",
"ports.2": "80", "ports.81": "81",
}, },
}, },
Diff: &terraform.InstanceDiff{ Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{ Attributes: map[string]*terraform.ResourceAttrDiff{
"ports.1": &terraform.ResourceAttrDiff{ "ports.120": &terraform.ResourceAttrDiff{
New: "120", New: "120",
}, },
}, },
@ -2179,9 +2193,10 @@ func TestResourceDataState(t *testing.T) {
Result: &terraform.InstanceState{ Result: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "2", "ports.#": "3",
"ports.0": "80", "ports.80": "80",
"ports.1": "100", "ports.81": "81",
"ports.100": "100",
}, },
}, },
}, },

View File

@ -618,25 +618,109 @@ func (m schemaMap) diffSet(
diff *terraform.InstanceDiff, diff *terraform.InstanceDiff,
d *ResourceData, d *ResourceData,
all bool) error { all bool) error {
if !all { o, n, _, computedSet := d.diffChange(k)
// This is a bit strange, but we expect the entire set to be in the diff, nSet := n != nil
// so we first diff the set normally but with a new diff. Then, if
// there IS any change, we just set the change to the entire list. // If we have an old value and no new value is set or will be
tempD := new(terraform.InstanceDiff) // computed once all variables can be interpolated and we're
tempD.Attributes = make(map[string]*terraform.ResourceAttrDiff) // computed, then nothing has changed.
if err := m.diffList(k, schema, tempD, d, false); err != nil { if o != nil && n == nil && !computedSet && schema.Computed {
return err return nil
}
if o == nil {
o = &Set{F: schema.Set}
}
if n == nil {
n = &Set{F: schema.Set}
}
os := o.(*Set)
ns := n.(*Set)
// If the new value was set, compare the listCode's to determine if
// the two are equal. Comparing listCode's instead of the actuall values
// is needed because there could be computed values in the set which
// would result in false positives while comparing.
if !all && nSet && reflect.DeepEqual(os.listCode(), ns.listCode()) {
return nil
}
// Get the counts
oldLen := os.Len()
newLen := ns.Len()
oldStr := strconv.Itoa(oldLen)
newStr := strconv.Itoa(newLen)
// If the set computed then say that the # is computed
if computedSet || (schema.Computed && !nSet) {
// If # already exists, equals 0 and no new set is supplied, there
// is nothing to record in the diff
count, ok := d.GetOk(k + ".#")
if ok && count.(int) == 0 && !nSet && !computedSet {
return nil
} }
// If we had no changes, then we're done // Set the count but make sure that if # does not exist, we don't
if tempD.Empty() { // use the zeroed value
return nil countStr := strconv.Itoa(count.(int))
if !ok {
countStr = ""
}
diff.Attributes[k+".#"] = &terraform.ResourceAttrDiff{
Old: countStr,
NewComputed: true,
}
return nil
}
// If the counts are not the same, then record that diff
changed := oldLen != newLen
if changed || all {
countSchema := &Schema{
Type: TypeInt,
Computed: schema.Computed,
ForceNew: schema.ForceNew,
}
diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{
Old: oldStr,
New: newStr,
})
}
for _, code := range ns.listCode() {
switch t := schema.Elem.(type) {
case *Schema:
// Copy the schema so that we can set Computed/ForceNew from
// the parent schema (the TypeSet).
t2 := *t
t2.ForceNew = schema.ForceNew
// This is just a primitive element, so go through each and
// just diff each.
subK := fmt.Sprintf("%s.%d", k, code)
subK = strings.Replace(subK, "-", "~", -1)
err := m.diff(subK, &t2, diff, d, true)
if err != nil {
return err
}
case *Resource:
// This is a complex resource
for k2, schema := range t.Schema {
subK := fmt.Sprintf("%s.%d.%s", k, code, k2)
subK = strings.Replace(subK, "-", "~", -1)
err := m.diff(subK, schema, diff, d, true)
if err != nil {
return err
}
}
default:
return fmt.Errorf("%s: unknown element type (internal)", k)
} }
} }
// We have changes, so re-run the diff, but set a flag to force return nil
// getting all diffs, even if there is no change.
return m.diffList(k, schema, diff, d, true)
} }
func (m schemaMap) diffString( func (m schemaMap) diffString(

View File

@ -21,6 +21,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* String decode * String decode
*/ */
// #0
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -50,6 +51,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #1
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -77,6 +79,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #2
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -98,7 +101,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// Computed, but set in config // #3 Computed, but set in config
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -130,7 +133,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// Default // #4 Default
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -156,7 +159,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// DefaultFunc, value // #5 DefaultFunc, value
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -184,7 +187,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// DefaultFunc, configuration set // #6 DefaultFunc, configuration set
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -214,7 +217,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// String with StateFunc // #7 String with StateFunc
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -246,7 +249,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// Variable (just checking) // #8 Variable (just checking)
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -277,7 +280,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// Variable computed // #9 Variable computed
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -312,6 +315,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* Int decode * Int decode
*/ */
// #10
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"port": &Schema{ "port": &Schema{
@ -345,6 +349,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* Bool decode * Bool decode
*/ */
// #11
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"port": &Schema{ "port": &Schema{
@ -377,6 +382,8 @@ func TestSchemaMap_Diff(t *testing.T) {
/* /*
* Bool * Bool
*/ */
// #12
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"delete": &Schema{ "delete": &Schema{
@ -403,6 +410,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* List decode * List decode
*/ */
// #13
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -442,6 +450,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #14
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -485,6 +494,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #15
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -518,6 +528,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #16
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -545,6 +556,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #17
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -582,6 +594,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #18
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -626,6 +639,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #19
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -656,6 +670,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* Set * Set
*/ */
// #20
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -680,15 +695,15 @@ func TestSchemaMap_Diff(t *testing.T) {
Old: "0", Old: "0",
New: "3", New: "3",
}, },
"ports.0": &terraform.ResourceAttrDiff{ "ports.1": &terraform.ResourceAttrDiff{
Old: "", Old: "",
New: "1", New: "1",
}, },
"ports.1": &terraform.ResourceAttrDiff{ "ports.2": &terraform.ResourceAttrDiff{
Old: "", Old: "",
New: "2", New: "2",
}, },
"ports.2": &terraform.ResourceAttrDiff{ "ports.5": &terraform.ResourceAttrDiff{
Old: "", Old: "",
New: "5", New: "5",
}, },
@ -698,6 +713,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #21
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -724,6 +740,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #22
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -753,6 +770,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #23
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -781,15 +799,15 @@ func TestSchemaMap_Diff(t *testing.T) {
Old: "0", Old: "0",
New: "3", New: "3",
}, },
"ports.0": &terraform.ResourceAttrDiff{ "ports.1": &terraform.ResourceAttrDiff{
Old: "", Old: "",
New: "1", New: "1",
}, },
"ports.1": &terraform.ResourceAttrDiff{ "ports.2": &terraform.ResourceAttrDiff{
Old: "", Old: "",
New: "2", New: "2",
}, },
"ports.2": &terraform.ResourceAttrDiff{ "ports.5": &terraform.ResourceAttrDiff{
Old: "", Old: "",
New: "5", New: "5",
}, },
@ -799,6 +817,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #24
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -825,7 +844,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Diff: &terraform.InstanceDiff{ Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{ Attributes: map[string]*terraform.ResourceAttrDiff{
"ports.#": &terraform.ResourceAttrDiff{ "ports.#": &terraform.ResourceAttrDiff{
Old: "0", Old: "",
New: "", New: "",
NewComputed: true, NewComputed: true,
}, },
@ -835,6 +854,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #25
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -850,8 +870,8 @@ func TestSchemaMap_Diff(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "2", "ports.#": "2",
"ports.0": "2",
"ports.1": "1", "ports.1": "1",
"ports.2": "2",
}, },
}, },
@ -865,15 +885,15 @@ func TestSchemaMap_Diff(t *testing.T) {
Old: "2", Old: "2",
New: "3", New: "3",
}, },
"ports.0": &terraform.ResourceAttrDiff{ "ports.1": &terraform.ResourceAttrDiff{
Old: "1", Old: "1",
New: "1", New: "1",
}, },
"ports.1": &terraform.ResourceAttrDiff{ "ports.2": &terraform.ResourceAttrDiff{
Old: "2", Old: "2",
New: "2", New: "2",
}, },
"ports.2": &terraform.ResourceAttrDiff{ "ports.5": &terraform.ResourceAttrDiff{
Old: "", Old: "",
New: "5", New: "5",
}, },
@ -883,6 +903,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #26
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -898,8 +919,8 @@ func TestSchemaMap_Diff(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ports.#": "2", "ports.#": "2",
"ports.0": "2",
"ports.1": "1", "ports.1": "1",
"ports.2": "2",
}, },
}, },
@ -911,20 +932,13 @@ func TestSchemaMap_Diff(t *testing.T) {
Old: "2", Old: "2",
New: "0", New: "0",
}, },
"ports.0": &terraform.ResourceAttrDiff{
Old: "1",
NewRemoved: true,
},
"ports.1": &terraform.ResourceAttrDiff{
Old: "2",
NewRemoved: true,
},
}, },
}, },
Err: false, Err: false,
}, },
// #27
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
@ -932,7 +946,9 @@ func TestSchemaMap_Diff(t *testing.T) {
Optional: true, Optional: true,
Computed: true, Computed: true,
Elem: &Schema{Type: TypeInt}, Elem: &Schema{Type: TypeInt},
Set: func(v interface{}) int { return v.(int) }, Set: func(a interface{}) int {
return a.(int)
},
}, },
}, },
@ -940,7 +956,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Attributes: map[string]string{ Attributes: map[string]string{
"availability_zone": "bar", "availability_zone": "bar",
"ports.#": "1", "ports.#": "1",
"ports.0": "80", "ports.80": "80",
}, },
}, },
@ -951,6 +967,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #28
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ingress": &Schema{ "ingress": &Schema{
@ -979,16 +996,16 @@ func TestSchemaMap_Diff(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"ingress.#": "2", "ingress.#": "2",
"ingress.0.ports.#": "1", "ingress.80.ports.#": "1",
"ingress.0.ports.0": "80", "ingress.80.ports.0": "80",
"ingress.1.ports.#": "1", "ingress.443.ports.#": "1",
"ingress.1.ports.0": "443", "ingress.443.ports.0": "443",
}, },
}, },
Config: map[string]interface{}{ Config: map[string]interface{}{
"ingress": []interface{}{ "ingress": []map[string]interface{}{
map[string]interface{}{ map[string]interface{}{
"ports": []interface{}{443}, "ports": []interface{}{443},
}, },
@ -1007,6 +1024,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* List of structure decode * List of structure decode
*/ */
// #29
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ingress": &Schema{ "ingress": &Schema{
@ -1053,6 +1071,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* ComputedWhen * ComputedWhen
*/ */
// #30
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -1083,6 +1102,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #31
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -1165,6 +1185,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* Maps * Maps
*/ */
// #32
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"config_vars": &Schema{ "config_vars": &Schema{
@ -1194,6 +1215,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #33
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"config_vars": &Schema{ "config_vars": &Schema{
@ -1231,6 +1253,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #34
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"vars": &Schema{ "vars": &Schema{
@ -1271,6 +1294,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #35
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"vars": &Schema{ "vars": &Schema{
@ -1292,6 +1316,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #36
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"config_vars": &Schema{ "config_vars": &Schema{
@ -1331,6 +1356,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #37
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"config_vars": &Schema{ "config_vars": &Schema{
@ -1373,6 +1399,7 @@ func TestSchemaMap_Diff(t *testing.T) {
* ForceNews * ForceNews
*/ */
// #38
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -1418,7 +1445,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// Set // #39 Set
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
@ -1432,7 +1459,9 @@ func TestSchemaMap_Diff(t *testing.T) {
Optional: true, Optional: true,
Computed: true, Computed: true,
Elem: &Schema{Type: TypeInt}, Elem: &Schema{Type: TypeInt},
Set: func(v interface{}) int { return v.(int) }, Set: func(a interface{}) int {
return a.(int)
},
}, },
}, },
@ -1440,7 +1469,7 @@ func TestSchemaMap_Diff(t *testing.T) {
Attributes: map[string]string{ Attributes: map[string]string{
"availability_zone": "bar", "availability_zone": "bar",
"ports.#": "1", "ports.#": "1",
"ports.0": "80", "ports.80": "80",
}, },
}, },
@ -1467,13 +1496,9 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
// #40 Set
{ {
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"internal": &Schema{
Type: TypeBool,
Required: true,
},
"instances": &Schema{ "instances": &Schema{
Type: TypeSet, Type: TypeSet,
Elem: &Schema{Type: TypeString}, Elem: &Schema{Type: TypeString},
@ -1487,13 +1512,11 @@ func TestSchemaMap_Diff(t *testing.T) {
State: &terraform.InstanceState{ State: &terraform.InstanceState{
Attributes: map[string]string{ Attributes: map[string]string{
"internal": "false",
"instances.#": "0", "instances.#": "0",
}, },
}, },
Config: map[string]interface{}{ Config: map[string]interface{}{
"internal": true,
"instances": []interface{}{"${var.foo}"}, "instances": []interface{}{"${var.foo}"},
}, },
@ -1503,13 +1526,136 @@ func TestSchemaMap_Diff(t *testing.T) {
Diff: &terraform.InstanceDiff{ Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{ Attributes: map[string]*terraform.ResourceAttrDiff{
"internal": &terraform.ResourceAttrDiff{ "instances.#": &terraform.ResourceAttrDiff{
Old: "0",
NewComputed: true,
},
},
},
Err: false,
},
// #41 Set
{
Schema: map[string]*Schema{
"route": &Schema{
Type: TypeSet,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"index": &Schema{
Type: TypeInt,
Required: true,
},
"gateway": &Schema{
Type: TypeString,
Optional: true,
},
},
},
Set: func(v interface{}) int {
m := v.(map[string]interface{})
return m["index"].(int)
},
},
},
State: nil,
Config: map[string]interface{}{
"route": []map[string]interface{}{
map[string]interface{}{
"index": "1",
"gateway": "${var.foo}",
},
},
},
ConfigVariables: map[string]string{
"var.foo": config.UnknownVariableValue,
},
Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"route.#": &terraform.ResourceAttrDiff{
Old: "0", Old: "0",
New: "1", New: "1",
}, },
"route.~1.index": &terraform.ResourceAttrDiff{
Old: "",
New: "1",
},
"route.~1.gateway": &terraform.ResourceAttrDiff{
Old: "",
New: "${var.foo}",
},
},
},
"instances.#": &terraform.ResourceAttrDiff{ Err: false,
Old: "0", },
// #42 Set
{
Schema: map[string]*Schema{
"route": &Schema{
Type: TypeSet,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"index": &Schema{
Type: TypeInt,
Required: true,
},
"gateway": &Schema{
Type: TypeSet,
Optional: true,
Elem: &Schema{Type: TypeInt},
Set: func(a interface{}) int {
return a.(int)
},
},
},
},
Set: func(v interface{}) int {
m := v.(map[string]interface{})
return m["index"].(int)
},
},
},
State: nil,
Config: map[string]interface{}{
"route": []map[string]interface{}{
map[string]interface{}{
"index": "1",
"gateway": []interface{}{
"${var.foo}",
},
},
},
},
ConfigVariables: map[string]string{
"var.foo": config.UnknownVariableValue,
},
Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"route.#": &terraform.ResourceAttrDiff{
Old: "0",
New: "1",
},
"route.~1.index": &terraform.ResourceAttrDiff{
Old: "",
New: "1",
},
"route.~1.gateway.#": &terraform.ResourceAttrDiff{
Old: "",
NewComputed: true, NewComputed: true,
}, },
}, },
@ -1522,12 +1668,12 @@ func TestSchemaMap_Diff(t *testing.T) {
for i, tc := range cases { for i, tc := range cases {
c, err := config.NewRawConfig(tc.Config) c, err := config.NewRawConfig(tc.Config)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("#%d err: %s", i, err)
} }
if len(tc.ConfigVariables) > 0 { if len(tc.ConfigVariables) > 0 {
if err := c.Interpolate(tc.ConfigVariables); err != nil { if err := c.Interpolate(tc.ConfigVariables); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("#%d err: %s", i, err)
} }
} }

View File

@ -5,6 +5,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"reflect" "reflect"
"regexp"
"sort" "sort"
"strings" "strings"
) )
@ -348,7 +349,7 @@ func (d *InstanceDiff) RequiresNew() bool {
return false return false
} }
// Same checks whether or not to InstanceDiff are the "same." When // Same checks whether or not two InstanceDiff's are the "same". When
// we say "same", it is not necessarily exactly equal. Instead, it is // we say "same", it is not necessarily exactly equal. Instead, it is
// just checking that the same attributes are changing, a destroy // just checking that the same attributes are changing, a destroy
// isn't suddenly happening, etc. // isn't suddenly happening, etc.
@ -376,7 +377,18 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) bool {
for k, _ := range d2.Attributes { for k, _ := range d2.Attributes {
checkNew[k] = struct{}{} checkNew[k] = struct{}{}
} }
for k, diffOld := range d.Attributes {
// Make an ordered list so we are sure the approximated hashes are left
// to process at the end of the loop
keys := make([]string, 0, len(d.Attributes))
for k, _ := range d.Attributes {
keys = append(keys, k)
}
sort.StringSlice(keys).Sort()
for _, k := range keys {
diffOld := d.Attributes[k]
if _, ok := checkOld[k]; !ok { if _, ok := checkOld[k]; !ok {
// We're not checking this key for whatever reason (see where // We're not checking this key for whatever reason (see where
// check is modified). // check is modified).
@ -389,14 +401,52 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) bool {
_, ok := d2.Attributes[k] _, ok := d2.Attributes[k]
if !ok { if !ok {
// The matching attribute was not found, we're different // No exact match, but maybe this is a set containing computed
return false // values. So check if there is an approximate hash in the key
// and if so, try to match the key.
if strings.Contains(k, "~") {
// TODO (SvH): There should be a better way to do this...
parts := strings.Split(k, ".")
parts2 := strings.Split(k, ".")
re := regexp.MustCompile(`^~\d+$`)
for i, part := range parts {
if re.MatchString(part) {
parts2[i] = `\d+`
}
}
re, err := regexp.Compile("^" + strings.Join(parts2, `\.`) + "$")
if err != nil {
return false
}
for k2, _ := range checkNew {
if re.MatchString(k2) {
delete(checkNew, k2)
if diffOld.NewComputed && strings.HasSuffix(k, ".#") {
// This is a computed list or set, so remove any keys with this
// prefix from the check list.
prefix := k2[:len(k2)-1]
for k2, _ := range checkNew {
if strings.HasPrefix(k2, prefix) {
delete(checkNew, k2)
}
}
}
ok = true
break
}
}
}
if !ok {
return false
}
} }
if diffOld.NewComputed && strings.HasSuffix(k, ".#") { if diffOld.NewComputed && strings.HasSuffix(k, ".#") {
// This is a computed list, so remove any keys with this // This is a computed list or set, so remove any keys with this
// prefix from the check list. // prefix from the check list.
kprefix := k[0:len(k)-2] + "." kprefix := k[:len(k)-1]
for k2, _ := range checkOld { for k2, _ := range checkOld {
if strings.HasPrefix(k2, kprefix) { if strings.HasPrefix(k2, kprefix) {
delete(checkOld, k2) delete(checkOld, k2)

View File

@ -464,6 +464,34 @@ func TestInstanceDiffSame(t *testing.T) {
}, },
true, true,
}, },
{
&InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"foo.#": &ResourceAttrDiff{
Old: "0",
New: "1",
},
"foo.~35964334.bar": &ResourceAttrDiff{
Old: "",
New: "${var.foo}",
},
},
},
&InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"foo.#": &ResourceAttrDiff{
Old: "0",
New: "1",
},
"foo.87654323.bar": &ResourceAttrDiff{
Old: "",
New: "12",
},
},
},
true,
},
} }
for i, tc := range cases { for i, tc := range cases {

View File

@ -1,8 +1,6 @@
package terraform package terraform
import ( import "sync"
"sync"
)
// MockResourceProvider implements ResourceProvider but mocks out all the // MockResourceProvider implements ResourceProvider but mocks out all the
// calls for testing purposes. // calls for testing purposes.