123 lines
3.6 KiB
Go
123 lines
3.6 KiB
Go
package pipeline
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"evobgp/internal/store"
|
|
)
|
|
|
|
// moduleIngestInputHash fingerprints module config and child entries so snapshots invalidate on CRUD.
|
|
func moduleIngestInputHash(st store.Backend, tenantID string, mod *store.Module) (string, error) {
|
|
if st == nil || mod == nil {
|
|
return "", fmt.Errorf("pipeline: module hash: missing store or module")
|
|
}
|
|
h := sha256.New()
|
|
_, _ = fmt.Fprintf(h, "type=%s\n", strings.TrimSpace(mod.Type))
|
|
_, _ = fmt.Fprintf(h, "enabled=%t\n", mod.Enabled)
|
|
if mod.DefaultCommunityID != nil {
|
|
_, _ = fmt.Fprintf(h, "default_community=%s\n", strings.TrimSpace(*mod.DefaultCommunityID))
|
|
}
|
|
_, _ = fmt.Fprintf(h, "doh_policy=%s\n", store.NormalizeDohResolverPolicy(mod.DohResolverPolicy))
|
|
for _, pid := range mod.EffectiveDohProfileIDs() {
|
|
_, _ = fmt.Fprintf(h, "doh_profile=%s\n", pid)
|
|
if prof, err := st.GetDohProfile(tenantID, pid); err == nil && prof != nil {
|
|
_, _ = fmt.Fprintf(h, "doh_url=%s\n", strings.TrimSpace(prof.URL))
|
|
if prof.TimeoutMs != nil {
|
|
_, _ = fmt.Fprintf(h, "doh_timeout=%d\n", *prof.TimeoutMs)
|
|
}
|
|
}
|
|
}
|
|
|
|
switch mod.Type {
|
|
case "IP_RANGES":
|
|
list, err := st.ListIPRangeEntries(tenantID, mod.ID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
sort.Slice(list, func(i, j int) bool { return list[i].Prefix < list[j].Prefix })
|
|
for _, e := range list {
|
|
comm := ""
|
|
if e.CommunityID != nil {
|
|
comm = *e.CommunityID
|
|
}
|
|
_, _ = fmt.Fprintf(h, "ip=%s|c=%s\n", e.Prefix, comm)
|
|
}
|
|
case "AS_PREFIXES":
|
|
list, err := st.ListASEntries(tenantID, mod.ID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
sort.Slice(list, func(i, j int) bool { return list[i].ASN < list[j].ASN })
|
|
for _, e := range list {
|
|
comm := ""
|
|
if e.CommunityID != nil {
|
|
comm = *e.CommunityID
|
|
}
|
|
_, _ = fmt.Fprintf(h, "as=%d|c=%s\n", e.ASN, comm)
|
|
}
|
|
case "CDN_CIDRS":
|
|
list, err := st.ListCDNSources(tenantID, mod.ID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
sort.Slice(list, func(i, j int) bool { return list[i].ID < list[j].ID })
|
|
for _, s := range list {
|
|
comm := ""
|
|
if s.CommunityID != nil {
|
|
comm = *s.CommunityID
|
|
}
|
|
interval := 0
|
|
if s.RefreshIntervalSec != nil {
|
|
interval = *s.RefreshIntervalSec
|
|
}
|
|
_, _ = fmt.Fprintf(h, "cdn=%s|url=%s|kind=%s|path=%s|c=%s|etag=%s|interval=%d\n",
|
|
s.ID, strings.TrimSpace(s.URL), s.SourceKind, strings.TrimSpace(s.PrefixPath), comm,
|
|
strings.TrimSpace(s.Etag), interval)
|
|
}
|
|
case "DOMAINS":
|
|
list, err := st.ListDomainEntries(tenantID, mod.ID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
sort.Slice(list, func(i, j int) bool { return list[i].FQDN < list[j].FQDN })
|
|
for _, e := range list {
|
|
comm := ""
|
|
if e.CommunityID != nil {
|
|
comm = *e.CommunityID
|
|
}
|
|
_, _ = fmt.Fprintf(h, "dom=%s|c=%s\n", strings.TrimSpace(e.FQDN), comm)
|
|
}
|
|
default:
|
|
_, _ = fmt.Fprintf(h, "unknown_type=%s\n", mod.Type)
|
|
}
|
|
return hex.EncodeToString(h.Sum(nil)), nil
|
|
}
|
|
|
|
func persistModuleSnapshot(st store.Backend, tenantID string, mod *store.Module, rows []store.PrefixRow) error {
|
|
if st == nil || mod == nil {
|
|
return nil
|
|
}
|
|
hash, err := moduleIngestInputHash(st, tenantID, mod)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return st.SetModulePrefixSnapshot(tenantID, mod.ID, hash, rows)
|
|
}
|
|
|
|
func moduleRowsFromSnapshot(st store.Backend, tenantID string, mod *store.Module) ([]store.PrefixRow, bool, error) {
|
|
hash, err := moduleIngestInputHash(st, tenantID, mod)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
snap, ok, err := st.GetModulePrefixSnapshot(tenantID, mod.ID)
|
|
if err != nil || !ok || snap == nil || snap.InputHash != hash {
|
|
return nil, false, err
|
|
}
|
|
cp := append([]store.PrefixRow(nil), snap.Prefixes...)
|
|
return cp, true, nil
|
|
}
|