package app import ( "fmt" "sort" ) func validateTransportPolicy(next []TransportPolicyIntent, current []TransportPolicyIntent, clients []TransportClient) transportValidationResult { clientByID := map[string]TransportClient{} for _, c := range clients { clientByID[c.ID] = c } type ownerSet map[string]struct{} ownership := map[string]ownerSet{} seenSameOwner := map[string]int{} cidrs := make([]cidrIntent, 0) conflicts := make([]TransportConflictRecord, 0) block := 0 warn := 0 normalized := make([]TransportPolicyIntent, 0, len(next)) for i, raw := range next { norm, key, pfx, err := normalizeTransportIntent(raw) if err != nil { conflicts = append(conflicts, TransportConflictRecord{ Key: fmt.Sprintf("intent:%d", i), Type: "invalid_intent", Severity: "block", Reason: err.Error(), SuggestedResolution: "fix selector/client fields", }) block++ continue } if _, ok := clientByID[norm.ClientID]; !ok { conflicts = append(conflicts, TransportConflictRecord{ Key: key, Type: "unknown_client", Severity: "block", Owners: []string{norm.ClientID}, Reason: "client not found", SuggestedResolution: "create client or fix client_id", }) block++ continue } if ownership[key] == nil { ownership[key] = ownerSet{} } ownership[key][norm.ClientID] = struct{}{} seenSameOwner[key+"|"+norm.ClientID]++ if seenSameOwner[key+"|"+norm.ClientID] > 1 { conflicts = append(conflicts, TransportConflictRecord{ Key: key, Type: "duplicate_intent", Severity: "warn", Owners: []string{norm.ClientID}, Reason: "duplicate selector for same client", SuggestedResolution: "deduplicate identical intents", }) warn++ } if pfx.IsValid() { cidrs = append(cidrs, cidrIntent{ClientID: norm.ClientID, Prefix: pfx, Key: key}) } normalized = append(normalized, norm) } for key, owners := range ownership { if len(owners) <= 1 { continue } own := make([]string, 0, len(owners)) for id := range owners { own = append(own, id) } sort.Strings(own) conflicts = append(conflicts, TransportConflictRecord{ Key: key, Type: "ownership", Severity: "block", Owners: own, Reason: "selector is assigned to multiple clients", SuggestedResolution: "keep exactly one owner for selector", }) block++ } for i := 0; i < len(cidrs); i++ { for j := i + 1; j < len(cidrs); j++ { a, b := cidrs[i], cidrs[j] if a.ClientID == b.ClientID { continue } if !prefixOverlap(a.Prefix, b.Prefix) { continue } key := "cidr_overlap:" + a.Prefix.String() + "|" + b.Prefix.String() conflicts = append(conflicts, TransportConflictRecord{ Key: key, Type: "cidr_overlap", Severity: "block", Owners: []string{a.ClientID, b.ClientID}, Reason: "CIDR selectors overlap across different clients", SuggestedResolution: "split CIDR ranges or keep one owner", }) block++ } } conflicts = dedupeTransportConflicts(conflicts) diff := computeTransportPolicyDiff(current, normalized) return transportValidationResult{ Normalized: normalized, Conflicts: conflicts, Summary: TransportPolicyValidateSummary{ BlockCount: block, WarnCount: warn, }, Diff: diff, Valid: block == 0, } }