Skip to content

Commit 1d3bec9

Browse files
Fix ACL rule validation and port ordering issues
This commit addresses three code review comments: 1. Add validation for ruleset create path - The ruleset create path was skipping verifyNetworkACLRuleParams before calling createNetworkACLRules - Added validation loop to verify each ruleset element before creation - This prevents panics from unchecked type assertions in createNetworkACLRule 2. Add validation for all rule creation paths - createNetworkACLRule is called from Create and Update paths for both 'rule' and 'ruleset' fields - Added validation in Update path for both 'rule' and 'ruleset' before calling updateNetworkACLRules - Ensures all new rules are validated before creation, preventing less actionable CloudStack API errors 3. Sort ports for deterministic rule numbering - When expanding deprecated 'ports' field, iteration used portsSet.List() which is unordered - Rule numbers were derived as baseRuleNum + portIndex, causing non-deterministic numbering across runs - Added sort.Strings() to ensure stable, deterministic rule number assignment Additionally, consolidated duplicate validation loops into a helper function validateRulesList() to improve code maintainability and reduce duplication across Create and Update paths. All changes tested with acceptance tests: - TestAccCloudStackNetworkACLRule_basic - TestAccCloudStackNetworkACLRule_update - TestAccCloudStackNetworkACLRule_ruleset_basic - TestAccCloudStackNetworkACLRule_ruleset_update - TestAccCloudStackNetworkACLRule_deprecated_ports
1 parent 4e43e06 commit 1d3bec9

File tree

1 file changed

+41
-7
lines changed

1 file changed

+41
-7
lines changed

cloudstack/resource_cloudstack_network_acl_rule.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -456,11 +456,8 @@ func resourceCloudStackNetworkACLRuleCreate(d *schema.ResourceData, meta interfa
456456
log.Printf("[DEBUG] Processing %d rules from 'rule' field", len(nrs))
457457

458458
// Validate rules BEFORE assigning numbers, so we can detect user-provided rule_number
459-
for _, rule := range nrs {
460-
if err := verifyNetworkACLRuleParams(d, rule.(map[string]interface{})); err != nil {
461-
log.Printf("[ERROR] Failed to verify rule parameters: %v", err)
462-
return err
463-
}
459+
if err := validateRulesList(d, nrs, "rule"); err != nil {
460+
return err
464461
}
465462

466463
// Assign rule numbers to rules that don't have them
@@ -490,6 +487,11 @@ func resourceCloudStackNetworkACLRuleCreate(d *schema.ResourceData, meta interfa
490487
// Convert Set to list (no auto-numbering needed, rule_number is required)
491488
rulesList := nrs.List()
492489

490+
// Validate rules BEFORE creating them
491+
if err := validateRulesList(d, rulesList, "ruleset"); err != nil {
492+
return err
493+
}
494+
493495
err := createNetworkACLRules(d, meta, &rules, rulesList)
494496
if err != nil {
495497
log.Printf("[ERROR] Failed to create network ACL rules: %v", err)
@@ -667,9 +669,18 @@ func createNetworkACLRule(d *schema.ResourceData, meta interface{}, rule map[str
667669
baseRuleNum = ruleNum
668670
}
669671

672+
// Convert TypeSet to sorted list for deterministic rule number assignment
673+
// This ensures that rule numbers are stable across runs
674+
portsList := portsSet.List()
675+
portsStrings := make([]string, len(portsList))
676+
for i, port := range portsList {
677+
portsStrings[i] = port.(string)
678+
}
679+
sort.Strings(portsStrings)
680+
log.Printf("[DEBUG] Sorted ports for deterministic numbering: %v", portsStrings)
681+
670682
portIndex := 0
671-
for _, port := range portsSet.List() {
672-
portValue := port.(string)
683+
for _, portValue := range portsStrings {
673684

674685
// Check if this port already has a UUID
675686
if _, hasUUID := getRuleUUID(rule, portValue); !hasUUID {
@@ -1153,6 +1164,11 @@ func resourceCloudStackNetworkACLRuleUpdate(d *schema.ResourceData, meta interfa
11531164

11541165
log.Printf("[DEBUG] Rule list changed, performing efficient updates")
11551166

1167+
// Validate new rules BEFORE assigning numbers
1168+
if err := validateRulesList(d, newRules, "rule"); err != nil {
1169+
return err
1170+
}
1171+
11561172
// Assign rule numbers to new rules that don't have them
11571173
newRulesWithNumbers := assignRuleNumbers(newRules)
11581174

@@ -1170,6 +1186,11 @@ func resourceCloudStackNetworkACLRuleUpdate(d *schema.ResourceData, meta interfa
11701186

11711187
log.Printf("[DEBUG] Ruleset changed: %d old rules -> %d new rules", len(oldRules), len(newRules))
11721188

1189+
// Validate new rules BEFORE updating
1190+
if err := validateRulesList(d, newRules, "ruleset"); err != nil {
1191+
return err
1192+
}
1193+
11731194
// No migration needed for ruleset (no deprecated 'ports' field)
11741195
// No auto-numbering needed (rule_number is required)
11751196
err := updateNetworkACLRules(d, meta, oldRules, newRules)
@@ -1273,6 +1294,19 @@ func verifyNetworkACLParams(d *schema.ResourceData) error {
12731294
return nil
12741295
}
12751296

1297+
// validateRulesList validates all rules in a list by calling verifyNetworkACLRuleParams on each
1298+
// This helper consolidates the validation logic used in Create and Update paths for both 'rule' and 'ruleset' fields
1299+
func validateRulesList(d *schema.ResourceData, rules []interface{}, fieldName string) error {
1300+
for i, rule := range rules {
1301+
if err := verifyNetworkACLRuleParams(d, rule.(map[string]interface{})); err != nil {
1302+
log.Printf("[ERROR] Failed to verify %s rule %d parameters: %v", fieldName, i, err)
1303+
return fmt.Errorf("validation failed for %s rule %d: %w", fieldName, i, err)
1304+
}
1305+
}
1306+
log.Printf("[DEBUG] Successfully validated %d %s rules", len(rules), fieldName)
1307+
return nil
1308+
}
1309+
12761310
func verifyNetworkACLRuleParams(d *schema.ResourceData, rule map[string]interface{}) error {
12771311
log.Printf("[DEBUG] Verifying parameters for rule: %+v", rule)
12781312

0 commit comments

Comments
 (0)