Phase 3: CLI banger image pull
newImagePullCommand mirrors newImageRegisterCommand with a positional <oci-ref> arg, the same kernel-ref / direct-paths flag set + mutual exclusion, plus --size that parses human-friendly values via model.ParseSize before crossing the RPC boundary. Calls "image.pull" RPC, prints the resulting image summary on success. Long help warns about the Phase A bootability gap (ownership not preserved; suitable as `image build` base, not yet directly bootable). CLI test confirms image pull is registered with the expected flags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a8c9983542
commit
d5f72dfad9
2 changed files with 84 additions and 0 deletions
|
|
@ -1427,6 +1427,7 @@ func newImageCommand() *cobra.Command {
|
|||
cmd.AddCommand(
|
||||
newImageBuildCommand(),
|
||||
newImageRegisterCommand(),
|
||||
newImagePullCommand(),
|
||||
newImagePromoteCommand(),
|
||||
newImageListCommand(),
|
||||
newImageShowCommand(),
|
||||
|
|
@ -1507,6 +1508,60 @@ func newImageRegisterCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newImagePullCommand() *cobra.Command {
|
||||
var (
|
||||
params api.ImagePullParams
|
||||
sizeRaw string
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "pull <oci-ref>",
|
||||
Short: "Pull an OCI image and register it as a managed banger image",
|
||||
Long: "Download an OCI image (e.g. docker.io/library/debian:bookworm), " +
|
||||
"flatten its layers into an ext4 rootfs, and register the result as a " +
|
||||
"managed image. Kernel info is required (via --kernel-ref or direct paths). " +
|
||||
"\n\nNote: Phase A primitive — file ownership in the produced ext4 reflects " +
|
||||
"the runner's uid/gid, not the OCI tar headers, so the resulting image is " +
|
||||
"suitable as a base for `image build` but is not directly bootable until a " +
|
||||
"future ownership-fixup pass lands.",
|
||||
Args: exactArgsUsage(1, "usage: banger image pull <oci-ref> [--name <name>] (--kernel-ref <name> | --kernel <path> [--initrd <path>] [--modules <dir>]) [--size <human>]"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
params.Ref = args[0]
|
||||
if strings.TrimSpace(params.KernelRef) != "" && (params.KernelPath != "" || params.InitrdPath != "" || params.ModulesDir != "") {
|
||||
return errors.New("--kernel-ref is mutually exclusive with --kernel/--initrd/--modules")
|
||||
}
|
||||
if strings.TrimSpace(sizeRaw) != "" {
|
||||
size, err := model.ParseSize(sizeRaw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("--size: %w", err)
|
||||
}
|
||||
params.SizeBytes = size
|
||||
}
|
||||
if err := absolutizePaths(¶ms.KernelPath, ¶ms.InitrdPath, ¶ms.ModulesDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := system.EnsureSudo(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
layout, _, err := ensureDaemon(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := rpc.Call[api.ImageShowResult](cmd.Context(), layout.SocketPath, "image.pull", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printImageSummary(cmd.OutOrStdout(), result.Image)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(¶ms.Name, "name", "", "image name (defaults to the ref's repo+tag, sanitised)")
|
||||
cmd.Flags().StringVar(¶ms.KernelPath, "kernel", "", "kernel path")
|
||||
cmd.Flags().StringVar(¶ms.InitrdPath, "initrd", "", "initrd path")
|
||||
cmd.Flags().StringVar(¶ms.ModulesDir, "modules", "", "modules dir")
|
||||
cmd.Flags().StringVar(¶ms.KernelRef, "kernel-ref", "", "name of a cataloged kernel (see 'banger kernel list')")
|
||||
cmd.Flags().StringVar(&sizeRaw, "size", "", "ext4 image size (e.g. 4GiB); defaults to content + 25%, min 1GiB")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newImagePromoteCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "promote <id-or-name>",
|
||||
|
|
|
|||
|
|
@ -59,6 +59,35 @@ func TestVersionCommandPrintsBuildInfo(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestImageCommandIncludesPull(t *testing.T) {
|
||||
cmd := NewBangerCommand()
|
||||
var image *cobra.Command
|
||||
for _, sub := range cmd.Commands() {
|
||||
if sub.Name() == "image" {
|
||||
image = sub
|
||||
break
|
||||
}
|
||||
}
|
||||
if image == nil {
|
||||
t.Fatalf("image command missing from root")
|
||||
}
|
||||
hasPull := false
|
||||
for _, sub := range image.Commands() {
|
||||
if sub.Name() == "pull" {
|
||||
hasPull = true
|
||||
if flag := sub.Flags().Lookup("kernel-ref"); flag == nil {
|
||||
t.Errorf("image pull missing --kernel-ref flag")
|
||||
}
|
||||
if flag := sub.Flags().Lookup("size"); flag == nil {
|
||||
t.Errorf("image pull missing --size flag")
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasPull {
|
||||
t.Fatalf("image pull subcommand missing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestKernelCommandExposesSubcommands(t *testing.T) {
|
||||
cmd := NewBangerCommand()
|
||||
var kernel *cobra.Command
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue