package maker import ( "context" "os" "os/exec" "path/filepath" "gitea.loveuer.com/yizhisec/pkg3/logger" "github.com/samber/lo" "yizhisec.com/hsv2/forge/pkg/model" ) type imageOpt struct { Fallbacks []string IgnoreFailure bool ForcePull bool Save string } type ImageOpt func(*imageOpt) func WithImageFallback(fallbacks ...string) ImageOpt { return func(o *imageOpt) { o.Fallbacks = lo.Filter(fallbacks, func(item string, _ int) bool { return item != "" }) } } func WithImageSave(filename string) ImageOpt { return func(o *imageOpt) { o.Save = filename } } func WithImageForcePull(force bool) ImageOpt { return func(o *imageOpt) { if force { o.ForcePull = true } } } func (m *maker) Image(ctx context.Context, name string, opts ...ImageOpt) error { var ( err error o = &imageOpt{} _cmd *exec.Cmd ) for _, fn := range opts { fn(o) } logger.Debug("๐Ÿ“‹ maker.Image: name = %s, opt = %#v", name, o) if !o.ForcePull { _cmd = exec.CommandContext(ctx, "docker", "image", "inspect", name) if err = _cmd.Run(); err == nil { logger.Debug("๐Ÿ’ฟ ้•œๅƒ %s ๅทฒๅญ˜ๅœจ", name) goto SAVE } } _cmd = exec.CommandContext(ctx, "docker", "pull", name) if err = _cmd.Run(); err != nil { logger.Debug("โŒ ่Žทๅ–ๅŽŸๅง‹้•œๅƒ %s ๅคฑ่ดฅ๏ผš %v", name, err) } else { logger.Debug("โœ… ๆˆๅŠŸ่Žทๅ–้•œๅƒ: %s", name) goto SAVE } for _, fallback := range o.Fallbacks { logger.Debug("โ˜‘๏ธ ๅผ€ๅง‹่Žทๅ–้•œๅƒ: %s (%s)", name, fallback) _cmd := exec.CommandContext(ctx, "docker", "pull", fallback) if err = _cmd.Run(); err != nil { logger.Debug("โŒ ่Žทๅ–้•œๅƒ %s (%s) ๅคฑ่ดฅ: %v", name, fallback, err) continue } // pull success, retag image _cmd = exec.CommandContext(ctx, "docker", "tag", fallback, name) if err = _cmd.Run(); err != nil { logger.Debug("โŒ ้‡ๅ‘ฝๅ้•œๅƒ %s => %s ๅคฑ่ดฅ: %s", fallback, name, err) continue } logger.Debug("โœ… ๆˆๅŠŸ่Žทๅ–้•œๅƒ: %s (%s)", name, fallback) break } SAVE: if o.Save != "" { logger.Debug("โ˜‘๏ธ ไฟๅญ˜้•œๅƒ %s ๅˆฐ %s", name, o.Save) _ = os.MkdirAll(filepath.Dir(o.Save), 0755) if err = exec.CommandContext(ctx, "docker", "save", "-o", o.Save, name).Run(); err != nil { logger.Debug("โŒ ไฟๅญ˜้•œๅƒ %s ๅˆฐ %s ๅคฑ่ดฅ: %v", name, o.Save, err) } else { logger.Debug("โœ… ้•œๅƒ %s ไฟๅญ˜ๅˆฐ %s ๆˆๅŠŸ", name, o.Save) } } if o.IgnoreFailure { return nil } return err } func (m *maker) Images(ctx context.Context) error { var images = []*model.Image{ {Name: "hub.yizhisec.com/external/alpine:3.22.2", Fallback: "", Save: "alpine.tar", Force: true}, {Name: "hub.yizhisec.com/external/nginx:1.29.1-alpine3.22", Fallback: "", Save: "nginx.1.29.1-alpine3.22.tar"}, {Name: "hub.yizhisec.com/hybridscope/user_management:latest", Fallback: "", Save: "app.user.tar", Force: true}, {Name: "hub.yizhisec.com/hybridscope/gateway_controller:latest", Fallback: "", Save: "app.gateway.tar", Force: true}, {Name: "hub.yizhisec.com/hybridscope/client_server:latest", Fallback: "", Save: "app.client.tar", Force: true}, {Name: "hub.yizhisec.com/hybridscope/mie-server:latest", Fallback: "", Save: "app.mie.tar", Force: true}, {Name: "hub.yizhisec.com/hybridscope/less_dns_service:latest", Fallback: "", Save: "app.less_dns.tar", Force: true}, {Name: "hub.yizhisec.com/hybridscope/hsnet:release_2.1.0-std", Fallback: "", Save: "app.hsnet.tar", Force: true}, } for _, image := range images { image.Save = filepath.Join(m.workdir, "dependency", "image", image.Save) } logger.Info("โ˜‘๏ธ ๅผ€ๅง‹่Žทๅ–้•œๅƒ(s)...") for _, image := range images { opts := []ImageOpt{ WithImageFallback(image.Fallback), WithImageSave(image.Save), WithImageForcePull(image.Force), } logger.Info("โ˜‘๏ธ ่Žทๅ–้•œๅƒ: %s", image.Name) if err := m.Image(ctx, image.Name, opts...); err != nil { logger.Error("โŒ ่Žทๅ–้•œๅƒๅคฑ่ดฅ: %s, ๅฏไปฅๆ‰‹ๅŠจ่Žทๅ–ๅŽ้‡่ฏ•", image.Name) logger.Debug("โŒ ่Žทๅ–้•œๅƒๅคฑ่ดฅ: %s, %v", image.Name, err) return err } logger.Info("โœ… ่Žทๅ–้•œๅƒๆˆๅŠŸ: %s", image.Name) } logger.Info("โœ… ่Žทๅ–้•œๅƒ(s)ๆˆๅŠŸ!!!") return nil }