Skip to content

Commit 9f206ca

Browse files
committed
Deprecate usage of ports
1 parent 002668e commit 9f206ca

File tree

2 files changed

+292
-190
lines changed

2 files changed

+292
-190
lines changed

cloudstack/resource_cloudstack_network_acl_rule.go

Lines changed: 144 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,16 @@ func resourceCloudStackNetworkACLRule() *schema.Resource {
9999
},
100100

101101
"ports": {
102-
Type: schema.TypeSet,
102+
Type: schema.TypeSet,
103+
Optional: true,
104+
Elem: &schema.Schema{Type: schema.TypeString},
105+
Set: schema.HashString,
106+
Deprecated: "Use 'port' instead. The 'ports' field is deprecated and will be removed in a future version.",
107+
},
108+
109+
"port": {
110+
Type: schema.TypeString,
103111
Optional: true,
104-
Elem: &schema.Schema{Type: schema.TypeString},
105-
Set: schema.HashString,
106112
},
107113

108114
"traffic_type": {
@@ -299,42 +305,19 @@ func createNetworkACLRule(d *schema.ResourceData, meta interface{}, rule map[str
299305
log.Printf("[DEBUG] Created ALL rule with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id)
300306
}
301307

302-
// If protocol is TCP or UDP, create the rule (with or without ports)
308+
// If protocol is TCP or UDP, create the rule (with or without port)
303309
if rule["protocol"].(string) == "tcp" || rule["protocol"].(string) == "udp" {
304-
ps, ok := rule["ports"].(*schema.Set)
305-
if !ok || ps == nil {
306-
log.Printf("[DEBUG] No ports specified for TCP/UDP rule, creating rule for all ports")
307-
ps = &schema.Set{F: schema.HashString}
308-
}
310+
portStr, hasPort := rule["port"].(string)
309311

310-
// Create an empty schema.Set to hold all processed ports
311-
ports := &schema.Set{F: schema.HashString}
312-
log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule", ps.Len())
313-
314-
if ps.Len() == 0 {
315-
// Create a rule for all ports
316-
r, err := Retry(4, retryableACLCreationFunc(cs, p))
317-
if err != nil {
318-
log.Printf("[ERROR] Failed to create TCP/UDP rule for all ports: %v", err)
319-
return err
320-
}
321-
uuids["all_ports"] = r.(*cloudstack.CreateNetworkACLResponse).Id
322-
rule["uuids"] = uuids
323-
log.Printf("[DEBUG] Created TCP/UDP rule for all ports with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id)
324-
} else {
325-
// Process specified ports
326-
for _, port := range ps.List() {
327-
if _, ok := uuids[port.(string)]; ok {
328-
ports.Add(port)
329-
rule["ports"] = ports
330-
log.Printf("[DEBUG] Port %s already has UUID, skipping", port.(string))
331-
continue
332-
}
312+
if hasPort && portStr != "" {
313+
// Handle single port
314+
log.Printf("[DEBUG] Processing single port for TCP/UDP rule: %s", portStr)
333315

334-
m := splitPorts.FindStringSubmatch(port.(string))
316+
if _, ok := uuids[portStr]; !ok {
317+
m := splitPorts.FindStringSubmatch(portStr)
335318
if m == nil {
336-
log.Printf("[ERROR] Invalid port format: %s", port.(string))
337-
return fmt.Errorf("%q is not a valid port value. Valid options are '80' or '80-90'", port.(string))
319+
log.Printf("[ERROR] Invalid port format: %s", portStr)
320+
return fmt.Errorf("%q is not a valid port value. Valid options are '80' or '80-90'", portStr)
338321
}
339322

340323
startPort, err := strconv.Atoi(m[1])
@@ -354,20 +337,31 @@ func createNetworkACLRule(d *schema.ResourceData, meta interface{}, rule map[str
354337

355338
p.SetStartport(startPort)
356339
p.SetEndport(endPort)
357-
log.Printf("[DEBUG] Set ports start=%d, end=%d", startPort, endPort)
340+
log.Printf("[DEBUG] Set port start=%d, end=%d", startPort, endPort)
358341

359342
r, err := Retry(4, retryableACLCreationFunc(cs, p))
360343
if err != nil {
361-
log.Printf("[ERROR] Failed to create TCP/UDP rule for port %s: %v", port.(string), err)
344+
log.Printf("[ERROR] Failed to create TCP/UDP rule for port %s: %v", portStr, err)
362345
return err
363346
}
364347

365-
ports.Add(port)
366-
rule["ports"] = ports
367-
uuids[port.(string)] = r.(*cloudstack.CreateNetworkACLResponse).Id
348+
uuids[portStr] = r.(*cloudstack.CreateNetworkACLResponse).Id
368349
rule["uuids"] = uuids
369-
log.Printf("[DEBUG] Created TCP/UDP rule for port %s with ID=%s", port.(string), r.(*cloudstack.CreateNetworkACLResponse).Id)
350+
log.Printf("[DEBUG] Created TCP/UDP rule for port %s with ID=%s", portStr, r.(*cloudstack.CreateNetworkACLResponse).Id)
351+
} else {
352+
log.Printf("[DEBUG] Port %s already has UUID, skipping", portStr)
353+
}
354+
} else {
355+
// No port specified - create rule for all ports
356+
log.Printf("[DEBUG] No port specified for TCP/UDP rule, creating rule for all ports")
357+
r, err := Retry(4, retryableACLCreationFunc(cs, p))
358+
if err != nil {
359+
log.Printf("[ERROR] Failed to create TCP/UDP rule for all ports: %v", err)
360+
return err
370361
}
362+
uuids["all_ports"] = r.(*cloudstack.CreateNetworkACLResponse).Id
363+
rule["uuids"] = uuids
364+
log.Printf("[DEBUG] Created TCP/UDP rule for all ports with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id)
371365
}
372366
}
373367

@@ -507,29 +501,71 @@ func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface
507501
}
508502

509503
if rule["protocol"].(string) == "tcp" || rule["protocol"].(string) == "udp" {
510-
ps, ok := rule["ports"].(*schema.Set)
511-
if !ok || ps == nil {
512-
log.Printf("[DEBUG] No ports specified for TCP/UDP rule, initializing empty set")
513-
ps = &schema.Set{F: schema.HashString}
514-
}
504+
// Check for deprecated ports field first (for backward compatibility)
505+
ps, hasPortsSet := rule["ports"].(*schema.Set)
506+
portStr, hasPort := rule["port"].(string)
507+
508+
if hasPortsSet && ps != nil && ps.Len() > 0 {
509+
// Handle deprecated ports field (multiple ports)
510+
log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule (deprecated field)", ps.Len())
511+
512+
// Create an empty schema.Set to hold all ports
513+
ports := &schema.Set{F: schema.HashString}
514+
515+
// Loop through all ports and retrieve their info
516+
for _, port := range ps.List() {
517+
id, ok := uuids[port.(string)]
518+
if !ok {
519+
log.Printf("[DEBUG] No UUID for port %s, skipping", port.(string))
520+
continue
521+
}
522+
523+
// Get the rule
524+
r, ok := ruleMap[id.(string)]
525+
if !ok {
526+
log.Printf("[DEBUG] TCP/UDP rule for port %s with ID %s not found, removing UUID", port.(string), id.(string))
527+
delete(uuids, port.(string))
528+
continue
529+
}
530+
531+
// Delete the known rule so only unknown rules remain in the ruleMap
532+
delete(ruleMap, id.(string))
533+
534+
// Create a set with all CIDR's
535+
cidrs := &schema.Set{F: schema.HashString}
536+
for _, cidr := range strings.Split(r.Cidrlist, ",") {
537+
cidrs.Add(cidr)
538+
}
539+
540+
// Update the values
541+
rule["action"] = strings.ToLower(r.Action)
542+
rule["protocol"] = r.Protocol
543+
rule["traffic_type"] = strings.ToLower(r.Traffictype)
544+
rule["cidr_list"] = cidrs
545+
ports.Add(port)
546+
log.Printf("[DEBUG] Added port %s to TCP/UDP rule", port.(string))
547+
}
515548

516-
// Create an empty schema.Set to hold all ports
517-
ports := &schema.Set{F: schema.HashString}
518-
log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule", ps.Len())
549+
// Add this rule to the rules set with ports
550+
rule["ports"] = ports
551+
rules.Add(rule)
552+
log.Printf("[DEBUG] Added TCP/UDP rule with deprecated ports to state: %+v", rule)
519553

520-
// Loop through all ports and retrieve their info
521-
for _, port := range ps.List() {
522-
id, ok := uuids[port.(string)]
554+
} else if hasPort && portStr != "" {
555+
// Handle new port field (single port)
556+
log.Printf("[DEBUG] Processing single port for TCP/UDP rule: %s", portStr)
557+
558+
id, ok := uuids[portStr]
523559
if !ok {
524-
log.Printf("[DEBUG] No UUID for port %s, skipping", port.(string))
560+
log.Printf("[DEBUG] No UUID for port %s, skipping rule", portStr)
525561
continue
526562
}
527563

528564
// Get the rule
529565
r, ok := ruleMap[id.(string)]
530566
if !ok {
531-
log.Printf("[DEBUG] TCP/UDP rule for port %s with ID %s not found, removing UUID", port.(string), id.(string))
532-
delete(uuids, port.(string))
567+
log.Printf("[DEBUG] TCP/UDP rule for port %s with ID %s not found, removing UUID", portStr, id.(string))
568+
delete(uuids, portStr)
533569
continue
534570
}
535571

@@ -547,20 +583,44 @@ func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface
547583
rule["protocol"] = r.Protocol
548584
rule["traffic_type"] = strings.ToLower(r.Traffictype)
549585
rule["cidr_list"] = cidrs
550-
ports.Add(port)
551-
log.Printf("[DEBUG] Added port %s to TCP/UDP rule", port.(string))
552-
}
553-
554-
// If there is at least one port found, add this rule to the rules set
555-
if ports.Len() > 0 {
556-
rule["ports"] = ports
586+
rule["port"] = portStr
557587
rules.Add(rule)
558-
log.Printf("[DEBUG] Added TCP/UDP rule to state: %+v", rule)
588+
log.Printf("[DEBUG] Added TCP/UDP rule with single port to state: %+v", rule)
589+
559590
} else {
560-
// Add the rule even if no ports are specified, as ports are optional
561-
rule["ports"] = ports
591+
// Handle rule with no port (all ports)
592+
log.Printf("[DEBUG] Processing TCP/UDP rule with no port specified")
593+
594+
id, ok := uuids["all_ports"]
595+
if !ok {
596+
log.Printf("[DEBUG] No UUID for all_ports, skipping rule")
597+
continue
598+
}
599+
600+
// Get the rule
601+
r, ok := ruleMap[id.(string)]
602+
if !ok {
603+
log.Printf("[DEBUG] TCP/UDP rule for all_ports with ID %s not found, removing UUID", id.(string))
604+
delete(uuids, "all_ports")
605+
continue
606+
}
607+
608+
// Delete the known rule so only unknown rules remain in the ruleMap
609+
delete(ruleMap, id.(string))
610+
611+
// Create a set with all CIDR's
612+
cidrs := &schema.Set{F: schema.HashString}
613+
for _, cidr := range strings.Split(r.Cidrlist, ",") {
614+
cidrs.Add(cidr)
615+
}
616+
617+
// Update the values
618+
rule["action"] = strings.ToLower(r.Action)
619+
rule["protocol"] = r.Protocol
620+
rule["traffic_type"] = strings.ToLower(r.Traffictype)
621+
rule["cidr_list"] = cidrs
562622
rules.Add(rule)
563-
log.Printf("[DEBUG] Added TCP/UDP rule with no ports to state: %+v", rule)
623+
log.Printf("[DEBUG] Added TCP/UDP rule with no port to state: %+v", rule)
564624
}
565625
}
566626
}
@@ -791,19 +851,26 @@ func verifyNetworkACLRuleParams(d *schema.ResourceData, rule map[string]interfac
791851
// No additional test are needed
792852
log.Printf("[DEBUG] Protocol 'all' validated")
793853
case "tcp", "udp":
794-
if ports, ok := rule["ports"].(*schema.Set); ok {
795-
log.Printf("[DEBUG] Found %d ports for TCP/UDP", ports.Len())
796-
for _, port := range ports.List() {
797-
m := splitPorts.FindStringSubmatch(port.(string))
798-
if m == nil {
799-
log.Printf("[ERROR] Invalid port format: %s", port.(string))
800-
return fmt.Errorf(
801-
"%q is not a valid port value. Valid options are '80' or '80-90'", port.(string))
802-
}
854+
// Check if deprecated ports field is used (not allowed for new configurations)
855+
portsSet, hasPortsSet := rule["ports"].(*schema.Set)
856+
portStr, hasPort := rule["port"].(string)
857+
858+
if hasPortsSet && portsSet.Len() > 0 {
859+
log.Printf("[ERROR] Deprecated ports field used in new configuration")
860+
return fmt.Errorf("The 'ports' field is deprecated. Use 'port' instead for new configurations.")
861+
}
862+
863+
// Validate the new port field if used
864+
if hasPort && portStr != "" {
865+
log.Printf("[DEBUG] Found port for TCP/UDP: %s", portStr)
866+
m := splitPorts.FindStringSubmatch(portStr)
867+
if m == nil {
868+
log.Printf("[ERROR] Invalid port format: %s", portStr)
869+
return fmt.Errorf(
870+
"%q is not a valid port value. Valid options are '80' or '80-90'", portStr)
803871
}
804872
} else {
805-
log.Printf("[DEBUG] No ports specified for TCP/UDP, assuming empty set")
806-
// Allow empty ports for TCP/UDP (your config has no ports)
873+
log.Printf("[DEBUG] No port specified for TCP/UDP, allowing empty port")
807874
}
808875
default:
809876
_, err := strconv.ParseInt(protocol, 0, 0)

0 commit comments

Comments
 (0)