This is an automated email from the ASF dual-hosted git repository. pearl11594 pushed a commit to branch fix-acl-ports in repository https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git
commit 9f206ca2cbbec8c57a02bf3c242277e04321d1ad Author: Pearl Dsilva <[email protected]> AuthorDate: Wed Oct 8 15:33:02 2025 -0400 Deprecate usage of ports --- cloudstack/resource_cloudstack_network_acl_rule.go | 221 +++++++++++------ .../resource_cloudstack_network_acl_rule_test.go | 261 ++++++++++++--------- 2 files changed, 292 insertions(+), 190 deletions(-) diff --git a/cloudstack/resource_cloudstack_network_acl_rule.go b/cloudstack/resource_cloudstack_network_acl_rule.go index 253fd1b..be6085f 100644 --- a/cloudstack/resource_cloudstack_network_acl_rule.go +++ b/cloudstack/resource_cloudstack_network_acl_rule.go @@ -99,10 +99,16 @@ func resourceCloudStackNetworkACLRule() *schema.Resource { }, "ports": { - Type: schema.TypeSet, + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Deprecated: "Use 'port' instead. The 'ports' field is deprecated and will be removed in a future version.", + }, + + "port": { + Type: schema.TypeString, Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, "traffic_type": { @@ -299,42 +305,19 @@ func createNetworkACLRule(d *schema.ResourceData, meta interface{}, rule map[str log.Printf("[DEBUG] Created ALL rule with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id) } - // If protocol is TCP or UDP, create the rule (with or without ports) + // If protocol is TCP or UDP, create the rule (with or without port) if rule["protocol"].(string) == "tcp" || rule["protocol"].(string) == "udp" { - ps, ok := rule["ports"].(*schema.Set) - if !ok || ps == nil { - log.Printf("[DEBUG] No ports specified for TCP/UDP rule, creating rule for all ports") - ps = &schema.Set{F: schema.HashString} - } + portStr, hasPort := rule["port"].(string) - // Create an empty schema.Set to hold all processed ports - ports := &schema.Set{F: schema.HashString} - log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule", ps.Len()) - - if ps.Len() == 0 { - // Create a rule for all ports - r, err := Retry(4, retryableACLCreationFunc(cs, p)) - if err != nil { - log.Printf("[ERROR] Failed to create TCP/UDP rule for all ports: %v", err) - return err - } - uuids["all_ports"] = r.(*cloudstack.CreateNetworkACLResponse).Id - rule["uuids"] = uuids - log.Printf("[DEBUG] Created TCP/UDP rule for all ports with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id) - } else { - // Process specified ports - for _, port := range ps.List() { - if _, ok := uuids[port.(string)]; ok { - ports.Add(port) - rule["ports"] = ports - log.Printf("[DEBUG] Port %s already has UUID, skipping", port.(string)) - continue - } + if hasPort && portStr != "" { + // Handle single port + log.Printf("[DEBUG] Processing single port for TCP/UDP rule: %s", portStr) - m := splitPorts.FindStringSubmatch(port.(string)) + if _, ok := uuids[portStr]; !ok { + m := splitPorts.FindStringSubmatch(portStr) if m == nil { - log.Printf("[ERROR] Invalid port format: %s", port.(string)) - return fmt.Errorf("%q is not a valid port value. Valid options are '80' or '80-90'", port.(string)) + log.Printf("[ERROR] Invalid port format: %s", portStr) + return fmt.Errorf("%q is not a valid port value. Valid options are '80' or '80-90'", portStr) } startPort, err := strconv.Atoi(m[1]) @@ -354,20 +337,31 @@ func createNetworkACLRule(d *schema.ResourceData, meta interface{}, rule map[str p.SetStartport(startPort) p.SetEndport(endPort) - log.Printf("[DEBUG] Set ports start=%d, end=%d", startPort, endPort) + log.Printf("[DEBUG] Set port start=%d, end=%d", startPort, endPort) r, err := Retry(4, retryableACLCreationFunc(cs, p)) if err != nil { - log.Printf("[ERROR] Failed to create TCP/UDP rule for port %s: %v", port.(string), err) + log.Printf("[ERROR] Failed to create TCP/UDP rule for port %s: %v", portStr, err) return err } - ports.Add(port) - rule["ports"] = ports - uuids[port.(string)] = r.(*cloudstack.CreateNetworkACLResponse).Id + uuids[portStr] = r.(*cloudstack.CreateNetworkACLResponse).Id rule["uuids"] = uuids - log.Printf("[DEBUG] Created TCP/UDP rule for port %s with ID=%s", port.(string), r.(*cloudstack.CreateNetworkACLResponse).Id) + log.Printf("[DEBUG] Created TCP/UDP rule for port %s with ID=%s", portStr, r.(*cloudstack.CreateNetworkACLResponse).Id) + } else { + log.Printf("[DEBUG] Port %s already has UUID, skipping", portStr) + } + } else { + // No port specified - create rule for all ports + log.Printf("[DEBUG] No port specified for TCP/UDP rule, creating rule for all ports") + r, err := Retry(4, retryableACLCreationFunc(cs, p)) + if err != nil { + log.Printf("[ERROR] Failed to create TCP/UDP rule for all ports: %v", err) + return err } + uuids["all_ports"] = r.(*cloudstack.CreateNetworkACLResponse).Id + rule["uuids"] = uuids + log.Printf("[DEBUG] Created TCP/UDP rule for all ports with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id) } } @@ -507,29 +501,71 @@ func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface } if rule["protocol"].(string) == "tcp" || rule["protocol"].(string) == "udp" { - ps, ok := rule["ports"].(*schema.Set) - if !ok || ps == nil { - log.Printf("[DEBUG] No ports specified for TCP/UDP rule, initializing empty set") - ps = &schema.Set{F: schema.HashString} - } + // Check for deprecated ports field first (for backward compatibility) + ps, hasPortsSet := rule["ports"].(*schema.Set) + portStr, hasPort := rule["port"].(string) + + if hasPortsSet && ps != nil && ps.Len() > 0 { + // Handle deprecated ports field (multiple ports) + log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule (deprecated field)", ps.Len()) + + // Create an empty schema.Set to hold all ports + ports := &schema.Set{F: schema.HashString} + + // Loop through all ports and retrieve their info + for _, port := range ps.List() { + id, ok := uuids[port.(string)] + if !ok { + log.Printf("[DEBUG] No UUID for port %s, skipping", port.(string)) + continue + } + + // Get the rule + r, ok := ruleMap[id.(string)] + if !ok { + log.Printf("[DEBUG] TCP/UDP rule for port %s with ID %s not found, removing UUID", port.(string), id.(string)) + delete(uuids, port.(string)) + continue + } + + // Delete the known rule so only unknown rules remain in the ruleMap + delete(ruleMap, id.(string)) + + // Create a set with all CIDR's + cidrs := &schema.Set{F: schema.HashString} + for _, cidr := range strings.Split(r.Cidrlist, ",") { + cidrs.Add(cidr) + } + + // Update the values + rule["action"] = strings.ToLower(r.Action) + rule["protocol"] = r.Protocol + rule["traffic_type"] = strings.ToLower(r.Traffictype) + rule["cidr_list"] = cidrs + ports.Add(port) + log.Printf("[DEBUG] Added port %s to TCP/UDP rule", port.(string)) + } - // Create an empty schema.Set to hold all ports - ports := &schema.Set{F: schema.HashString} - log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule", ps.Len()) + // Add this rule to the rules set with ports + rule["ports"] = ports + rules.Add(rule) + log.Printf("[DEBUG] Added TCP/UDP rule with deprecated ports to state: %+v", rule) - // Loop through all ports and retrieve their info - for _, port := range ps.List() { - id, ok := uuids[port.(string)] + } else if hasPort && portStr != "" { + // Handle new port field (single port) + log.Printf("[DEBUG] Processing single port for TCP/UDP rule: %s", portStr) + + id, ok := uuids[portStr] if !ok { - log.Printf("[DEBUG] No UUID for port %s, skipping", port.(string)) + log.Printf("[DEBUG] No UUID for port %s, skipping rule", portStr) continue } // Get the rule r, ok := ruleMap[id.(string)] if !ok { - log.Printf("[DEBUG] TCP/UDP rule for port %s with ID %s not found, removing UUID", port.(string), id.(string)) - delete(uuids, port.(string)) + log.Printf("[DEBUG] TCP/UDP rule for port %s with ID %s not found, removing UUID", portStr, id.(string)) + delete(uuids, portStr) continue } @@ -547,20 +583,44 @@ func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface rule["protocol"] = r.Protocol rule["traffic_type"] = strings.ToLower(r.Traffictype) rule["cidr_list"] = cidrs - ports.Add(port) - log.Printf("[DEBUG] Added port %s to TCP/UDP rule", port.(string)) - } - - // If there is at least one port found, add this rule to the rules set - if ports.Len() > 0 { - rule["ports"] = ports + rule["port"] = portStr rules.Add(rule) - log.Printf("[DEBUG] Added TCP/UDP rule to state: %+v", rule) + log.Printf("[DEBUG] Added TCP/UDP rule with single port to state: %+v", rule) + } else { - // Add the rule even if no ports are specified, as ports are optional - rule["ports"] = ports + // Handle rule with no port (all ports) + log.Printf("[DEBUG] Processing TCP/UDP rule with no port specified") + + id, ok := uuids["all_ports"] + if !ok { + log.Printf("[DEBUG] No UUID for all_ports, skipping rule") + continue + } + + // Get the rule + r, ok := ruleMap[id.(string)] + if !ok { + log.Printf("[DEBUG] TCP/UDP rule for all_ports with ID %s not found, removing UUID", id.(string)) + delete(uuids, "all_ports") + continue + } + + // Delete the known rule so only unknown rules remain in the ruleMap + delete(ruleMap, id.(string)) + + // Create a set with all CIDR's + cidrs := &schema.Set{F: schema.HashString} + for _, cidr := range strings.Split(r.Cidrlist, ",") { + cidrs.Add(cidr) + } + + // Update the values + rule["action"] = strings.ToLower(r.Action) + rule["protocol"] = r.Protocol + rule["traffic_type"] = strings.ToLower(r.Traffictype) + rule["cidr_list"] = cidrs rules.Add(rule) - log.Printf("[DEBUG] Added TCP/UDP rule with no ports to state: %+v", rule) + log.Printf("[DEBUG] Added TCP/UDP rule with no port to state: %+v", rule) } } } @@ -791,19 +851,26 @@ func verifyNetworkACLRuleParams(d *schema.ResourceData, rule map[string]interfac // No additional test are needed log.Printf("[DEBUG] Protocol 'all' validated") case "tcp", "udp": - if ports, ok := rule["ports"].(*schema.Set); ok { - log.Printf("[DEBUG] Found %d ports for TCP/UDP", ports.Len()) - for _, port := range ports.List() { - m := splitPorts.FindStringSubmatch(port.(string)) - if m == nil { - log.Printf("[ERROR] Invalid port format: %s", port.(string)) - return fmt.Errorf( - "%q is not a valid port value. Valid options are '80' or '80-90'", port.(string)) - } + // Check if deprecated ports field is used (not allowed for new configurations) + portsSet, hasPortsSet := rule["ports"].(*schema.Set) + portStr, hasPort := rule["port"].(string) + + if hasPortsSet && portsSet.Len() > 0 { + log.Printf("[ERROR] Deprecated ports field used in new configuration") + return fmt.Errorf("The 'ports' field is deprecated. Use 'port' instead for new configurations.") + } + + // Validate the new port field if used + if hasPort && portStr != "" { + log.Printf("[DEBUG] Found port for TCP/UDP: %s", portStr) + m := splitPorts.FindStringSubmatch(portStr) + if m == nil { + log.Printf("[ERROR] Invalid port format: %s", portStr) + return fmt.Errorf( + "%q is not a valid port value. Valid options are '80' or '80-90'", portStr) } } else { - log.Printf("[DEBUG] No ports specified for TCP/UDP, assuming empty set") - // Allow empty ports for TCP/UDP (your config has no ports) + log.Printf("[DEBUG] No port specified for TCP/UDP, allowing empty port") } default: _, err := strconv.ParseInt(protocol, 0, 0) diff --git a/cloudstack/resource_cloudstack_network_acl_rule_test.go b/cloudstack/resource_cloudstack_network_acl_rule_test.go index a7dad42..e894a8e 100644 --- a/cloudstack/resource_cloudstack_network_acl_rule_test.go +++ b/cloudstack/resource_cloudstack_network_acl_rule_test.go @@ -41,43 +41,43 @@ func TestAccCloudStackNetworkACLRule_basic(t *testing.T) { testAccCheckCloudStackNetworkACLRulesExist("cloudstack_network_acl.foo"), resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.#", "3"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.action", "allow"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.cidr_list.0", "172.16.100.0/24"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.1", "80"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.0", "443"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.description", "Allow HTTP and HTTPS"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.rule_number", "20"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.action", "allow"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.cidr_list.#", "1"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.cidr_list.0", "172.18.100.0/24"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.icmp_code", "-1"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.icmp_type", "-1"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.traffic_type", "ingress"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.description", "Allow ICMP traffic"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.rule_number", "10"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.description", "Allow all traffic"), + "cloudstack_network_acl_rule.foo", "rule.#", "4"), + // Don't rely on specific rule ordering as TypeSet doesn't guarantee order + // Just check that we have the expected rules with their attributes + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "rule_number": "10", + "action": "allow", + "protocol": "all", + "traffic_type": "ingress", + "description": "Allow all traffic", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "rule_number": "20", + "action": "allow", + "protocol": "icmp", + "icmp_type": "-1", + "icmp_code": "-1", + "traffic_type": "ingress", + "description": "Allow ICMP traffic", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "allow", + "protocol": "tcp", + "port": "80", + "traffic_type": "ingress", + "description": "Allow HTTP", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "allow", + "protocol": "tcp", + "port": "443", + "traffic_type": "ingress", + "description": "Allow HTTPS", + }), ), }, }, @@ -95,35 +95,42 @@ func TestAccCloudStackNetworkACLRule_update(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckCloudStackNetworkACLRulesExist("cloudstack_network_acl.foo"), resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.#", "3"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.action", "allow"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.cidr_list.0", "172.16.100.0/24"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.1", "80"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.0", "443"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.rule_number", "20"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.action", "allow"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.cidr_list.#", "1"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.cidr_list.0", "172.18.100.0/24"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.icmp_code", "-1"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.icmp_type", "-1"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.traffic_type", "ingress"), + "cloudstack_network_acl_rule.foo", "rule.#", "4"), + // Don't rely on specific rule ordering as TypeSet doesn't guarantee order + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "rule_number": "10", + "action": "allow", + "protocol": "all", + "traffic_type": "ingress", + "description": "Allow all traffic", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "rule_number": "20", + "action": "allow", + "protocol": "icmp", + "icmp_type": "-1", + "icmp_code": "-1", + "traffic_type": "ingress", + "description": "Allow ICMP traffic", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "allow", + "protocol": "tcp", + "port": "80", + "traffic_type": "ingress", + "description": "Allow HTTP", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "allow", + "protocol": "tcp", + "port": "443", + "traffic_type": "ingress", + "description": "Allow HTTPS", + }), ), }, @@ -132,49 +139,53 @@ func TestAccCloudStackNetworkACLRule_update(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckCloudStackNetworkACLRulesExist("cloudstack_network_acl.foo"), resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.#", "4"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.action", "deny"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.cidr_list.0", "10.0.0.0/24"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.protocol", "tcp"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.ports.#", "2"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.ports.0", "1000-2000"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.ports.1", "80"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.1.traffic_type", "egress"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.action", "deny"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.cidr_list.#", "2"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.cidr_list.1", "172.18.101.0/24"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.cidr_list.0", "172.18.100.0/24"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.icmp_code", "-1"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.icmp_type", "-1"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.2.traffic_type", "ingress"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.action", "allow"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.cidr_list.0", "172.18.100.0/24"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.1", "80"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.ports.0", "443"), - resource.TestCheckResourceAttr( - "cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"), + "cloudstack_network_acl_rule.foo", "rule.#", "6"), + // Check for the expected rules using TypeSet elem matching + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "deny", + "protocol": "all", + "traffic_type": "ingress", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "deny", + "protocol": "icmp", + "icmp_type": "-1", + "icmp_code": "-1", + "traffic_type": "ingress", + "description": "Deny ICMP traffic", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "allow", + "protocol": "tcp", + "port": "80", + "traffic_type": "ingress", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "allow", + "protocol": "tcp", + "port": "443", + "traffic_type": "ingress", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "deny", + "protocol": "tcp", + "port": "80", + "traffic_type": "egress", + "description": "Deny specific TCP ports", + }), + resource.TestCheckTypeSetElemNestedAttrs( + "cloudstack_network_acl_rule.foo", "rule.*", map[string]string{ + "action": "deny", + "protocol": "tcp", + "port": "1000-2000", + "traffic_type": "egress", + "description": "Deny specific TCP ports", + }), ), }, }, @@ -280,9 +291,17 @@ resource "cloudstack_network_acl_rule" "foo" { rule { cidr_list = ["172.16.100.0/24"] protocol = "tcp" - ports = ["80", "443"] + port = "80" + traffic_type = "ingress" + description = "Allow HTTP" + } + + rule { + cidr_list = ["172.16.100.0/24"] + protocol = "tcp" + port = "443" traffic_type = "ingress" - description = "Allow HTTP and HTTPS" + description = "Allow HTTPS" } }` @@ -324,15 +343,31 @@ resource "cloudstack_network_acl_rule" "foo" { action = "allow" cidr_list = ["172.18.100.0/24"] protocol = "tcp" - ports = ["80", "443"] + port = "80" traffic_type = "ingress" } + rule { + cidr_list = ["172.16.100.0/24"] + protocol = "tcp" + port = "443" + traffic_type = "ingress" + } + + rule { + action = "deny" + cidr_list = ["10.0.0.0/24"] + protocol = "tcp" + port = "80" + traffic_type = "egress" + description = "Deny specific TCP ports" + } + rule { action = "deny" cidr_list = ["10.0.0.0/24"] protocol = "tcp" - ports = ["80", "1000-2000"] + port = "1000-2000" traffic_type = "egress" description = "Deny specific TCP ports" }
