mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-28 09:08:54 -04:00
A library can now list backends. An entry of kind backend names the image of the backend gallery by digest, and POST /backends/library/install installs it on the local node. The gallery stays the source of truth and its install path is the one used: the library resolves the entry, takes the signature policy from the library, pins the image by digest and calls the same install as the gallery. Installing a name that came from a gallery, another library or by hand is refused and changes nothing. The signature policy of a backend library names the signer with an anchored identityRegex, so images signed by the merge workflow on master and on tags both verify. A pattern that is not anchored is refused. An install is bounded by a size limit per kind: add-ons keep 512 MiB and a backend image defaults to 8 GiB, set with --addons-backend-max-bytes. The limit reads the manifest after the signature check and before any layer is fetched. The metadata of an installed backend records the library it came from. scripts/backends/cmd/backendcatalog renders entries from backend/index.yaml and pins them against the registry. The pilot is the CPU build of local-store, in gallery/backends/index.yaml. The library is registered on the first start only with --addons-backend-library, which is off by default. Assisted-by: Claude Code:claude-sonnet-5-5 go Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
67 lines
1.7 KiB
Go
67 lines
1.7 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mudler/LocalAI/core/services/addons/catalog"
|
|
)
|
|
|
|
const fakeGallery = `
|
|
- &demo
|
|
name: "demo"
|
|
description: |
|
|
A demo
|
|
backend.
|
|
capabilities:
|
|
default: "cpu-demo"
|
|
- !!merge <<: *demo
|
|
name: "cpu-demo"
|
|
alias: "demo"
|
|
uri: "quay.io/example/backends:latest-cpu-demo"
|
|
- !!merge <<: *demo
|
|
name: "cpu-demo-development"
|
|
uri: "quay.io/example/backends:master-cpu-demo"
|
|
`
|
|
|
|
func fakeDigest(image, platform string) (string, error) {
|
|
return "sha256:" + strings.Repeat("ab", 32), nil
|
|
}
|
|
|
|
func TestRenderPinsTheGalleryImage(t *testing.T) {
|
|
got, err := render([]byte(fakeGallery), []string{"cpu-demo"}, "linux", "amd64", fakeDigest)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(got) != 1 {
|
|
t.Fatalf("want 1 entry, got %d", len(got))
|
|
}
|
|
r := got[0]
|
|
if r.EntryKind() != catalog.KindBackend || r.AddonID != "cpu-demo" || r.Alias != "demo" || r.Channel != "latest" ||
|
|
r.Artifact != "oci://quay.io/example/backends:latest-cpu-demo" || r.Version != "0.0.0-latest.abababababab" ||
|
|
r.Description != "A demo backend." {
|
|
t.Fatalf("unexpected entry: %+v", r)
|
|
}
|
|
data, err := catalog.EncodeIndex(got)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
read, err := catalog.ReadIndex(data, catalog.KindBackend)
|
|
if err != nil || len(read.Releases) != 1 || len(read.Skipped) != 0 {
|
|
t.Fatalf("the rendered entry does not read back: %v %+v", err, read)
|
|
}
|
|
}
|
|
|
|
func TestRenderRefusesWhatItCannotPin(t *testing.T) {
|
|
for name, tc := range map[string][]string{
|
|
"unknown": {"nope"},
|
|
"meta": {"demo"},
|
|
"master only": {"cpu-demo-development"},
|
|
} {
|
|
if _, err := render([]byte(fakeGallery), tc, "linux", "amd64", fakeDigest); err == nil {
|
|
t.Errorf("%s: want an error", name)
|
|
}
|
|
}
|
|
}
|