CI / changes (push) Successful in 6s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 39s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 15s
CI / docker-go-prime (push) Successful in 24s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 59s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 6m8s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m22s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m21s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m22s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m6s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m20s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m20s
Updated the worker test cases to improve the handling of job status checks. Added nil checks for job instances and refined the logic to verify job statuses directly from snapshots, ensuring more robust test coverage and preventing potential nil pointer dereferences.
179 lines
5.4 KiB
Go
179 lines
5.4 KiB
Go
package jobs
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"evobgp/internal/store"
|
|
)
|
|
|
|
func TestParallelModuleRefresh_CoalescesDeployApply(t *testing.T) {
|
|
t.Setenv("EVOBGP_ASN_RESOLVE", "0")
|
|
t.Setenv("EVOBGP_BIRD_ACTIVE_DIR", "") // skip bird binary path in deploy_apply
|
|
|
|
m := store.NewMemory()
|
|
m.SeedDemo()
|
|
tenant, _, modIP, _, _ := m.DemoIDs()
|
|
for _, mod := range m.ListModules(tenant) {
|
|
if mod == nil || mod.ID == modIP {
|
|
continue
|
|
}
|
|
disabled := false
|
|
if _, err := m.UpdateModule(tenant, mod.ID, &store.ModulePatch{Enabled: &disabled}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
mod2, err := m.CreateModule(tenant, &store.Module{Type: "IP_RANGES", Name: "extra-ip", Enabled: true, Priority: 30})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := m.CreateIPRangeEntry(tenant, modIP, &store.IPRangeEntry{Prefix: "10.0.0.0/24"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := m.CreateIPRangeEntry(tenant, mod2.ID, &store.IPRangeEntry{Prefix: "192.168.0.0/24"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
wk := &Worker{Store: m}
|
|
reg := NewRegistry(wk.Process)
|
|
wk.Registry = reg
|
|
|
|
mid1 := modIP
|
|
mid2 := mod2.ID
|
|
if _, _, err := reg.Enqueue(tenant, KindModuleRefresh, nil, &mid1, map[string]any{"module_id": modIP}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := reg.Enqueue(tenant, KindModuleRefresh, nil, &mid2, map[string]any{"module_id": mod2.ID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
waitSucceededJobsByKindCount(t, reg, tenant, KindModuleRefresh, 2)
|
|
|
|
deployJobs, _, _ := reg.List(tenant, "", KindDeployApply, "", 100)
|
|
if len(deployJobs) != 1 {
|
|
t.Fatalf("want exactly one deploy_apply job, got %d", len(deployJobs))
|
|
}
|
|
|
|
var deferred, withDeployID int
|
|
refreshJobs, _, _ := reg.List(tenant, "", KindModuleRefresh, "", 100)
|
|
for _, j := range refreshJobs {
|
|
if j == nil {
|
|
continue
|
|
}
|
|
snap := j.Snapshot()
|
|
if st, _ := snap["status"].(string); st != StatusSucceeded {
|
|
continue
|
|
}
|
|
meta, _ := snap["meta"].(map[string]any)
|
|
if v, ok := meta["deploy_apply_deferred"].(bool); ok && v {
|
|
deferred++
|
|
}
|
|
if _, ok := meta["deploy_apply_job_id"].(string); ok {
|
|
withDeployID++
|
|
}
|
|
}
|
|
if deferred != 1 || withDeployID != 1 {
|
|
t.Fatalf("want one deferred and one with deploy_apply_job_id, got deferred=%d deploy_meta=%d", deferred, withDeployID)
|
|
}
|
|
}
|
|
|
|
func waitSucceededModuleRefreshCount(t *testing.T, reg *Registry, tenant string, want int) {
|
|
t.Helper()
|
|
waitSucceededJobsByKindCount(t, reg, tenant, KindModuleRefresh, want)
|
|
}
|
|
|
|
func waitSucceededJobsByKindCount(t *testing.T, reg *Registry, tenant, kind string, want int) {
|
|
t.Helper()
|
|
deadline := time.Now().Add(30 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
jobs, _, _ := reg.List(tenant, StatusSucceeded, kind, "", 100)
|
|
if len(jobs) >= want {
|
|
return
|
|
}
|
|
all, _, _ := reg.List(tenant, "", kind, "", 100)
|
|
for _, j := range all {
|
|
if j == nil {
|
|
continue
|
|
}
|
|
snap := j.Snapshot()
|
|
if st, _ := snap["status"].(string); st != StatusFailed {
|
|
continue
|
|
}
|
|
t.Fatalf("%s job failed: %#v", kind, snap["error"])
|
|
}
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
t.Fatalf("timeout waiting for %s jobs", kind)
|
|
}
|
|
|
|
func TestPeerReconcile_RendersFromLatestRevisionAndQueuesDeploy(t *testing.T) {
|
|
t.Setenv("EVOBGP_ASN_RESOLVE", "0")
|
|
t.Setenv("EVOBGP_BIRD_ACTIVE_DIR", "") // skip bird binary path in deploy_apply
|
|
|
|
m := store.NewMemory()
|
|
m.SeedDemo()
|
|
tenant, _, modIP, _, _ := m.DemoIDs()
|
|
for _, mod := range m.ListModules(tenant) {
|
|
if mod == nil || mod.ID == modIP {
|
|
continue
|
|
}
|
|
disabled := false
|
|
if _, err := m.UpdateModule(tenant, mod.ID, &store.ModulePatch{Enabled: &disabled}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if _, err := m.CreateIPRangeEntry(tenant, modIP, &store.IPRangeEntry{Prefix: "10.10.0.0/24"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
peer, err := m.CreatePeer(tenant, &store.BGPPeer{
|
|
Name: "peer-a",
|
|
Neighbor: "192.0.2.2",
|
|
RemoteASN: 64512,
|
|
Enabled: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
wk := &Worker{Store: m}
|
|
reg := NewRegistry(wk.Process)
|
|
wk.Registry = reg
|
|
|
|
// Create baseline revision from enabled IP module.
|
|
mid := modIP
|
|
if _, _, err := reg.Enqueue(tenant, KindModuleRefresh, nil, &mid, map[string]any{"module_id": modIP}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
waitSucceededJobsByKindCount(t, reg, tenant, KindModuleRefresh, 1)
|
|
beforeRevs, _, _ := m.ListRevisions(tenant, "", "", 200)
|
|
|
|
enabled := false
|
|
if _, err := m.UpdatePeer(tenant, peer.ID, &store.PeerPatch{Enabled: &enabled}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := reg.Enqueue(tenant, KindPeerReconcile, nil, nil, map[string]any{"trigger": "peer_patch"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
waitSucceededJobsByKindCount(t, reg, tenant, KindPeerReconcile, 1)
|
|
|
|
afterRevs, _, _ := m.ListRevisions(tenant, "", "", 200)
|
|
if len(afterRevs) <= len(beforeRevs) {
|
|
t.Fatalf("expected new revision after peer_reconcile, before=%d after=%d", len(beforeRevs), len(afterRevs))
|
|
}
|
|
peerJobs, _, _ := reg.List(tenant, StatusSucceeded, KindPeerReconcile, "", 10)
|
|
if len(peerJobs) == 0 {
|
|
t.Fatal("expected succeeded peer_reconcile job")
|
|
}
|
|
meta := peerJobs[0].Snapshot()["meta"].(map[string]any)
|
|
if _, ok := meta["revision_id"].(string); !ok {
|
|
t.Fatalf("expected revision_id in peer_reconcile meta, got %#v", meta)
|
|
}
|
|
if title, ok := meta["job_title"].(string); !ok || title != "Обновление BGP пиров" {
|
|
t.Fatalf("expected job_title in peer_reconcile meta, got %#v", meta["job_title"])
|
|
}
|
|
if _, ok := meta["deploy_apply_job_id"].(string); !ok {
|
|
t.Fatalf("expected deploy_apply_job_id in peer_reconcile meta, got %#v", meta)
|
|
}
|
|
}
|