feat(imagepush): 写出按架构的发布镜像包并支持按名推送
This commit is contained in:
8 files changed
+1135
-3
No files matched your search
@@ -0,0 +1,109 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"felis.lolicon.best/internal/imagepush"
|
||||
)
|
||||
|
||||
// cmdImageBundle writes a release's image bundle: one OCI layout tar holding every
|
||||
// image an install runs, for one platform, plus its listing (one "role name
|
||||
// manifest-digest config-digest" line per image). deploy/build-release-artifacts.sh
|
||||
// runs it in CI; deploy/bootstrap.sh imports the tar into k3s's containerd and
|
||||
// pushes it into the platform registry with push-image --image.
|
||||
//
|
||||
// --layout role=name=path an image buildx wrote with --output type=oci
|
||||
// --pull role=ref a digest-pinned public image, named repository@digest
|
||||
//
|
||||
// The tar and the listing are written beside their final paths and renamed into
|
||||
// place, so a failed run leaves neither behind.
|
||||
func cmdImageBundle(args []string, _, stderr io.Writer) int {
|
||||
fs := flag.NewFlagSet("image-bundle", flag.ContinueOnError)
|
||||
fs.SetOutput(stderr)
|
||||
platform := fs.String("platform", "", "os/arch the bundle is for, e.g. linux/arm64")
|
||||
out := fs.String("out", "", "path of the bundle tar to write")
|
||||
list := fs.String("list", "", "path of the listing to write")
|
||||
var images []imagepush.BundleImage
|
||||
fs.Func("layout", "role=name=path of an OCI layout tar (repeatable)", func(v string) error {
|
||||
role, rest, ok := strings.Cut(v, "=")
|
||||
name, path, ok2 := strings.Cut(rest, "=")
|
||||
if !ok || !ok2 || role == "" || name == "" || path == "" {
|
||||
return fmt.Errorf("want role=name=path, got %q", v)
|
||||
}
|
||||
images = append(images, imagepush.BundleImage{Role: role, Name: name, Layout: path})
|
||||
return nil
|
||||
})
|
||||
fs.Func("pull", "role=ref of a digest-pinned public image (repeatable)", func(v string) error {
|
||||
role, ref, ok := strings.Cut(v, "=")
|
||||
if !ok || role == "" || !strings.Contains(ref, "@sha256:") {
|
||||
return fmt.Errorf("want role=ref with ref pinned by digest, got %q", v)
|
||||
}
|
||||
images = append(images, imagepush.BundleImage{Role: role, Name: imagepush.PinnedName(ref), Source: ref})
|
||||
return nil
|
||||
})
|
||||
if err := fs.Parse(args); err != nil {
|
||||
if errors.Is(err, flag.ErrHelp) {
|
||||
return 0
|
||||
}
|
||||
return 2
|
||||
}
|
||||
if *platform == "" || *out == "" || *list == "" || len(images) == 0 {
|
||||
fmt.Fprintln(stderr, "felis image-bundle: --platform, --out, --list and at least one --layout or --pull are required")
|
||||
return 2
|
||||
}
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
if err := writeImageBundle(ctx, &imagepush.Source{Platform: *platform}, images, *out, *list); err != nil {
|
||||
fmt.Fprintf(stderr, "felis image-bundle: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func writeImageBundle(ctx context.Context, s *imagepush.Source, images []imagepush.BundleImage, out, list string) (err error) {
|
||||
tmpOut, tmpList := out+".tmp", list+".tmp"
|
||||
defer func() {
|
||||
if err != nil {
|
||||
os.Remove(tmpOut)
|
||||
os.Remove(tmpList)
|
||||
}
|
||||
}()
|
||||
f, err := os.Create(tmpOut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := bufio.NewWriterSize(f, 1<<20)
|
||||
entries, err := imagepush.WriteBundle(ctx, s, images, w)
|
||||
if err == nil {
|
||||
err = w.Flush()
|
||||
}
|
||||
if err == nil {
|
||||
err = f.Sync()
|
||||
}
|
||||
if cerr := f.Close(); err == nil {
|
||||
err = cerr
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var b strings.Builder
|
||||
for _, e := range entries {
|
||||
fmt.Fprintf(&b, "%s %s %s %s\n", e.Role, e.Name, e.Digest, e.Config)
|
||||
}
|
||||
if err := os.WriteFile(tmpList, []byte(b.String()), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Rename(tmpOut, out); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmpList, list)
|
||||
}
|
||||
Reference in new issue
Block a user