130 lines
3.7 KiB
Go
130 lines
3.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
reg "reg/internal/reg"
|
|
)
|
|
|
|
// RunPull реализует команду `reg pull <image>[:<tag>|@<digest>] [options]`.
|
|
func RunPull(ctx context.Context, client *reg.RegistryClient, args []string) int {
|
|
if len(args) == 0 {
|
|
reg.PrintError(MsgPullRequiresImage.String())
|
|
return 1
|
|
}
|
|
imageArg := args[0]
|
|
args = args[1:]
|
|
|
|
// Предварительно разбираем -f (необязательный по значению флаг).
|
|
tarSet, tarName, rest := extractTarFlag(args)
|
|
if tarSet && tarName != "" && filepath.Base(tarName) != tarName {
|
|
reg.PrintError(MsgPullTarNoPath.String())
|
|
return 1
|
|
}
|
|
|
|
cmdFlags := flag.NewFlagSet("pull", flag.ContinueOnError)
|
|
cmdFlags.SetOutput(io.Discard)
|
|
outputFlag := cmdFlags.String("o", "", "parent directory for saving")
|
|
platformFlag := cmdFlags.String("platform", "", "platform (os/arch[/variant]); default — current")
|
|
verboseFlag := cmdFlags.Bool("v", false, "print progress to stderr (one JSON line per event)")
|
|
cmdFlags.Usage = func() {}
|
|
if code, ok := parseFlags(cmdFlags, rest); !ok {
|
|
return code
|
|
}
|
|
|
|
ref, err := reg.ParseImageRef(imageArg)
|
|
if err != nil {
|
|
reg.PrintError(err.Error())
|
|
return 1
|
|
}
|
|
|
|
platform := reg.DefaultPlatform()
|
|
if *platformFlag != "" {
|
|
platform, err = reg.ParsePlatform(*platformFlag)
|
|
if err != nil {
|
|
reg.PrintError(err.Error())
|
|
return 1
|
|
}
|
|
}
|
|
|
|
progress := func(event string, data map[string]interface{}) {
|
|
if !*verboseFlag {
|
|
return
|
|
}
|
|
m := map[string]interface{}{"event": event}
|
|
for k, v := range data {
|
|
m[k] = v
|
|
}
|
|
enc := json.NewEncoder(os.Stderr)
|
|
_ = enc.Encode(m)
|
|
}
|
|
|
|
// Режим tar (-f).
|
|
if tarSet {
|
|
name := tarName
|
|
if name == "" {
|
|
name = reg.DefaultOutputDir(ref) + ".tar"
|
|
}
|
|
tarPath := name
|
|
if *outputFlag != "" {
|
|
tarPath = filepath.Join(*outputFlag, name)
|
|
}
|
|
result, err := reg.PullImageToTar(ctx, client, ref, platform, tarPath, progress)
|
|
if err != nil {
|
|
reg.PrintError(MsgPullTarError.F(ref.String(), err))
|
|
return 1
|
|
}
|
|
reg.PrintJSON(result)
|
|
return 0
|
|
}
|
|
|
|
// Режим каталога (по умолчанию).
|
|
outDir := reg.DefaultOutputDir(ref)
|
|
if *outputFlag != "" {
|
|
outDir = filepath.Join(*outputFlag, outDir)
|
|
}
|
|
result, err := reg.PullImage(ctx, client, ref, platform, outDir, progress)
|
|
if err != nil {
|
|
reg.PrintError(MsgPullError.F(ref.String(), err))
|
|
return 1
|
|
}
|
|
reg.PrintJSON(result)
|
|
return 0
|
|
}
|
|
|
|
// extractTarFlag выделяет флаг -f из args.
|
|
// Поддерживаются формы:
|
|
// -f → set=true, name=""
|
|
// -f name → set=true, name="name"
|
|
// -f=name → set=true, name="name"
|
|
//
|
|
// Всё остальное возвращается в rest (для последующего flag.Parse).
|
|
func extractTarFlag(args []string) (bool, string, []string) {
|
|
var set bool
|
|
var name string
|
|
out := make([]string, 0, len(args))
|
|
for i := 0; i < len(args); i++ {
|
|
a := args[i]
|
|
switch {
|
|
case a == "-f":
|
|
set = true
|
|
if i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") {
|
|
name = args[i+1]
|
|
i++
|
|
}
|
|
case strings.HasPrefix(a, "-f="):
|
|
set = true
|
|
name = strings.TrimPrefix(a, "-f=")
|
|
default:
|
|
out = append(out, a)
|
|
}
|
|
}
|
|
return set, name, out
|
|
}
|