Merge pull request #663 from svanharmelen/f-fix/change-set-logic
core: refactoring the way sets work internally v2
This commit is contained in:
commit
6a663796d5
2
Makefile
2
Makefile
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}"
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
|
@ -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"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
|
@ -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}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue