127 lines
3.5 KiB
Go
127 lines
3.5 KiB
Go
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,
|
|
}
|
|
}
|