Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
90 lines
3.0 KiB
Go
90 lines
3.0 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ed25519"
|
|
"encoding/base64"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"net/http/httptest"
|
|
|
|
"evobgp/internal/bundle"
|
|
)
|
|
|
|
// TestLegacyNodeBinaryCurrentControlPlane exercises an independently built old
|
|
// CLI against today's HTTP handlers. It never calls apply or touches host BIRD.
|
|
func TestLegacyNodeBinaryCurrentControlPlane(t *testing.T) {
|
|
binary := os.Getenv("EVOBGP_TEST_LEGACY_NODE_BIN")
|
|
if binary == "" {
|
|
t.Skip("EVOBGP_TEST_LEGACY_NODE_BIN not set")
|
|
}
|
|
binary, err := filepath.Abs(binary)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(binary); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
srv, err := New(Options{SeedDemo: true, BundleSeedHex: testBundleSeed})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer srv.Close()
|
|
tenant, _, _, revision, speaker := srv.Store().DemoIDs()
|
|
mustSetTestAPIKeys(t, srv, "legacy-node|"+tenant+"|node,other-node|other-tenant|node")
|
|
if err := srv.Store().PublishRevisionForSpeaker(speaker, revision); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
server := httptest.NewServer(srv.Handler())
|
|
defer server.Close()
|
|
run := func(wantSuccess bool, args ...string) {
|
|
t.Helper()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer cancel()
|
|
output, err := exec.CommandContext(ctx, binary, args...).CombinedOutput()
|
|
if ctx.Err() != nil {
|
|
t.Fatalf("legacy CLI timed out: %v", ctx.Err())
|
|
}
|
|
if wantSuccess && err != nil {
|
|
t.Fatalf("legacy CLI failed: %v: %s", err, output)
|
|
}
|
|
if !wantSuccess && err == nil {
|
|
t.Fatalf("legacy CLI unexpectedly accepted invalid request: %s", output)
|
|
}
|
|
}
|
|
path := filepath.Join(t.TempDir(), "current-bundle.tar.gz")
|
|
run(true, "pull-bundle", "-base-url", server.URL, "-token", "legacy-node", "-speaker-id", speaker, "-o", path)
|
|
run(true, "verify-bundle", "-f", path, "-pubkey-base64", srv.BundlePublicKeyBase64())
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pub, err := base64.StdEncoding.DecodeString(srv.BundlePublicKeyBase64())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
verified, err := bundle.VerifyGzippedTar(raw, ed25519.PublicKey(pub))
|
|
if err != nil || verified.Manifest.RevisionID != revision || verified.Manifest.SpeakerID != speaker {
|
|
t.Fatalf("legacy CLI fetched the wrong revision/speaker or an invalid bundle: %v", err)
|
|
}
|
|
// The archive is valid, but verification with an unrelated key must fail.
|
|
run(false, "verify-bundle", "-f", path, "-pubkey-hex", strings.Repeat("00", ed25519.PublicKeySize))
|
|
for _, token := range []string{"invalid-node", "other-node"} {
|
|
denied := filepath.Join(t.TempDir(), "denied.tar.gz")
|
|
run(false, "pull-bundle", "-base-url", server.URL, "-token", token, "-speaker-id", speaker, "-o", denied)
|
|
if _, err := os.Stat(denied); !os.IsNotExist(err) {
|
|
t.Fatalf("unauthorized CLI wrote a bundle: %v", err)
|
|
}
|
|
}
|
|
// A damaged payload must not pass the old verifier with the correct key.
|
|
if err := os.WriteFile(path, []byte("not a signed bundle"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
run(false, "verify-bundle", "-f", path, "-pubkey-base64", srv.BundlePublicKeyBase64())
|
|
}
|