feat: implement peer reconciliation job for BGP peers
CI / changes (push) Successful in 7s
CI / openapi (push) Successful in 23s
CI / go (push) Failing after 30s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m5s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m3s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Has been skipped
CI / docker-go-prime (push) Has been skipped
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Has been skipped
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Has been skipped
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Has been skipped
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Has been skipped
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Has been skipped
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Has been skipped
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Has been skipped
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Has been skipped
CI / changes (push) Successful in 7s
CI / openapi (push) Successful in 23s
CI / go (push) Failing after 30s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m5s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m3s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Has been skipped
CI / docker-go-prime (push) Has been skipped
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Has been skipped
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Has been skipped
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Has been skipped
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Has been skipped
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Has been skipped
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Has been skipped
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Has been skipped
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Has been skipped
Added a new job type `peer_reconcile` to handle fast reconciliation of BGP peers without module ingestion. Updated API documentation to reflect the new job's functionality and its automatic application to speakers post-reconciliation. Enhanced the HTTP API to enqueue reconciliation jobs during peer creation, updates, and deletions, ensuring efficient state management for BGP configurations.
This commit is contained in:
@@ -72,7 +72,34 @@ func RenderTenantRevision(ctx context.Context, st store.Backend, hc *http.Client
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hash := hashAggregatedMaterialization(tenantID, agg)
|
||||
hash := hashAggregatedMaterializationWithPeers(st, tenantID, agg)
|
||||
if prev := latestTenantRevision(st, tenantID); prev != nil && prev.ContentHash == hash {
|
||||
return prev.ID, nil
|
||||
}
|
||||
agg = smartAggregatePrefixRows(agg)
|
||||
|
||||
revisionID = uuid.NewString()
|
||||
parent := parentRevision(st, tenantID, triggerModuleID)
|
||||
preview, err := buildPreviewFragments(st, tenantID, triggerModuleID, revisionID, agg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := st.CreateRenderRevision(revisionID, tenantID, triggerModuleID, parent, hash, preview, agg); err != nil {
|
||||
return "", err
|
||||
}
|
||||
applyRevisionRetention(st, tenantID)
|
||||
return revisionID, nil
|
||||
}
|
||||
|
||||
// RenderTenantRevisionFromPrefixes renders one tenant-wide revision from already materialized prefixes.
|
||||
// This is used for fast paths (e.g. peer-only changes) to avoid ingest/external fetches.
|
||||
func RenderTenantRevisionFromPrefixes(ctx context.Context, st store.Backend, hc *http.Client, tenantID, triggerModuleID string, rows []store.PrefixRow) (revisionID string, err error) {
|
||||
_ = ctx
|
||||
if hc == nil {
|
||||
hc = http.DefaultClient
|
||||
}
|
||||
agg := append([]store.PrefixRow(nil), rows...)
|
||||
hash := hashAggregatedMaterializationWithPeers(st, tenantID, agg)
|
||||
if prev := latestTenantRevision(st, tenantID); prev != nil && prev.ContentHash == hash {
|
||||
return prev.ID, nil
|
||||
}
|
||||
@@ -679,6 +706,69 @@ func hashAggregatedMaterialization(tenantID string, rows []store.PrefixRow) stri
|
||||
return fmt.Sprintf("sha256:%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
func hashAggregatedMaterializationWithPeers(st store.Backend, tenantID string, rows []store.PrefixRow) string {
|
||||
type line struct{ p, c, s string }
|
||||
var lines []line
|
||||
for _, r := range rows {
|
||||
c := ""
|
||||
if r.CommunityID != nil {
|
||||
c = *r.CommunityID
|
||||
}
|
||||
lines = append(lines, line{r.Prefix, c, r.Source})
|
||||
}
|
||||
sort.Slice(lines, func(i, j int) bool {
|
||||
if lines[i].p != lines[j].p {
|
||||
return lines[i].p < lines[j].p
|
||||
}
|
||||
if lines[i].c != lines[j].c {
|
||||
return lines[i].c < lines[j].c
|
||||
}
|
||||
return lines[i].s < lines[j].s
|
||||
})
|
||||
h := sha256.New()
|
||||
h.Write([]byte(strings.TrimSpace(tenantID)))
|
||||
h.Write([]byte{0})
|
||||
for _, l := range lines {
|
||||
h.Write([]byte(l.p))
|
||||
h.Write([]byte{1})
|
||||
h.Write([]byte(l.c))
|
||||
h.Write([]byte{1})
|
||||
h.Write([]byte(l.s))
|
||||
h.Write([]byte{0})
|
||||
}
|
||||
h.Write([]byte("peers"))
|
||||
h.Write([]byte{0})
|
||||
peers := st.ListPeers(tenantID)
|
||||
sort.Slice(peers, func(i, j int) bool {
|
||||
if peers[i] == nil || peers[j] == nil {
|
||||
return i < j
|
||||
}
|
||||
return peers[i].ID < peers[j].ID
|
||||
})
|
||||
for _, p := range peers {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
speakerID := ""
|
||||
if p.SpeakerID != nil {
|
||||
speakerID = strings.TrimSpace(*p.SpeakerID)
|
||||
}
|
||||
h.Write([]byte(strings.TrimSpace(p.ID)))
|
||||
h.Write([]byte{1})
|
||||
h.Write([]byte(strings.TrimSpace(p.Neighbor)))
|
||||
h.Write([]byte{1})
|
||||
h.Write([]byte(strconv.FormatInt(p.RemoteASN, 10)))
|
||||
h.Write([]byte{1})
|
||||
h.Write([]byte(strconv.FormatBool(p.Enabled)))
|
||||
h.Write([]byte{1})
|
||||
h.Write([]byte(strings.TrimSpace(p.PoliciesJSON)))
|
||||
h.Write([]byte{1})
|
||||
h.Write([]byte(speakerID))
|
||||
h.Write([]byte{0})
|
||||
}
|
||||
return fmt.Sprintf("sha256:%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
func buildPreviewFragments(st store.Backend, tenantID, moduleID, revisionID string, rows []store.PrefixRow) (map[string]string, error) {
|
||||
v4, v6, pathASNs, staticGroups, err := materializeRowsForBird(st, tenantID, rows)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user