Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <[email protected]>
121 lines
3.6 KiB
Go
121 lines
3.6 KiB
Go
package bundle
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"compress/gzip"
|
|
"crypto/ed25519"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
)
|
|
|
|
// legacyManifestV1 mirrors the decoder fields present in the locally available
|
|
// v1.0.0 source. It deliberately has no future capability or deployment fields.
|
|
type legacyManifestV1 struct {
|
|
RevisionID string `json:"revision_id"`
|
|
SpeakerID string `json:"speaker_id"`
|
|
CreatedAt string `json:"created_at"`
|
|
Files []struct {
|
|
Path string `json:"path"`
|
|
SHA256 string `json:"sha256"`
|
|
} `json:"files"`
|
|
Algorithm string `json:"signature_algorithm"`
|
|
PublicKeyBase64 string `json:"public_key_base64"`
|
|
}
|
|
|
|
func TestLegacyCompatibilityFrozenSignedCurrentBaseline(t *testing.T) {
|
|
raw, err := os.ReadFile("testdata/current_baseline_signed_bundle.tar.gz")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pub := ed25519.NewKeyFromSeed(bytes.Repeat([]byte{0x42}, ed25519.SeedSize)).Public().(ed25519.PublicKey)
|
|
verified, err := VerifyGzippedTar(raw, pub)
|
|
if err != nil {
|
|
t.Fatalf("frozen signed baseline rejected: %v", err)
|
|
}
|
|
if verified.Manifest.RevisionID != "revision-legacy" || verified.Manifest.SpeakerID != "speaker-legacy" || len(verified.Files) != 2 {
|
|
t.Fatalf("frozen baseline contents: %+v", verified.Manifest)
|
|
}
|
|
}
|
|
|
|
func TestLegacyCompatibilityBundleWire(t *testing.T) {
|
|
seed := bytes.Repeat([]byte{0x42}, ed25519.SeedSize)
|
|
priv := ed25519.NewKeyFromSeed(seed)
|
|
pub := priv.Public().(ed25519.PublicKey)
|
|
fragments := map[string]string{
|
|
"bird.conf": "router id 192.0.2.1;\n",
|
|
"bird.d/evobgp_prefixes_v4.conf": "# revision\n",
|
|
"_preview": "must not ship",
|
|
}
|
|
raw, err := BuildGzippedTar("revision-legacy", "speaker-legacy", fragments, priv)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
gr, err := gzip.NewReader(bytes.NewReader(raw))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = gr.Close() }()
|
|
tr := tar.NewReader(gr)
|
|
entries := map[string][]byte{}
|
|
var order []string
|
|
for {
|
|
h, err := tr.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body, err := io.ReadAll(tr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
order = append(order, h.Name)
|
|
entries[h.Name] = body
|
|
}
|
|
wantOrder := []string{"manifest.json", "bundle.sig", "bird.conf", "bird.d/evobgp_prefixes_v4.conf"}
|
|
if !reflect.DeepEqual(order, wantOrder) {
|
|
t.Fatalf("legacy tar layout: got %v, want %v", order, wantOrder)
|
|
}
|
|
var top map[string]json.RawMessage
|
|
if err := json.Unmarshal(entries["manifest.json"], &top); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wantFields := []string{"created_at", "files", "public_key_base64", "revision_id", "signature_algorithm", "speaker_id"}
|
|
fields := make([]string, 0, len(top))
|
|
for k := range top {
|
|
fields = append(fields, k)
|
|
}
|
|
sort.Strings(fields)
|
|
if !reflect.DeepEqual(fields, wantFields) {
|
|
t.Fatalf("manifest fields: got %v, want %v", fields, wantFields)
|
|
}
|
|
var m legacyManifestV1
|
|
if err := json.Unmarshal(entries["manifest.json"], &m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if m.RevisionID != "revision-legacy" || m.SpeakerID != "speaker-legacy" || m.CreatedAt == "" || m.Algorithm != "ed25519" || m.PublicKeyBase64 != base64.StdEncoding.EncodeToString(pub) {
|
|
t.Fatalf("legacy manifest fields: %+v", m)
|
|
}
|
|
if !ed25519.Verify(pub, entries["manifest.json"], entries["bundle.sig"]) {
|
|
t.Fatal("legacy Ed25519 verifier rejected current signed manifest")
|
|
}
|
|
if len(m.Files) != 2 {
|
|
t.Fatalf("legacy manifest file count: %d", len(m.Files))
|
|
}
|
|
for _, f := range m.Files {
|
|
sum := sha256.Sum256(entries[f.Path])
|
|
if hex.EncodeToString(sum[:]) != f.SHA256 {
|
|
t.Fatalf("legacy SHA-256 mismatch for %q", f.Path)
|
|
}
|
|
}
|
|
}
|