feat(httpapi): harden control plane and speed up prefix refresh
Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
924f111e48
commit
f8e0520817
197 files changed
+18655
-1416
No files matched your search
@@ -0,0 +1,110 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"evobgp/internal/httpclient"
|
||||
"evobgp/internal/jobs"
|
||||
"evobgp/internal/pipeline"
|
||||
"evobgp/internal/store"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestBootstrapWorkerPreservesExplicitPrivateDOH(t *testing.T) {
|
||||
t.Setenv("EVOBGP_NODE_DISPATCH_ENABLED", "0")
|
||||
t.Setenv("EVOBGP_BIRDC_SOCKET", "")
|
||||
t.Setenv("EVOBGP_CDN_ALLOW_PRIVATE", "0")
|
||||
var calls atomic.Int32
|
||||
resolver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
calls.Add(1)
|
||||
raw, err := base64.RawURLEncoding.DecodeString(r.URL.Query().Get("dns"))
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
query := new(dns.Msg)
|
||||
if err = query.Unpack(raw); err != nil || len(query.Question) == 0 {
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
answer := new(dns.Msg)
|
||||
answer.SetReply(query)
|
||||
if query.Question[0].Qtype == dns.TypeA {
|
||||
answer.Answer = []dns.RR{&dns.A{Hdr: dns.RR_Header{Name: query.Question[0].Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60}, A: net.IPv4(192, 0, 2, 20)}}
|
||||
}
|
||||
body, err := answer.Pack()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/dns-message")
|
||||
_, _ = w.Write(body)
|
||||
}))
|
||||
defer resolver.Close()
|
||||
st, reg, _, err := BootstrapWorkers(context.Background(), Options{SeedDemo: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
reg.RequestCancelAll()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if e := reg.Drain(ctx); e != nil {
|
||||
t.Error(e)
|
||||
}
|
||||
}()
|
||||
tenant, _, _, _, _ := st.DemoIDs()
|
||||
profile, err := st.CreateDohProfile(tenant, &store.DohProfile{Name: "explicit private resolver", URL: resolver.URL})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mod, err := st.CreateModule(tenant, &store.Module{Name: "private-doh", Type: "DOMAINS", Enabled: true, DohProfileIDs: []string{profile.ID}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = st.CreateDomainEntry(tenant, mod.ID, &store.DomainEntry{FQDN: "private-resolver.example.test"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
j, _, err := reg.Enqueue(tenant, jobs.KindModuleRefresh, nil, &mod.ID, map[string]any{"module_id": mod.ID})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deadline := time.After(5 * time.Second)
|
||||
ticker := time.NewTicker(5 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
snap := j.Snapshot()
|
||||
status := snap["status"]
|
||||
if status == jobs.StatusSucceeded {
|
||||
break
|
||||
}
|
||||
if status == jobs.StatusFailed || status == jobs.StatusCancelled {
|
||||
t.Fatalf("worker refresh failed: %+v", snap)
|
||||
}
|
||||
select {
|
||||
case <-deadline:
|
||||
t.Fatal("worker did not finish")
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
if calls.Load() < 2 {
|
||||
t.Fatalf("bootstrap worker blocked private DoH: %d requests", calls.Load())
|
||||
}
|
||||
// The same general-client choice must not remove CDN's independent ACL.
|
||||
cdn := httptest.NewTLSServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { t.Error("private CDN reached") }))
|
||||
defer cdn.Close()
|
||||
req, err := http.NewRequest(http.MethodGet, cdn.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = pipeline.UpstreamHTTPDo(context.Background(), httpclient.New(httpclient.DefaultTimeout), req); err == nil {
|
||||
t.Fatal("private CDN accepted")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user