A bundle carries what a cluster would download — llmgw's release with its signed checksums, every llama.cpp build per architecture with GitHub's checksums, model files pinned and checked against Hugging Face's — into a directory or a tar whose manifest sha256 is carried apart. Import checks everything, the release against the signing key, before anything is put in the mirror. With offline.mirror set, nodes install llama.cpp from the mirror against checksums the control plane holds and sends over the agent stream; releases default to the mirror for node add, ctl upgrade and agent upgrades. server init and the setup form install from a bundle. The Helm chart runs one control plane with SQLite on a claim or an external PostgreSQL, Secrets for the agent certificate and admin token, Services, Ingress and ServiceMonitor; deploy/container/Dockerfile builds its image. GPU nodes stay outside Kubernetes, as docs/kubernetes.md explains. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
133 lines
3.8 KiB
Go
133 lines
3.8 KiB
Go
package bundle
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"maps"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Trust records a bundle's engine checksums in the file the control plane
|
|
// reads them from, beside any recorded before, and says how many it holds
|
|
// now. Only the manifest is read: the checksums are what each node holds
|
|
// the files it fetches to, so the manifest must be the one that was made —
|
|
// which is what the digest says.
|
|
func Trust(p, file, digest string, unchecked bool) (Manifest, int, error) {
|
|
src, m, err := open(p)
|
|
if err != nil {
|
|
return Manifest{}, 0, err
|
|
}
|
|
defer src.close() //nolint:errcheck // read only
|
|
if err := checkDigest(src.manifest(), digest, unchecked); err != nil {
|
|
return m, 0, err
|
|
}
|
|
n, err := TrustManifest(m, file)
|
|
return m, n, err
|
|
}
|
|
|
|
// TrustManifest records a manifest's engine checksums, for one already
|
|
// checked by an import.
|
|
func TrustManifest(m Manifest, file string) (int, error) {
|
|
sums, err := readTrusted(file)
|
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
return 0, err
|
|
}
|
|
if sums == nil {
|
|
sums = map[string]string{}
|
|
}
|
|
for _, f := range m.Files {
|
|
if strings.HasPrefix(f.Path, kindEngine+"/") {
|
|
sums[Prefix+"/"+f.Path] = f.SHA256
|
|
}
|
|
}
|
|
var b strings.Builder
|
|
b.WriteString("# Engine files the nodes may install, as the offline bundles they came in\n" +
|
|
"# gave them. Kept by 'llmgw bundle import' and 'llmgw bundle trust'.\n")
|
|
for _, k := range slices.Sorted(maps.Keys(sums)) {
|
|
fmt.Fprintf(&b, "%s %s\n", sums[k], k)
|
|
}
|
|
if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil {
|
|
return 0, err
|
|
}
|
|
tmp := file + partSuffix
|
|
if err := os.WriteFile(tmp, []byte(b.String()), 0o644); err != nil { //nolint:gosec // checksums, for anyone to read
|
|
return 0, err
|
|
}
|
|
return len(sums), os.Rename(tmp, file)
|
|
}
|
|
|
|
// readTrusted reads a checksums file: "<sha256> <path>" a line, as
|
|
// sha256sum writes them, with paths as the mirror has the files.
|
|
func readTrusted(file string) (map[string]string, error) {
|
|
b, err := os.ReadFile(file) //nolint:gosec // the control plane's own file
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := map[string]string{}
|
|
sc := bufio.NewScanner(bytes.NewReader(b))
|
|
for sc.Scan() {
|
|
line := strings.TrimSpace(sc.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
f := strings.Fields(line)
|
|
if len(f) != 2 || len(f[0]) != 64 {
|
|
return nil, fmt.Errorf("%s: %q is not a checksum and a file", file, line)
|
|
}
|
|
out[strings.TrimPrefix(f[1], "*")] = f[0]
|
|
}
|
|
return out, sc.Err()
|
|
}
|
|
|
|
// Trusted is the engine checksums a control plane holds, read from their
|
|
// file again whenever it changes — so a bundle imported while the control
|
|
// plane runs is offered to the next node that asks.
|
|
type Trusted struct {
|
|
file string
|
|
|
|
mu sync.Mutex
|
|
mod time.Time
|
|
size int64
|
|
sums map[string]string
|
|
err error
|
|
}
|
|
|
|
// NewTrusted reads checksums from file as they are asked for.
|
|
func NewTrusted(file string) *Trusted { return &Trusted{file: file} }
|
|
|
|
// For is an engine version's files and their checksums, by file name; none
|
|
// when no bundle imported here carried it.
|
|
func (t *Trusted) For(engine, version string) map[string]string {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
if info, err := os.Stat(t.file); err != nil {
|
|
t.sums, t.err, t.mod, t.size = nil, err, time.Time{}, 0
|
|
} else if !info.ModTime().Equal(t.mod) || info.Size() != t.size {
|
|
t.sums, t.err = readTrusted(t.file)
|
|
t.mod, t.size = info.ModTime(), info.Size()
|
|
}
|
|
prefix := Prefix + "/" + kindEngine + "/" + engine + "/" + version + "/"
|
|
out := map[string]string{}
|
|
for p, sum := range t.sums {
|
|
if rest, ok := strings.CutPrefix(p, prefix); ok && path.Base(rest) == rest {
|
|
out[rest] = sum
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Err is why the file could not be read, the last time it was.
|
|
func (t *Trusted) Err() error {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
return t.err
|
|
}
|