From eb87d6fbedfb3f8628f7d5c69ea768bc24a84aa9 Mon Sep 17 00:00:00 2001 From: zhaoyupeng Date: Wed, 31 Dec 2025 17:31:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=9A=9B=20=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E4=BA=86=20client=20=E8=B5=84=E6=BA=90=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- helper/nginx-with-tool/Dockerfile | 3 + internal/cmd/make.go | 3 +- internal/cmd/makecmd/all.go | 2 +- internal/cmd/makecmd/binaries.go | 10 +- internal/cmd/makecmd/client.go | 176 ++++++++++++++++++++++++ internal/controller/maker/binary.go | 40 +++++- internal/controller/maker/check.go | 4 + internal/controller/maker/client.pkg.go | 154 +++++++++++++++++++++ internal/controller/maker/command.go | 28 ++++ internal/controller/maker/elastic.go | 27 +++- internal/controller/maker/emqx.go | 9 ++ internal/controller/maker/image.go | 22 --- internal/controller/maker/minio.go | 39 ++++-- internal/controller/maker/mysql.go | 36 ++++- internal/controller/maker/redis.go | 21 ++- pkg/resource/nginx/client_pkg.conf | 28 ++++ pkg/resource/resource.go | 6 + pkg/resource/yaml/client.pkg.yaml | 42 ++++++ pkg/resource/yaml/registry.yaml | 2 +- pkg/tool/client/http.go | 40 ++++++ 20 files changed, 632 insertions(+), 60 deletions(-) create mode 100644 helper/nginx-with-tool/Dockerfile create mode 100644 internal/cmd/makecmd/client.go create mode 100644 internal/controller/maker/client.pkg.go create mode 100644 internal/controller/maker/command.go create mode 100644 pkg/resource/nginx/client_pkg.conf create mode 100644 pkg/resource/yaml/client.pkg.yaml create mode 100644 pkg/tool/client/http.go diff --git a/helper/nginx-with-tool/Dockerfile b/helper/nginx-with-tool/Dockerfile new file mode 100644 index 0000000..cbc547c --- /dev/null +++ b/helper/nginx-with-tool/Dockerfile @@ -0,0 +1,3 @@ +FROM docker-mirror.yizhisec.com/library/nginx:1.29.4-alpine3.23 + +RUN apk add curl wget tzdata unzip \ No newline at end of file diff --git a/internal/cmd/make.go b/internal/cmd/make.go index 698a176..bc98780 100644 --- a/internal/cmd/make.go +++ b/internal/cmd/make.go @@ -52,7 +52,7 @@ func makeCmd() *cobra.Command { _cmd.AddCommand( makecmd.ALL(), makecmd.Images(), - makecmd.Binaries(), + makecmd.K0s(), makecmd.Flannel(), makecmd.Longhorn(), makecmd.Mysql(), @@ -68,6 +68,7 @@ func makeCmd() *cobra.Command { makecmd.Proxy(), makecmd.Seafile(), makecmd.App(), + makecmd.Client(), ) return _cmd diff --git a/internal/cmd/makecmd/all.go b/internal/cmd/makecmd/all.go index 3619351..70efaad 100644 --- a/internal/cmd/makecmd/all.go +++ b/internal/cmd/makecmd/all.go @@ -24,7 +24,7 @@ func ALL() *cobra.Command { return err } - if err = mk.Binary(cmd.Context()); err != nil { + if err = mk.K0s(cmd.Context()); err != nil { return err } diff --git a/internal/cmd/makecmd/binaries.go b/internal/cmd/makecmd/binaries.go index 71c3b09..78f8844 100644 --- a/internal/cmd/makecmd/binaries.go +++ b/internal/cmd/makecmd/binaries.go @@ -6,15 +6,13 @@ import ( "yizhisec.com/hsv2/forge/internal/opt" ) -func Binaries() *cobra.Command { +func K0s() *cobra.Command { cmd := &cobra.Command{ - Use: "binaries", - Aliases: []string{"bin", "B"}, - Short: "Build binary files", - Long: `Build all required binary files for the project.`, + Use: "k0s", + Short: "make k0s files(binary, images)", RunE: func(cmd *cobra.Command, args []string) error { mk := maker.NewMaker(opt.Cfg.Make.Dir) - return mk.Binary(cmd.Context()) + return mk.K0s(cmd.Context()) }, } diff --git a/internal/cmd/makecmd/client.go b/internal/cmd/makecmd/client.go new file mode 100644 index 0000000..49b6b9f --- /dev/null +++ b/internal/cmd/makecmd/client.go @@ -0,0 +1,176 @@ +package makecmd + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + "gitea.loveuer.com/yizhisec/pkg3/logger" + "github.com/spf13/cobra" + "yizhisec.com/hsv2/forge/internal/controller/maker" + "yizhisec.com/hsv2/forge/internal/opt" + tc "yizhisec.com/hsv2/forge/pkg/tool/client" +) + +func Client() *cobra.Command { + _cmd := &cobra.Command{ + Use: "client", + Short: "make client pkg", + } + + _cmd.AddCommand( + clientMac(), + clientWin(), + clientLinux(), + ) + + return _cmd +} + +func clientMac() *cobra.Command { + _cmd := &cobra.Command{ + Use: "mac", + Aliases: []string{"macos"}, + Short: "make client-mac pkg", + RunE: func(cmd *cobra.Command, args []string) error { + const ( + DMG_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_appleclient-csgElink/release/2.1.0-std/hybridscope-client-mac.dmg" + PKG_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_appleclient-csgElink/release/2.1.0-std/hybridscope-client-mac.pkg" + VERSION_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_appleclient/release/2.1.0-std/mac_version.json" + ) + + var ( + err error + version string + ) + + if version, err = clientVersion(VERSION_URL); err != nil { + return err + } + + mk := maker.NewMaker(opt.Cfg.Make.Dir) + return mk.ClientPKG( + cmd.Context(), + "mac", + version, + "/api/v2_2/_client/mac", + maker.WithClientPKGDownload(DMG_URL, "hybridscope-client-mac.dmg"), + maker.WithClientPKGDownload(PKG_URL, "hybridscope-client-mac.pkg"), + maker.WithClientPKGDownload(VERSION_URL, "version.json"), + ) + }, + } + + return _cmd +} + +func clientWin() *cobra.Command { + _cmd := &cobra.Command{ + Use: "win", + Short: "make client-win pkg", + RunE: func(cmd *cobra.Command, args []string) error { + const ( + ZIP_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_client-yizhianquan/release/2.1.0-std/hs_client.zip" + VERSION_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_client-yizhianquan/release/2.1.0-std/windows_version.json" + ) + + var ( + err error + version string + ) + + if version, err = clientVersion(VERSION_URL); err != nil { + return err + } + + mk := maker.NewMaker(opt.Cfg.Make.Dir) + return mk.ClientPKG( + cmd.Context(), + "win", + version, + "/api/v2_2/_client/win", + maker.WithClientPKGDownload(ZIP_URL, "hs_client.zip"), + maker.WithClientPKGDownload(VERSION_URL, "version.json"), + maker.WithClientPKGCMD("unzip /data/hs_client.zip"), + maker.WithClientPKGCMD("mv resources/app.7z /data/app.7z"), + maker.WithClientPKGCMD("mv resources/hybridscope_offline_installer.exe /data/hybridscope_offline_installer.exe"), + maker.WithClientPKGCMD("mv resources/hybridscope-client-setup-zh.exe /data/hybridscope-client-setup-zh.exe"), + maker.WithClientPKGCMD("mv resources/hybridscope-client-setup-en.exe /data/hybridscope-client-setup-en.exe"), + maker.WithClientPKGCMD("rm -rf /data/hs_client.zip"), + maker.WithClientPKGCMD("rm -rf resources"), + ) + }, + } + + return _cmd +} + +func clientLinux() *cobra.Command { + _cmd := &cobra.Command{ + Use: "linux", + Short: "make client-linux pkg", + RunE: func(cmd *cobra.Command, args []string) error { + const ( + DEB_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/universal-hsclient-linux/release/2.1.0/hscore-linux-2.1.0-csgElink-amd64.deb" + VERSION_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/universal-hsclient-linux/release/2.1.0/hscore-linux-2.1.0-std-amd64.json" + ) + + var ( + err error + version string + ) + + if version, err = clientVersion(VERSION_URL); err != nil { + return err + } + + mk := maker.NewMaker(opt.Cfg.Make.Dir) + return mk.ClientPKG( + cmd.Context(), + "linux", + version, + "/api/v2_2/_client/linux", + maker.WithClientPKGDownload(DEB_URL, "hybridscope-client-linux.deb"), + maker.WithClientPKGDownload(VERSION_URL, "version.json"), + ) + }, + } + + return _cmd +} + +func clientVersion(_url string) (string, error) { + type Res struct { + Version string `json:"version"` + } + + var ( + err error + rr *http.Response + res Res + bs []byte + ) + + logger.Debug("clientVersion: getting client version with url = %s", _url) + if rr, err = tc.HttpClient().Get(_url); err != nil { + return "", err + } + defer rr.Body.Close() + + if bs, err = io.ReadAll(rr.Body); err != nil { + return "", err + } + + logger.Debug("clientVersion: got client version body raw: %s, url = %s", string(bs), _url) + + if err = json.Unmarshal(bs, &res); err != nil { + return "", err + } + + if res.Version == "" { + return "", fmt.Errorf("get version empty, url = %s", _url) + } + + return res.Version, nil +} diff --git a/internal/controller/maker/binary.go b/internal/controller/maker/binary.go index 4644015..236f064 100644 --- a/internal/controller/maker/binary.go +++ b/internal/controller/maker/binary.go @@ -6,22 +6,23 @@ import ( "gitea.loveuer.com/yizhisec/pkg3/logger" "yizhisec.com/hsv2/forge/pkg/archiver" + "yizhisec.com/hsv2/forge/pkg/model" ) -func (m *maker) Binary(ctx context.Context) error { +func (m *maker) K0s(ctx context.Context) error { var ( - tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/k8s-bin.tar" - binDir = filepath.Join(m.workdir, "dependency") + tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/k8s-bin.tar" + location = filepath.Join(m.workdir, "dependency", "k0s") ) - logger.Info("☑️ 开始准备 k8s 二进制文件...") + logger.Info("☑️ 开始准备 k0s...") logger.Debug("下载地址: %s", tarURL) - logger.Debug("目标目录: %s", binDir) + logger.Debug("目标目录: %s", location) if err := archiver.DownloadAndExtract( ctx, tarURL, - binDir, + location, archiver.WithInsecureSkipVerify(), archiver.WithGzipCompression(true), ); err != nil { @@ -29,7 +30,32 @@ func (m *maker) Binary(ctx context.Context) error { return err } - logger.Info("✅ 准备 k8s 二进制文件成功!!!") + logger.Debug("☑️ maker.K0s: 开始准备相关镜像...") + var images = []*model.Image{ + {Name: "quay.io/k0sproject/apiserver-network-proxy-agent:v0.32.0", Fallback: "hub.yizhisec.com/external/apiserver-network-proxy-agent:v0.32.0", Save: "k0s.apiserver-network-proxy-agent.tar"}, + {Name: "quay.io/k0sproject/cni-node:1.7.1-k0s.0", Fallback: "", Save: "k0s.cni-node.tar"}, + {Name: "quay.io/k0sproject/coredns:1.12.2", Fallback: "", Save: "k0s.coredns.tar"}, + {Name: "quay.io/k0sproject/kube-proxy:v1.33.4", Fallback: "", Save: "k0s.kube-proxy.tar"}, + {Name: "quay.io/k0sproject/kube-router:v2.5.0-iptables1.8.11-0", Fallback: "", Save: "k0s.kube-router.tar"}, + {Name: "quay.io/k0sproject/metrics-server:v0.7.2-0", Fallback: "", Save: "k0s.metrics-server.tar"}, + {Name: "quay.io/k0sproject/pause:3.10.1", Fallback: "", Save: "k0s.pause.tar"}, + } + for _, image := range images { + opts := []ImageOpt{ + WithImageFallback(image.Fallback), + WithImageSave(filepath.Join(location, image.Save)), + WithImageForcePull(image.Force), + } + + if err := m.Image(ctx, image.Name, opts...); err != nil { + logger.Error("❌ maker.K0s: 获取镜像失败: %s, 可以手动获取后重试", image.Name) + logger.Debug("❌ maker.K0s: 获取镜像失败: %s, %v", image.Name, err) + return err + } + } + logger.Debug("✅ maker.K0s: 准备相关镜像成功!!!") + + logger.Info("✅ 准备 k0s 成功!!!") return nil } diff --git a/internal/controller/maker/check.go b/internal/controller/maker/check.go index 0cccf9d..8a37987 100644 --- a/internal/controller/maker/check.go +++ b/internal/controller/maker/check.go @@ -22,6 +22,10 @@ func (m *maker) DependencyCheck(ctx context.Context) error { return fmt.Errorf("docker 命令未找到: %w", err) } + if err := checkCommand(ctx, "7z"); err != nil { + return fmt.Errorf("7z 命令未找到: %w", err) + } + return nil } diff --git a/internal/controller/maker/client.pkg.go b/internal/controller/maker/client.pkg.go new file mode 100644 index 0000000..7ce6e9f --- /dev/null +++ b/internal/controller/maker/client.pkg.go @@ -0,0 +1,154 @@ +package maker + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + + "gitea.loveuer.com/yizhisec/pkg3/logger" + "github.com/samber/lo" + "yizhisec.com/hsv2/forge/pkg/resource" +) + +type clientPKGDownload struct { + URL string + Name string +} + +type ClientPKGOption func(*clientPKGOption) +type clientPKGOption struct { + Downloads []*clientPKGDownload + CMDs []string +} + +func WithClientPKGDownload(url, location string) ClientPKGOption { + return func(o *clientPKGOption) { + if url == "" || location == "" { + return + } + + o.Downloads = append(o.Downloads, &clientPKGDownload{ + URL: url, + Name: location, + }) + } +} + +func WithClientPKGCMD(cmd string) ClientPKGOption { + return func(o *clientPKGOption) { + if cmd == "" { + return + } + o.CMDs = append(o.CMDs, cmd) + } +} + +func (m *maker) ClientPKG(ctx context.Context, _os string, _version string, api string, opts ...ClientPKGOption) error { + const ( + Dockerfile = ` +FROM hub.yizhisec.com/external/nginx:1.29.4-alpine3.23 + +RUN mkdir -p /data +%s +%s + +COPY nginx.conf /etc/nginx/nginx.conf +` + ) + + if !lo.Contains([]string{"mac", "win", "linux"}, _os) { + return fmt.Errorf("invalid os: %s", _os) + } + + var ( + o = &clientPKGOption{ + Downloads: []*clientPKGDownload{}, + CMDs: []string{}, + } + err error + location = filepath.Join(m.workdir, "client", _os) + _file string + _content string + _cmds = "" + ) + + for _, fn := range opts { + fn(o) + } + + logger.Info("☑️ maker.ClientPKG: start build client pkg, os = %s, version = %s, location = %s", _os, _version, location) + + if err = os.MkdirAll(location, 0755); err != nil { + logger.Debug("❌ maker.ClientPKG: create directory failed, directory = %s, err = %s", location, err.Error()) + return err + } + + if err = os.WriteFile(filepath.Join(location, "version.txt"), []byte(_version), 0644); err != nil { + logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", filepath.Join(location, "version.txt"), err.Error()) + return err + } + + _file = filepath.Join(location, "nginx.conf") + _content = fmt.Sprintf(resource.NGINXClientPKG, api) + logger.Debug("☑️ maker.ClientPKG: start write file = %s", _file) + if os.WriteFile(_file, []byte(_content), 0644); err != nil { + logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", _file, err.Error()) + return err + } + logger.Debug("✅ maker.ClientPKG: write file success, file = %s", _file) + + lines := lo.Map(o.Downloads, func(d *clientPKGDownload, index int) string { + return fmt.Sprintf("RUN wget -O /data/%s %s", d.Name, d.URL) + }) + + if len(o.CMDs) > 0 { + _cmds = fmt.Sprintf("RUN %s", strings.Join(o.CMDs, " && ")) + } + + _file = filepath.Join(location, "Dockerfile") + _content = fmt.Sprintf(Dockerfile, strings.Join(lines, "\n"), _cmds) + logger.Debug("☑️ maker.ClientPKG: start write file = %s", _file) + if os.WriteFile(_file, []byte(_content), 0644); err != nil { + logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", _file, err.Error()) + return err + } + logger.Debug("✅ maker.ClientPKG: write file success, file = %s", _file) + + imgName := fmt.Sprintf("hub.yizhisec.com/hsv2/client/%s:%s", _os, _version) + logger.Debug("☑️ maker.ClientPKG: build docker image, os = %s, version = %s, full_name = %s", _os, _version, imgName) + _cmd := fmt.Sprintf("docker build --network host -t %s -f Dockerfile .", imgName) + if err = m.RunCommand(ctx, location, _cmd); err != nil { + logger.Debug("❌ maker.ClientPKG: run command failed, cmd = %s, err = %s", _cmd, err.Error()) + return err + } + + imgLocation := filepath.Join(location, _os+".tar") + logger.Debug("☑️ maker.ClientPKG: save img to %s", imgLocation) + _cmd = fmt.Sprintf("docker save hub.yizhisec.com/hsv2/client/%s:%s -o %s", _os, _version, imgLocation) + if err = m.RunCommand(ctx, location, _cmd); err != nil { + logger.Debug("❌ maker.ClientPKG: run command failed, cmd = %s, err = %s", _cmd, err.Error()) + return err + } + + imgNameInCluster := fmt.Sprintf("10.96.123.45:80/hsv2/client/%s:%s", _os, _version) + deployment := filepath.Join(location, "deployment.yaml") + _content = strings.ReplaceAll( + fmt.Sprintf( + resource.YAMLClientPKG, + 1, // replica 先默认 1 就够了 + imgNameInCluster, + ), + "__os__", + _os, + ) + if err = os.WriteFile(deployment, []byte(_content), 0644); err != nil { + logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", deployment, err.Error()) + return err + } + + logger.Info("️✅ maker.ClientPKG: build client pkg success, os = %s, version = %s, location = %s", _os, _version, location) + + return nil +} diff --git a/internal/controller/maker/command.go b/internal/controller/maker/command.go new file mode 100644 index 0000000..e182037 --- /dev/null +++ b/internal/controller/maker/command.go @@ -0,0 +1,28 @@ +package maker + +import ( + "context" + "fmt" + "os/exec" +) + +func (m *maker) RunCommand(ctx context.Context, dir string, _cmds ...string) error { + if len(_cmds) == 0 { + return nil + } + + for _, cmdStr := range _cmds { + cmd := exec.CommandContext(ctx, "sh", "-c", cmdStr) + + if dir != "" { + cmd.Dir = dir + } + + // Execute the command + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to run command '%s' in directory '%s': %w", cmdStr, dir, err) + } + } + + return nil +} diff --git a/internal/controller/maker/elastic.go b/internal/controller/maker/elastic.go index a6d18e6..9c75a7b 100644 --- a/internal/controller/maker/elastic.go +++ b/internal/controller/maker/elastic.go @@ -10,6 +10,7 @@ import ( "gitea.loveuer.com/yizhisec/pkg3/logger" "yizhisec.com/hsv2/forge/internal/opt" "yizhisec.com/hsv2/forge/pkg/archiver" + "yizhisec.com/hsv2/forge/pkg/model" "yizhisec.com/hsv2/forge/pkg/resource" ) @@ -65,8 +66,8 @@ RUN chmod +x /data/create_index.sh var ( err error output []byte - location = filepath.Join(m.workdir, "dependency", "elastic") - helperTarLocation = filepath.Join(m.workdir, "dependency", "image", "es-init-helper.alpine-3.22.2.tar") + location = filepath.Join(m.workdir, "dependency", "elastic") + helperTarLocation = filepath.Join(m.workdir, "dependency", "image", "es-init-helper.alpine-3.22.2.tar") ) opt := &elasticOpt{ @@ -178,6 +179,28 @@ RUN chmod +x /data/create_index.sh } logger.Debug("✅ maker.Elastic: 写入 kibana.yaml 文件成功") + logger.Debug("☑️ maker.Elastic: 开始获取所需镜像...") + + var images = []*model.Image{ + {Name: "hub.yizhisec.com/external/elasticsearch:7.17.28", Fallback: "", Save: "elasticsearch.7.17.28.tar"}, + {Name: "hub.yizhisec.com/external/kibana:7.17.28", Fallback: "", Save: "kibana.7.17.28.tar"}, + } + + for _, image := range images { + opts := []ImageOpt{ + WithImageFallback(image.Fallback), + WithImageSave(filepath.Join(location, image.Save)), + WithImageForcePull(image.Force), + } + + if err := m.Image(ctx, image.Name, opts...); err != nil { + logger.Error("❌ maker.Elastic: 获取镜像失败: %s, 可以手动获取后重试", image.Name) + logger.Debug("❌ maker.Elastic: 获取镜像失败: %s, %v", image.Name, err) + return err + } + } + logger.Debug("✅ maker.Elastic: 获取所需镜像成功!!!") + logger.Info("✅ maker.Elastic: 构建 elastic(es) 依赖成功!!!") return nil } diff --git a/internal/controller/maker/emqx.go b/internal/controller/maker/emqx.go index 6c491a5..702c347 100644 --- a/internal/controller/maker/emqx.go +++ b/internal/controller/maker/emqx.go @@ -6,6 +6,7 @@ import ( "path/filepath" "gitea.loveuer.com/yizhisec/pkg3/logger" + "yizhisec.com/hsv2/forge/pkg/model" "yizhisec.com/hsv2/forge/pkg/resource" ) @@ -31,6 +32,14 @@ func (m *maker) EMQX(ctx context.Context) error { } logger.Debug("✅ maker.EMQX: 写入 emqx.yaml 文件成功, dest = %s", filepath.Join(location, "emqx.yaml")) + logger.Debug("☑️ maker.EMQX: 开始获取所需镜像...") + var img = &model.Image{Name: "hub.yizhisec.com/external/emqx:5.1", Fallback: "", Save: "emqx.5.1.tar"} + if err = m.Image(ctx, img.Name, WithImageFallback(img.Fallback), WithImageSave(filepath.Join(location, img.Save))); err != nil { + logger.Debug("❌ maker.EMQX: 获取镜像失败: %s, %v", img.Name, err) + return err + } + logger.Debug("✅ maker.EMQX: 获取所需镜像成功!!!") + logger.Info("✅ maker.EMQX: 构建 emqx(mqtt) 依赖成功!!!") return nil } diff --git a/internal/controller/maker/image.go b/internal/controller/maker/image.go index 56c284c..184f869 100644 --- a/internal/controller/maker/image.go +++ b/internal/controller/maker/image.go @@ -111,31 +111,9 @@ SAVE: func (m *maker) Images(ctx context.Context) error { var images = []*model.Image{ - {Name: "quay.io/k0sproject/apiserver-network-proxy-agent:v0.32.0", Fallback: "hub.yizhisec.com/external/apiserver-network-proxy-agent:v0.32.0", Save: "k0s.apiserver-network-proxy-agent.tar"}, - {Name: "quay.io/k0sproject/cni-node:1.7.1-k0s.0", Fallback: "", Save: "k0s.cni-node.tar"}, - {Name: "quay.io/k0sproject/coredns:1.12.2", Fallback: "", Save: "k0s.coredns.tar"}, - {Name: "quay.io/k0sproject/kube-proxy:v1.33.4", Fallback: "", Save: "k0s.kube-proxy.tar"}, - {Name: "quay.io/k0sproject/kube-router:v2.5.0-iptables1.8.11-0", Fallback: "", Save: "k0s.kube-router.tar"}, - {Name: "quay.io/k0sproject/metrics-server:v0.7.2-0", Fallback: "", Save: "k0s.metrics-server.tar"}, - {Name: "quay.io/k0sproject/pause:3.10.1", Fallback: "", Save: "k0s.pause.tar"}, - {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: "docker.io/bitpoke/mysql-operator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator:v0.6.3", Save: "bitpoke.mysql-operator.v0.6.3.tar"}, - {Name: "docker.io/bitpoke/mysql-operator-orchestrator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-orchestrator:v0.6.3", Save: "bitpoke.mysql-operator-orchestrator.v0.6.3.tar"}, - {Name: "docker.io/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Save: "bitpoke.mysql-operator-sidecar-8.0.v0.6.3.tar"}, - {Name: "docker.io/library/percona:8.0.28-20", Fallback: "docker-mirror.yizhisec.com/library/percona:8.0.28-20", Save: "percona.8.0.28-20.tar"}, - {Name: "docker.io/prom/mysqld-exporter:v0.13.0", Fallback: "docker-mirror.yizhisec.com/prom/mysqld-exporter:v0.13.0", Save: "prom.mysqld-exporter.v0.13.0.tar"}, - - {Name: "docker.io/bitnami/redis:8.2.2", Fallback: "hub.yizhisec.com/external/bitnami/redis:8.2.2", Save: "bitnami.redis.8.2.2.tar"}, - {Name: "hub.yizhisec.com/external/elasticsearch:7.17.28", Fallback: "", Save: "elasticsearch.7.17.28.tar"}, - {Name: "hub.yizhisec.com/external/kibana:7.17.28", Fallback: "", Save: "kibana.7.17.28.tar"}, - {Name: "hub.yizhisec.com/external/emqx:5.1", Fallback: "", Save: "emqx.5.1.tar"}, - - {Name: "hub.yizhisec.com/hybridscope/v3/minio-init:latest", Fallback: "", Save: "dep.minio-init.tar"}, - {Name: "hub.yizhisec.com/external/minio:RELEASE.2025-03-12T18-04-18Z", Fallback: "", Save: "dep.minio.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}, diff --git a/internal/controller/maker/minio.go b/internal/controller/maker/minio.go index 41aaefd..bebe39c 100644 --- a/internal/controller/maker/minio.go +++ b/internal/controller/maker/minio.go @@ -7,26 +7,27 @@ import ( "path/filepath" "gitea.loveuer.com/yizhisec/pkg3/logger" + "yizhisec.com/hsv2/forge/pkg/model" "yizhisec.com/hsv2/forge/pkg/resource" ) // todo, remake minio-init image func (m *maker) Minio(ctx context.Context, storage string) error { var ( - err error - workdir = filepath.Join(m.workdir, "dependency", "minio") + err error + location = filepath.Join(m.workdir, "dependency", "minio") ) - logger.Info("☑️ maker.Minio: 开始构建 minio 依赖, workdir = %s", workdir) + logger.Info("☑️ maker.Minio: 开始构建 minio 依赖, workdir = %s", location) - logger.Debug("☑️ maker.Minio: 构建工作目录, workdir = %s", workdir) - if err = os.MkdirAll(workdir, 0755); err != nil { - logger.Debug("❌ maker.Minio: 创建工作目录失败, workdir = %s, err = %v", workdir, err) + logger.Debug("☑️ maker.Minio: 构建工作目录, workdir = %s", location) + if err = os.MkdirAll(location, 0755); err != nil { + logger.Debug("❌ maker.Minio: 创建工作目录失败, workdir = %s, err = %v", location, err) return err } - logger.Debug("✅ maker.Minio: 创建工作目录成功, workdir = %s", workdir) + logger.Debug("✅ maker.Minio: 创建工作目录成功, workdir = %s", location) - filename := filepath.Join(workdir, "minio.yaml") + filename := filepath.Join(location, "minio.yaml") logger.Debug("☑️ maker.Minio: 准备资源文件, filename = %s, storage = %s", filename, storage) bs := []byte(fmt.Sprintf(resource.YAMLMinIO, storage)) if err = os.WriteFile(filename, bs, 0644); err != nil { @@ -35,7 +36,27 @@ func (m *maker) Minio(ctx context.Context, storage string) error { } logger.Debug("✅ maker.Minio: 准备资源文件成功, filename = %s", filename) - logger.Info("✅ maker.Minio: 构建 minio 依赖成功, workdir = %s", workdir) + logger.Debug("☑️ maker.Minio: 开始获取所需镜像...") + var images = []*model.Image{ + {Name: "hub.yizhisec.com/hybridscope/v3/minio-init:latest", Fallback: "", Save: "dep.minio-init.tar"}, + {Name: "hub.yizhisec.com/external/minio:RELEASE.2025-03-12T18-04-18Z", Fallback: "", Save: "dep.minio.tar"}, + } + for _, image := range images { + opts := []ImageOpt{ + WithImageFallback(image.Fallback), + WithImageSave(filepath.Join(location, image.Save)), + WithImageForcePull(image.Force), + } + + if err := m.Image(ctx, image.Name, opts...); err != nil { + logger.Error("❌ maker.Minio: 获取镜像失败: %s, 可以手动获取后重试", image.Name) + logger.Debug("❌ maker.Minio: 获取镜像失败: %s, %v", image.Name, err) + return err + } + } + logger.Debug("✅ maker.Minio: 获取所需镜像成功!!!") + + logger.Info("✅ maker.Minio: 构建 minio 依赖成功, workdir = %s", location) return nil } diff --git a/internal/controller/maker/mysql.go b/internal/controller/maker/mysql.go index 8000231..eeb1fac 100644 --- a/internal/controller/maker/mysql.go +++ b/internal/controller/maker/mysql.go @@ -10,6 +10,7 @@ import ( "gitea.loveuer.com/yizhisec/pkg3/logger" "yizhisec.com/hsv2/forge/internal/opt" "yizhisec.com/hsv2/forge/pkg/downloader" + "yizhisec.com/hsv2/forge/pkg/model" ) type MysqlOpt func(*mysqlOpt) @@ -116,9 +117,9 @@ spec: err error chartURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/charts/mysql-operator-0.6.3.tgz" - mysqlDir = filepath.Join(m.workdir, "dependency", "mysql") - chartFile = filepath.Join(mysqlDir, "mysql-operator-0.6.3.tgz") - clusterFile = filepath.Join(mysqlDir, "cluster.yaml") + location = filepath.Join(m.workdir, "dependency", "mysql") + chartFile = filepath.Join(location, "mysql-operator-0.6.3.tgz") + clusterFile = filepath.Join(location, "cluster.yaml") ) for _, fn := range opts { @@ -127,13 +128,13 @@ spec: logger.Info("☑️ 开始构建 MySQL 资源...") - if err = os.MkdirAll(mysqlDir, 0755); err != nil { + if err = os.MkdirAll(location, 0755); err != nil { return err } o.Password = base64.StdEncoding.EncodeToString([]byte(o.Password)) - logger.Debug("正在下载 MySQL operator chart..., url = %s, dir = %s", chartURL, mysqlDir) + logger.Debug("正在下载 MySQL operator chart..., url = %s, dir = %s", chartURL, location) if err = downloader.Download( ctx, chartURL, @@ -156,6 +157,31 @@ spec: logger.Debug("✅ 成功创建 database.yaml") + logger.Debug("☑️ MakeMysql: 开始获取所需镜像...") + + var images = []*model.Image{ + {Name: "docker.io/bitpoke/mysql-operator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator:v0.6.3", Save: "bitpoke.mysql-operator.v0.6.3.tar"}, + {Name: "docker.io/bitpoke/mysql-operator-orchestrator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-orchestrator:v0.6.3", Save: "bitpoke.mysql-operator-orchestrator.v0.6.3.tar"}, + {Name: "docker.io/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Save: "bitpoke.mysql-operator-sidecar-8.0.v0.6.3.tar"}, + {Name: "docker.io/library/percona:8.0.28-20", Fallback: "docker-mirror.yizhisec.com/library/percona:8.0.28-20", Save: "percona.8.0.28-20.tar"}, + {Name: "docker.io/prom/mysqld-exporter:v0.13.0", Fallback: "docker-mirror.yizhisec.com/prom/mysqld-exporter:v0.13.0", Save: "prom.mysqld-exporter.v0.13.0.tar"}, + } + + for _, image := range images { + opts := []ImageOpt{ + WithImageFallback(image.Fallback), + WithImageSave(filepath.Join(location, image.Save)), + WithImageForcePull(image.Force), + } + + if err := m.Image(ctx, image.Name, opts...); err != nil { + logger.Error("❌ MakeMysql: 获取镜像失败: %s, 可以手动获取后重试", image.Name) + logger.Debug("❌ MakeMysql: 获取镜像失败: %s, %v", image.Name, err) + return err + } + } + logger.Debug("✅ MakeMysql: 获取镜像成功!!!") + logger.Info("✅ MySQL 资源构建成功!!!") return nil diff --git a/internal/controller/maker/redis.go b/internal/controller/maker/redis.go index 0e26c1d..da0ca20 100644 --- a/internal/controller/maker/redis.go +++ b/internal/controller/maker/redis.go @@ -9,6 +9,7 @@ import ( "gitea.loveuer.com/yizhisec/pkg3/logger" "yizhisec.com/hsv2/forge/internal/opt" "yizhisec.com/hsv2/forge/pkg/downloader" + "yizhisec.com/hsv2/forge/pkg/model" ) type RedisOpt func(*redisOpt) @@ -90,18 +91,18 @@ metrics: fn(o) } - redisDir := filepath.Join(m.workdir, "dependency", "redis") - chartFile := filepath.Join(redisDir, chartFilename) - valuesFile := filepath.Join(redisDir, "values.yaml") + location := filepath.Join(m.workdir, "dependency", "redis") + chartFile := filepath.Join(location, chartFilename) + valuesFile := filepath.Join(location, "values.yaml") logger.Info("☑️ maker.Redis: 开始构建 Redis 资源...") logger.Debug("☑️ maker.Redis: 开始构建 Redis 资源..., opt = %#v", o) - logger.Debug("☑️ maker.Redis: 创建目录 %s", redisDir) - if err := os.MkdirAll(redisDir, 0755); err != nil { + logger.Debug("☑️ maker.Redis: 创建目录 %s", location) + if err := os.MkdirAll(location, 0755); err != nil { logger.Debug("❌ maker.Redis: 创建目录失败: %v", err) return err } - logger.Debug("✅ maker.Redis: 创建目录 %s 成功", redisDir) + logger.Debug("✅ maker.Redis: 创建目录 %s 成功", location) logger.Debug("☑️ maker.Redis: 下载 Redis chart..., url = %s, dest = %s", chartURL, chartFile) if err := downloader.Download( @@ -123,6 +124,14 @@ metrics: } logger.Debug("✅ maker.Redis: 写入 values.yaml 文件成功, dest = %s", valuesFile) + logger.Debug("☑️ maker.Redis: 开始获取镜像...") + img := &model.Image{Name: "docker.io/bitnami/redis:8.2.2", Fallback: "hub.yizhisec.com/external/bitnami/redis:8.2.2", Save: "bitnami.redis.8.2.2.tar"} + if err := m.Image(ctx, img.Name, WithImageFallback(img.Fallback), WithImageSave(filepath.Join(location, img.Save))); err != nil { + logger.Debug("❌ maker.Redis: 获取镜像失败: %s, %v", img.Name, err) + return err + } + logger.Debug("✅ maker.Redis: 获取镜像成功!!!") + logger.Info("✅ maker.Redis: 构建 Redis 资源成功!!!") return nil diff --git a/pkg/resource/nginx/client_pkg.conf b/pkg/resource/nginx/client_pkg.conf new file mode 100644 index 0000000..a7abbd3 --- /dev/null +++ b/pkg/resource/nginx/client_pkg.conf @@ -0,0 +1,28 @@ +user root; +worker_processes auto; + +events { + worker_connections 1024; +} + +http { + log_format custom '$time_iso8601 - $remote_addr - $http_host - $status - $request_time - $request_method - $request_uri'; + access_log /var/log/nginx/access.log custom; + + include /etc/nginx/mime.types; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + + gzip on; + + server { + listen 80; + + location %s { + alias /data; + try_files $uri $uri/ =404; + } + } +} \ No newline at end of file diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go index 2f5cdc6..a6728bb 100644 --- a/pkg/resource/resource.go +++ b/pkg/resource/resource.go @@ -71,6 +71,9 @@ var ( //go:embed yaml/app.nginx.yaml YAMLAppNGINX string + //go:embed yaml/client.pkg.yaml + YAMLClientPKG string + //go:embed ssl/ca.crt SSLCaCrt string @@ -127,4 +130,7 @@ var ( //go:embed nginx/user.conf NGINXUser []byte + + //go:embed nginx/client_pkg.conf + NGINXClientPKG string ) diff --git a/pkg/resource/yaml/client.pkg.yaml b/pkg/resource/yaml/client.pkg.yaml new file mode 100644 index 0000000..69051e7 --- /dev/null +++ b/pkg/resource/yaml/client.pkg.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: client-__os__-deployment + namespace: hsv2 +spec: + replicas: %d + selector: + matchLabels: + app: client-__os__ + template: + metadata: + labels: + app: client-__os__ + spec: + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: client-__os__ + containers: + - name: client-__os__ + image: %s + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: client-__os__-service + namespace: hsv2 +spec: + selector: + app: client-__os__ + ports: + - protocol: TCP + port: 80 + targetPort: 80 + type: ClusterIP diff --git a/pkg/resource/yaml/registry.yaml b/pkg/resource/yaml/registry.yaml index 2a99f5a..5a34377 100644 --- a/pkg/resource/yaml/registry.yaml +++ b/pkg/resource/yaml/registry.yaml @@ -54,7 +54,7 @@ metadata: namespace: db-registry spec: clusterIP: 10.96.123.45 - type: NodePork + type: NodePort selector: app: registry ports: diff --git a/pkg/tool/client/http.go b/pkg/tool/client/http.go new file mode 100644 index 0000000..9991d94 --- /dev/null +++ b/pkg/tool/client/http.go @@ -0,0 +1,40 @@ +package client + +import ( + "crypto/tls" + "net/http" + "net/url" +) + +type HttpClientOption func(*httpClientOption) +type httpClientOption struct { + SkipTLSVerify bool + Proxy string +} + +func HttpClient(opts ...HttpClientOption) *http.Client { + var ( + o = &httpClientOption{} + t = &http.Transport{} + ) + + for _, fn := range opts { + fn(o) + } + + if o.SkipTLSVerify { + t.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + } + + if o.Proxy != "" { + if ins, err := url.Parse(o.Proxy); err == nil { + t.Proxy = http.ProxyURL(ins) + } + } + + c := &http.Client{ + Transport: t, + } + + return c +}