Skip to content

Commit 2d92457

Browse files
intel352claude
andcommitted
fix: show firewall rules detail in infra plan output
Firewall section now displays each inbound/outbound rule with protocol, port, and source IPs. Fixed key mismatch (inbound_rules vs inbound) and sources (plural array) vs source (singular string). Each rule gets its own line for clarity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fba4f35 commit 2d92457

File tree

1 file changed

+78
-6
lines changed

1 file changed

+78
-6
lines changed

cmd/wfctl/infra.go

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -413,12 +413,23 @@ func resourceSummaryKeys(resType string, cfg map[string]any) [][2]string {
413413

414414
case "infra.firewall":
415415
add(&pairs, "name", str("name"))
416-
// Inbound rules summary.
417-
if inbound, ok := cfg["inbound"]; ok {
418-
add(&pairs, "inbound", formatFirewallRules(inbound))
416+
// Inbound rules — try both key variants.
417+
for _, key := range []string{"inbound_rules", "inbound"} {
418+
if rules, ok := cfg[key]; ok {
419+
for _, line := range formatFirewallRulesList(rules) {
420+
add(&pairs, "allow inbound", line)
421+
}
422+
break
423+
}
419424
}
420-
if outbound, ok := cfg["outbound"]; ok {
421-
add(&pairs, "outbound", formatFirewallRules(outbound))
425+
// Outbound rules.
426+
for _, key := range []string{"outbound_rules", "outbound"} {
427+
if rules, ok := cfg[key]; ok {
428+
for _, line := range formatFirewallRulesList(rules) {
429+
add(&pairs, "allow outbound", line)
430+
}
431+
break
432+
}
422433
}
423434

424435
case "infra.database":
@@ -493,7 +504,68 @@ func resourceSummaryKeys(resType string, cfg map[string]any) [][2]string {
493504
return pairs
494505
}
495506

496-
// formatFirewallRules produces a compact summary of firewall rule config.
507+
// formatFirewallRulesList returns one summary line per firewall rule.
508+
func formatFirewallRulesList(v any) []string {
509+
rules, ok := v.([]any)
510+
if !ok {
511+
return nil
512+
}
513+
var lines []string
514+
for _, r := range rules {
515+
rm, ok := r.(map[string]any)
516+
if !ok {
517+
continue
518+
}
519+
var parts []string
520+
if proto, ok := rm["protocol"].(string); ok && proto != "" {
521+
parts = append(parts, strings.ToUpper(proto))
522+
}
523+
if ports, ok := rm["ports"].(string); ok && ports != "" {
524+
parts = append(parts, ports)
525+
}
526+
// Handle both "source"/"sources" and "destination"/"destinations".
527+
if src := extractSources(rm, "source", "sources"); src != "" {
528+
parts = append(parts, "from "+src)
529+
}
530+
if dst := extractSources(rm, "destination", "destinations"); dst != "" {
531+
parts = append(parts, "to "+dst)
532+
}
533+
if len(parts) > 0 {
534+
lines = append(lines, strings.Join(parts, " "))
535+
}
536+
}
537+
return lines
538+
}
539+
540+
// extractSources gets a string or []string from a map, trying both singular and plural keys.
541+
func extractSources(m map[string]any, singular, plural string) string {
542+
// Try plural first (array of IPs).
543+
if v, ok := m[plural]; ok {
544+
switch sv := v.(type) {
545+
case []any:
546+
strs := make([]string, 0, len(sv))
547+
for _, s := range sv {
548+
if str, ok := s.(string); ok {
549+
strs = append(strs, str)
550+
}
551+
}
552+
return strings.Join(strs, ",")
553+
case []string:
554+
return strings.Join(sv, ",")
555+
case string:
556+
return sv
557+
}
558+
}
559+
// Try singular.
560+
if v, ok := m[singular]; ok {
561+
if s, ok := v.(string); ok {
562+
return s
563+
}
564+
}
565+
return ""
566+
}
567+
568+
// formatFirewallRules produces a compact summary of firewall rule config (legacy, single-line).
497569
func formatFirewallRules(v any) string {
498570
switch rules := v.(type) {
499571
case []any:

0 commit comments

Comments
 (0)