Compare commits
5 Commits
eb87d6fbed
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
760784a5ac | ||
|
|
fcbaa5be2f | ||
|
|
da6a846550 | ||
|
|
ce6ab8ab5f | ||
|
|
aafe60ee35 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,3 +5,7 @@ dist
|
||||
.vscode
|
||||
.idea
|
||||
forge
|
||||
dev_*.sh
|
||||
*.tar
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
41
README.md
41
README.md
@@ -4,7 +4,8 @@
|
||||
|
||||
## 环境要求
|
||||
- Go `1.25.2`
|
||||
- Linux 环境,能够访问外部下载源(如 `artifactory.yizhisec.com`、`docker.io` 等)
|
||||
- Linux 环境,能够访问外部下载源(如 `artifactory.yizhisec.com`、`docker.io` 等
|
||||
- 基础组件: Docker, 7za 等
|
||||
|
||||
## 快速开始
|
||||
```bash
|
||||
@@ -23,40 +24,4 @@ go build ./...
|
||||
|
||||
默认构建目录为 `/root/hsv2-installation`,可通过 `--dir` 修改。
|
||||
|
||||
## 常用命令
|
||||
- `forge`:根命令,支持全局调试开关 `--debug`(参考 `internal/cmd/root.go:12`)。
|
||||
- `forge make`:构建依赖与资源(参考 `internal/cmd/make.go:13`)。
|
||||
- 全局选项:
|
||||
- `--dir`:输出基础目录(默认 `/root/hsv2-installation`,参考 `internal/cmd/make.go:40`)
|
||||
- `--disable-dependency-check`:禁用依赖检查(参考 `internal/cmd/make.go:38`)
|
||||
- 子命令(节选):
|
||||
- `images`:准备镜像相关资源
|
||||
- `binaries`:下载并解压 K8s 相关二进制
|
||||
- `debs`:下载并解压 Debian 包(Docker)
|
||||
- `flannel`:生成 Flannel YAML(参考 `internal/cmd/make.flannel.go`)
|
||||
- `longhorn`:下载 Longhorn chart 并生成 `values.yaml`
|
||||
- `mysql`:下载 MySQL operator 并生成 `cluster.yaml`,支持 `--replica-count` 与 `--storage-size`
|
||||
- `redis`:下载 Redis chart 并生成 `values.yaml`,支持 `--replica-count` 与 `--password`
|
||||
- `emqx`:生成 EMQX 配置
|
||||
- `es`:准备 Elasticsearch/Kibana 相关资源
|
||||
- `yosguard`:生成 YOSGuard 配置与服务文件
|
||||
- `less-dns`:生成 DNS 相关资源
|
||||
- `hs-net`:占位命令,当前仅创建目录(参考 `internal/cmd/make.hsnet.go:23`)
|
||||
- `forge install`:安装占位命令(参考 `internal/cmd/install.go`)。
|
||||
|
||||
## 目录结构(简要)
|
||||
- `internal/cmd`:命令行入口与各 `make` 子命令实现
|
||||
- `internal/controller/maker`:构建流程控制
|
||||
- `pkg/resource`:内置 YAML/SQL/bash 等资源模板
|
||||
- `pkg/downloader`、`pkg/archiver`:下载与解压工具
|
||||
|
||||
## 开发与维护
|
||||
- 构建:`go build ./...`
|
||||
- 测试:`go test ./...`
|
||||
- 代码检查:`go vet ./...`
|
||||
- 格式化:`go fmt ./...`
|
||||
- 依赖清理:`go mod tidy`
|
||||
|
||||
## 注意事项
|
||||
- 部分子命令会从公司内网仓库下载资源,请确保网络可达。
|
||||
- 如果仅试运行流程,可使用 `--disable-dependency-check` 快速跳过依赖检查。
|
||||
## 基础镜像准备
|
||||
7
debug/manual.md
Normal file
7
debug/manual.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 手动打包
|
||||
|
||||
## 打包某个(或多个) app 的 pkg
|
||||
|
||||
- 1. `go run . make app xx`
|
||||
- 2. `cd /root/hsv2-installation/app`
|
||||
- 3. `7z a apps.pkg -pRrPt7Uo9WM1dkXOJmHps56T8BZY2qA4g *`
|
||||
3
debug/mysql.md
Normal file
3
debug/mysql.md
Normal file
@@ -0,0 +1,3 @@
|
||||
- 1. after reboot, mysql-master can't be ready
|
||||
* exec into mysql-master
|
||||
* `update sys_operator.status set value = 1 where name="configured";`
|
||||
5
go.mod
5
go.mod
@@ -3,14 +3,13 @@ module yizhisec.com/hsv2/forge
|
||||
go 1.25.2
|
||||
|
||||
require (
|
||||
gitea.loveuer.com/yizhisec/pkg3 v0.0.1
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/samber/lo v1.52.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -1,5 +1,3 @@
|
||||
gitea.loveuer.com/yizhisec/pkg3 v0.0.1 h1:bcT58K6W7TQ5u7Lt7B5JxrVbU/riXwcrshd2lu+Q22c=
|
||||
gitea.loveuer.com/yizhisec/pkg3 v0.0.1/go.mod h1:Ws/tNONjDC4BLdOXAe+wPD6xk3H7TIXvh2de8qKqQpc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package installcmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/installer"
|
||||
)
|
||||
@@ -8,7 +10,6 @@ import (
|
||||
func Check() *cobra.Command {
|
||||
var (
|
||||
workdir string
|
||||
target string
|
||||
ignoreDisk bool
|
||||
ignoreMemory bool
|
||||
ignoreCPU bool
|
||||
@@ -18,9 +19,20 @@ func Check() *cobra.Command {
|
||||
Use: "check",
|
||||
Short: "Check system requirements",
|
||||
Long: `Check system requirements for the project.`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
if os.Getenv("FORGE_NO_CHECK_DISK") == "true" {
|
||||
ignoreDisk = true
|
||||
}
|
||||
if os.Getenv("FORGE_NO_CHECK_MEMORY") == "true" {
|
||||
ignoreMemory = true
|
||||
}
|
||||
if os.Getenv("FORGE_NO_CHECK_CPU") == "true" {
|
||||
ignoreCPU = true
|
||||
}
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_installer := installer.NewInstaller(workdir, target)
|
||||
return _installer.Check(
|
||||
_installer := installer.NewInstaller(workdir)
|
||||
return _installer.HardwareCheck(
|
||||
cmd.Context(),
|
||||
installer.WithIgnoreDiskCheck(ignoreDisk),
|
||||
installer.WithIgnoreMemoryCheck(ignoreMemory),
|
||||
@@ -30,10 +42,6 @@ func Check() *cobra.Command {
|
||||
}
|
||||
|
||||
_cmd.Flags().StringVar(&workdir, "workdir", "/root/hs-installation", "Working directory")
|
||||
_cmd.Flags().StringVar(&target, "target", "self", "Target")
|
||||
_cmd.Flags().BoolVar(&ignoreDisk, "ignore-check-disk", false, "ignore disk requirement check result")
|
||||
_cmd.Flags().BoolVar(&ignoreMemory, "ignore-check-memory", false, "ignore memory requirement check result")
|
||||
_cmd.Flags().BoolVar(&ignoreCPU, "ignore-check-cpu", false, "ignore cpu requirement check result")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -9,21 +9,23 @@ func K0s() *cobra.Command {
|
||||
|
||||
var (
|
||||
workdir string
|
||||
target string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "k0s",
|
||||
Short: "Install k0s",
|
||||
Long: "Install k0s",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
i := installer.NewInstaller(workdir)
|
||||
return i.CheckOK(cmd.Context())
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_installer := installer.NewInstaller(workdir, target)
|
||||
_installer := installer.NewInstaller(workdir)
|
||||
return _installer.K0s(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.PersistentFlags().StringVar(&workdir, "workdir", "/root/hs-installation", "working directory")
|
||||
_cmd.PersistentFlags().StringVar(&target, "target", "self", "target directory")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -9,22 +9,24 @@ func Prepare() *cobra.Command {
|
||||
|
||||
var (
|
||||
workdir string
|
||||
target string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "prepare",
|
||||
Short: "Prepare for installation",
|
||||
Long: "Prepare for installation",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
i := installer.NewInstaller(workdir)
|
||||
return i.CheckOK(cmd.Context())
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_installer := installer.NewInstaller(workdir, target)
|
||||
_installer := installer.NewInstaller(workdir)
|
||||
_installer.Prepare(cmd.Context())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().StringVar(&workdir, "workdir", "/root/hs-installation", "Working directory")
|
||||
_cmd.Flags().StringVar(&target, "target", "self", "Target")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/cmd/makecmd"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
func makeCmd() *cobra.Command {
|
||||
@@ -68,6 +68,7 @@ func makeCmd() *cobra.Command {
|
||||
makecmd.Proxy(),
|
||||
makecmd.Seafile(),
|
||||
makecmd.App(),
|
||||
makecmd.Apps(),
|
||||
makecmd.Client(),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
@@ -9,6 +12,9 @@ import (
|
||||
func ALL() *cobra.Command {
|
||||
var (
|
||||
_workdir string
|
||||
_vendor string
|
||||
_version string
|
||||
_replica int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
@@ -20,10 +26,6 @@ func ALL() *cobra.Command {
|
||||
mk = maker.NewMaker(_workdir)
|
||||
)
|
||||
|
||||
if err = mk.Images(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.K0s(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,31 +94,35 @@ func ALL() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppOEM(cmd.Context(), "standard", 2); err != nil {
|
||||
if err = mk.AppOEM(cmd.Context(), _version, _vendor, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppUser(cmd.Context(), 2); err != nil {
|
||||
if err = mk.AppUser(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppClient(cmd.Context(), 2); err != nil {
|
||||
if err = mk.AppClient(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppGateway(cmd.Context(), 2); err != nil {
|
||||
if err = mk.AppGateway(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppFront(cmd.Context(), "standard", 2); err != nil {
|
||||
if err = mk.AppFront(cmd.Context(), _version, _vendor, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppMie(cmd.Context(), 2); err != nil {
|
||||
if err = mk.AppMie(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppNginx(cmd.Context()); err != nil {
|
||||
if err = mk.AppNginx(cmd.Context(), _version, _vendor, _replica, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppHelper(cmd.Context(), _version, _vendor, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -124,7 +130,10 @@ func ALL() *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.PersistentFlags().StringVar(&_workdir, "workdir", opt.DefaultWorkdir, "Work directory")
|
||||
_cmd.Flags().StringVar(&_workdir, "workdir", opt.DefaultWorkdir, "Work directory")
|
||||
_cmd.Flags().StringVar(&_vendor, "vendor", "standard", "Vendor name")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "Version, default: v2.2.0-<timestamp>")
|
||||
_cmd.Flags().IntVar(&_replica, "replica", 2, "Replica count")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
)
|
||||
|
||||
func App() *cobra.Command {
|
||||
@@ -20,14 +25,96 @@ func App() *cobra.Command {
|
||||
appOEM(),
|
||||
appFront(),
|
||||
appNginx(),
|
||||
appHelper(),
|
||||
)
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func Apps() *cobra.Command {
|
||||
var (
|
||||
_pkg bool
|
||||
_replica int
|
||||
_vendor string
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "apps",
|
||||
Short: "Make Apps(user, client, gateway, mie, nginx, etc...)",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if model.GetVendor(_vendor) == nil {
|
||||
return fmt.Errorf("invalid vendor: %s", _vendor)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
mk = maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
)
|
||||
|
||||
if err = mk.AppUser(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppClient(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppGateway(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppMie(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppFront(cmd.Context(), _version, _vendor, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppNginx(cmd.Context(), _version, _vendor, _replica, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppOEM(cmd.Context(), _version, _vendor, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppHelper(cmd.Context(), _version, _vendor, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _pkg {
|
||||
var (
|
||||
dir = filepath.Join(opt.Cfg.Make.Dir, "app")
|
||||
_cmd = "7za a -pRrPt7Uo9WM1dkXOJmHps56T8BZY2qA4g -mhe=on apps.pkg *"
|
||||
)
|
||||
|
||||
if err = mk.RunCommand(cmd.Context(), dir, _cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().BoolVar(&_pkg, "pkg", false, "Make upgrade pkg")
|
||||
_cmd.Flags().IntVar(&_replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().StringVar(&_vendor, "vendor", "standard", "Vendor name")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "Apps version, default: v2.2.0-<timestamp>")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func appUser() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
@@ -35,11 +122,12 @@ func appUser() *cobra.Command {
|
||||
Short: "Make User App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppUser(cmd.Context(), replica)
|
||||
return mk.AppUser(cmd.Context(), _version, replica)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "App user version, default: v2.2.0-<timestamp>")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -47,6 +135,7 @@ func appUser() *cobra.Command {
|
||||
func appClient() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
@@ -54,11 +143,12 @@ func appClient() *cobra.Command {
|
||||
Short: "Make Client App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppClient(cmd.Context(), replica)
|
||||
return mk.AppClient(cmd.Context(), _version, replica)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "App client version, default: v2.2.0-<timestamp>")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -66,6 +156,7 @@ func appClient() *cobra.Command {
|
||||
func appGateway() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
@@ -73,11 +164,12 @@ func appGateway() *cobra.Command {
|
||||
Short: "Make Gateway App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppGateway(cmd.Context(), replica)
|
||||
return mk.AppGateway(cmd.Context(), _version, replica)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "App gateway version, default: v2.2.0-<timestamp>")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -85,6 +177,7 @@ func appGateway() *cobra.Command {
|
||||
func appMie() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
@@ -92,11 +185,12 @@ func appMie() *cobra.Command {
|
||||
Short: "Make Mie App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppMie(cmd.Context(), replica)
|
||||
return mk.AppMie(cmd.Context(), _version, replica)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "App mie version, default: v2.2.0-<timestamp>")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -105,6 +199,7 @@ func appOEM() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
vendor string
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
@@ -112,12 +207,13 @@ func appOEM() *cobra.Command {
|
||||
Short: "Make OEM App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppOEM(cmd.Context(), vendor, replica)
|
||||
return mk.AppOEM(cmd.Context(), _version, vendor, replica)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().StringVar(&vendor, "vendor", "standard", "Vendor name")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "App oem version, default: v2.2.0-<timestamp>")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -126,6 +222,7 @@ func appFront() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
vendor string
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
@@ -133,12 +230,13 @@ func appFront() *cobra.Command {
|
||||
Short: "Make Front App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppFront(cmd.Context(), vendor, replica)
|
||||
return mk.AppFront(cmd.Context(), _version, vendor, replica)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().StringVar(&vendor, "vendor", "standard", "Vendor name")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "App front version, default: v2.2.0-<timestamp>")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -146,26 +244,47 @@ func appFront() *cobra.Command {
|
||||
func appNginx() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
disableSeafile bool
|
||||
vendor string
|
||||
_version string
|
||||
_include_image bool
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "nginx",
|
||||
Short: "Make Nginx App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts := []maker.NginxOpt{
|
||||
maker.WithNginxReplica(replica),
|
||||
}
|
||||
if disableSeafile {
|
||||
opts = append(opts, maker.WithoutNginxSeafile())
|
||||
}
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppNginx(cmd.Context(), opts...)
|
||||
return mk.AppNginx(cmd.Context(), _version, vendor, replica, _include_image)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().BoolVar(&disableSeafile, "disable-seafile", false, "Disable seafile")
|
||||
_cmd.Flags().StringVar(&vendor, "vendor", "standard", "Vendor name")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "App nginx version, default: v2.2.0-<timestamp>")
|
||||
_cmd.Flags().BoolVar(&_include_image, "include-image", false, "Include image")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func appHelper() *cobra.Command {
|
||||
var (
|
||||
_replica int
|
||||
_vendor string
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "helper",
|
||||
Short: "Make Helper App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppHelper(cmd.Context(), _version, _vendor, _replica)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&_replica, "replica-count", 2, "Replica count")
|
||||
_cmd.Flags().StringVar(&_vendor, "vendor", "standard", "Vendor name")
|
||||
_cmd.Flags().StringVar(&_version, "version", fmt.Sprintf("v2.2.0-%d", time.Now().Unix()), "App helper version, default: v2.2.0-<timestamp>")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -5,11 +5,14 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
tc "yizhisec.com/hsv2/forge/pkg/tool/client"
|
||||
)
|
||||
|
||||
@@ -23,56 +26,26 @@ func Client() *cobra.Command {
|
||||
clientMac(),
|
||||
clientWin(),
|
||||
clientLinux(),
|
||||
clientBaseImage(),
|
||||
)
|
||||
|
||||
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 {
|
||||
var (
|
||||
_version string
|
||||
_pkg bool
|
||||
)
|
||||
|
||||
_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"
|
||||
ZIP_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_client-csgElink/release/2.1.0-std/hs_client_csgElink.zip"
|
||||
VERSION_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_client-csgElink/release/2.1.0-std/windows_version.json"
|
||||
RC_JSON_URL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/win/win-rc.json"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -84,36 +57,62 @@ func clientWin() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
if _version != "" {
|
||||
logger.InfoCtx(cmd.Context(), "clientWin: using manual version: %s => %s", version, _version)
|
||||
version = _version
|
||||
}
|
||||
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.ClientPKG(
|
||||
cmd.Context(),
|
||||
"win",
|
||||
version,
|
||||
"/api/v2_2/_client/win",
|
||||
maker.WithClientPkgMakePkg(_pkg),
|
||||
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"),
|
||||
maker.WithClientPKGDownload(RC_JSON_URL, "rc.json"),
|
||||
maker.WithClientPKGCMDs("unzip /data/hs_client.zip"),
|
||||
maker.WithClientPKGCMDs("mv resources/app.7z /data/app.7z"),
|
||||
maker.WithClientPKGCMDs("7z x /data/app.7z -o/tmp/app"),
|
||||
maker.WithClientPKGCMDs("rm -rf /data/app.7z"),
|
||||
maker.WithClientPKGCMDs("cp /data/version.json /tmp/app/x64/config/windows_version.json"),
|
||||
maker.WithClientPKGCMDs("cp resources/hybridscope-client-setup-zh.exe /tmp/app/x64/hybridscope-client-setup.exe"),
|
||||
maker.WithClientPKGCMDs("7z a /data/app.7z /tmp/app/x64"),
|
||||
maker.WithClientPKGCMDs("rm -rf /tmp/app"),
|
||||
maker.WithClientPKGCMDs("md5sum /data/app.7z | awk '{print $1}' > /data/app.7z.md5"),
|
||||
maker.WithClientPKGCMDs(`sh -c 'echo "{\"url\":\"/api/v2_2/_client/win/app.7z\",\"md5\":\"$(cat /data/app.7z.md5)\"}" > /data/check.json'`),
|
||||
maker.WithClientPKGCMDs("mv resources/hybridscope_offline_installer.exe /data/hybridscope_offline_installer.exe"),
|
||||
maker.WithClientPKGCMDs("mv resources/hybridscope-client-setup-zh.exe /data/hybridscope-client-setup-zh.exe"),
|
||||
maker.WithClientPKGCMDs("mv resources/hybridscope-client-setup-en.exe /data/hybridscope-client-setup-en.exe"),
|
||||
maker.WithClientPKGCMDs("rm -rf /data/hs_client.zip"),
|
||||
maker.WithClientPKGCMDs("rm -rf resources"),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().StringVar(&_version, "version", "", "手动指定版本")
|
||||
_cmd.Flags().BoolVar(&_pkg, "pkg", false, "是否生成升级 pkg")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func clientLinux() *cobra.Command {
|
||||
func clientMac() *cobra.Command {
|
||||
|
||||
var (
|
||||
_version string
|
||||
_pkg bool
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "linux",
|
||||
Short: "make client-linux pkg",
|
||||
Use: "mac",
|
||||
Aliases: []string{"macos"},
|
||||
Short: "make client-mac 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"
|
||||
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-csgElink/release/2.1.0-std/mac_version.json"
|
||||
// std: VERSION_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_appleclient/release/2.1.0-std/mac_version.json"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -125,18 +124,76 @@ func clientLinux() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
if _version != "" {
|
||||
logger.InfoCtx(cmd.Context(), "clientMac: using manual version: %s => %s", version, _version)
|
||||
version = _version
|
||||
}
|
||||
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.ClientPKG(
|
||||
cmd.Context(),
|
||||
"mac",
|
||||
version,
|
||||
"/api/v2_2/_client/mac",
|
||||
maker.WithClientPkgMakePkg(_pkg),
|
||||
maker.WithClientPKGDownload(DMG_URL, "hybridscope-client-mac.dmg"),
|
||||
maker.WithClientPKGDownload(PKG_URL, "hybridscope-client-mac.pkg"),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().StringVar(&_version, "version", "", "手动指定版本")
|
||||
_cmd.Flags().BoolVar(&_pkg, "pkg", false, "是否生成升级 pkg")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func clientLinux() *cobra.Command {
|
||||
|
||||
var (
|
||||
_version string
|
||||
_pkg bool
|
||||
)
|
||||
|
||||
_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-csgElink-amd64.json"
|
||||
// std: 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
|
||||
}
|
||||
|
||||
if _version != "" {
|
||||
logger.InfoCtx(cmd.Context(), "clientLinux: using manual version: %s => %s", version, _version)
|
||||
version = _version
|
||||
}
|
||||
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.ClientPKG(
|
||||
cmd.Context(),
|
||||
"linux",
|
||||
version,
|
||||
"/api/v2_2/_client/linux",
|
||||
maker.WithClientPkgMakePkg(_pkg),
|
||||
maker.WithClientPKGDownload(DEB_URL, "hybridscope-client-linux.deb"),
|
||||
maker.WithClientPKGDownload(VERSION_URL, "version.json"),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().StringVar(&_version, "version", "", "手动指定版本")
|
||||
_cmd.Flags().BoolVar(&_pkg, "pkg", false, "是否生成升级 pkg")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -174,3 +231,68 @@ func clientVersion(_url string) (string, error) {
|
||||
|
||||
return res.Version, nil
|
||||
}
|
||||
|
||||
func clientBaseImage() *cobra.Command {
|
||||
const (
|
||||
dbs = `
|
||||
FROM %s
|
||||
RUN sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories
|
||||
RUN apk update && apk add curl wget tzdata unzip p7zip
|
||||
ENV TZ=Asia/Shanghai
|
||||
`
|
||||
_command = "docker build -f Dockerfile -t hub.yizhisec.com/hsv2/base/nginx:latest ."
|
||||
)
|
||||
|
||||
var (
|
||||
_push bool
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "base",
|
||||
Short: "make client base image",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
tmpDir = filepath.Join(os.TempDir(), "hsv2-client-base")
|
||||
output []byte
|
||||
)
|
||||
|
||||
if err = os.MkdirAll(tmpDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
dockerfile := filepath.Join(tmpDir, "Dockerfile")
|
||||
if err = os.WriteFile(dockerfile, []byte(fmt.Sprintf(dbs, opt.IMAGE_NGINX)), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc := exec.CommandContext(cmd.Context(), "sh", "-c", _command)
|
||||
rc.Dir = tmpDir
|
||||
|
||||
if output, err = rc.CombinedOutput(); err != nil {
|
||||
logger.Debug("❌ clientBaseImage: build image failed, output = %s, err = %v", string(output), err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Debug("☑️ clientBaseImage: build image output = %s", string(output))
|
||||
|
||||
if _push {
|
||||
rc = exec.CommandContext(cmd.Context(), "sh", "-c", "docker push hub.yizhisec.com/hsv2/base/nginx:latest")
|
||||
|
||||
if output, err = rc.CombinedOutput(); err != nil {
|
||||
logger.Debug("❌ clientBaseImage: push image failed, output = %s, err = %v", string(output), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("☑️ clientBaseImage: push image output = %s", string(output))
|
||||
}
|
||||
|
||||
logger.Info("️✅ clientBaseImage: build image success!!!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().BoolVar(&_push, "push", false, "push image")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package makecmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
@@ -24,5 +26,84 @@ func Minio() *cobra.Command {
|
||||
|
||||
_cmd.Flags().IntVar(&storage, "storage-size", 100, "Storage size(GB)")
|
||||
|
||||
_cmd.AddCommand(minioBase())
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func minioBase() *cobra.Command {
|
||||
var (
|
||||
push bool
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "base",
|
||||
Short: "Make Minio Image Rely(minio initer)",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
const (
|
||||
DOCKERFILE = `FROM %s
|
||||
COPY mc /usr/local/bin/mc
|
||||
RUN sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories && \
|
||||
apk update && \
|
||||
apk add curl wget tzdata && chmod +x /usr/local/bin/mc && \
|
||||
mkdir -p /data && \
|
||||
wget https://artifactory.yizhisec.com/artifactory/filestore/hsv2/db/ipv4.ipdb -O /data/ipv4.ipdb
|
||||
`
|
||||
)
|
||||
var (
|
||||
err error
|
||||
mk = maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
tmpDir = filepath.Join(os.TempDir(), "minio-base")
|
||||
mcFile = filepath.Join(tmpDir, "mc")
|
||||
dockerfile = filepath.Join(tmpDir, "Dockerfile")
|
||||
)
|
||||
|
||||
if err = os.MkdirAll(tmpDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
if err = mk.RunCommand(cmd.Context(),
|
||||
tmpDir,
|
||||
fmt.Sprintf("docker run -d --name minio-base %s server /data", opt.IMAGE_MINIO),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer mk.RunCommand(cmd.Context(), tmpDir, "docker rm -f minio-base")
|
||||
|
||||
if err = mk.RunCommand(cmd.Context(),
|
||||
tmpDir,
|
||||
fmt.Sprintf("docker cp minio-base:/usr/bin/mc %s", mcFile),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(dockerfile, []byte(fmt.Sprintf(DOCKERFILE, opt.IMAGE_ALPINE)), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.RunCommand(cmd.Context(),
|
||||
tmpDir,
|
||||
fmt.Sprintf("docker build --network host -t %s -f %s %s", opt.IMAGE_MINIO_BASE, dockerfile, tmpDir),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if push {
|
||||
if err = mk.RunCommand(cmd.Context(),
|
||||
tmpDir,
|
||||
fmt.Sprintf("docker push %s", opt.IMAGE_MINIO_BASE),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().BoolVar(&push, "push", false, "Push image to registry")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func Redis() *cobra.Command {
|
||||
cmd.Context(),
|
||||
maker.WithRedisReplicaCount(replicas),
|
||||
maker.WithRedisPassword(password),
|
||||
maker.WithRedisStorage(fmt.Sprintf("%dGi")),
|
||||
maker.WithRedisStorage(fmt.Sprintf("%dGi", storage)),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -7,15 +7,21 @@ import (
|
||||
)
|
||||
|
||||
func Yosguard() *cobra.Command {
|
||||
var (
|
||||
_pkg bool // make yosguard upgrade pkg
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "yosguard",
|
||||
Aliases: []string{"YOS"},
|
||||
Short: "Make Yosguard",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Yosguard(cmd.Context())
|
||||
return mk.Yosguard(cmd.Context(), maker.WithYosguardPkg(_pkg))
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().BoolVar(&_pkg, "pkg", false, "make yosguard upgrade pkg")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
@@ -12,14 +11,6 @@ var rootCmd = &cobra.Command{
|
||||
Use: "forge",
|
||||
Short: "Forge is a tool for building and installing hsv2",
|
||||
Long: `A tool for managing build and installation workflows for hsv2.`,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if opt.Cfg.Debug {
|
||||
logger.SetLogLevel(logger.LogLevelDebug)
|
||||
logger.Warn("running in debug mode")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func Execute(ctx context.Context) error {
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
package installer
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/syscheck"
|
||||
"yizhisec.com/hsv2/forge/pkg/tool/human"
|
||||
)
|
||||
|
||||
type CheckOption func(*checkOpt)
|
||||
@@ -13,6 +20,7 @@ type checkOpt struct {
|
||||
ignoreDisk bool
|
||||
ignoreMemory bool
|
||||
ignoreCPU bool
|
||||
noWriteDown bool
|
||||
}
|
||||
|
||||
func WithIgnoreDiskCheck(ignore bool) CheckOption {
|
||||
@@ -33,9 +41,177 @@ func WithIgnoreCPUCheck(ignore bool) CheckOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (i *installer) Check(ctx context.Context, opts ...CheckOption) error {
|
||||
func WithNoWriteDown(noWriteDown bool) CheckOption {
|
||||
return func(o *checkOpt) {
|
||||
o.noWriteDown = noWriteDown
|
||||
}
|
||||
}
|
||||
|
||||
type HardwareCheckResult struct {
|
||||
Timestamp int64 // ms
|
||||
DiskSize int64 // bytes, if err, -1; if ignore, 0
|
||||
DiskReadSpeed int64 // bytes/s, if err, -1; if ignore, 0
|
||||
DiskWriteSpeed int64 // bytes/s, if err, -1; if ignore, 0
|
||||
MemorySize int64 // bytes, if err, -1; if ignore, 0
|
||||
MemoryReadSpeed int64 // bytes/s, if err, -1; if ignore, 0
|
||||
MemoryWriteSpeed int64 // bytes/s, if err, -1; if ignore, 0
|
||||
CPUCores int64 // if err, -1; if ignore, 0
|
||||
CPUFrequency int64 // MHz, if err, -1; if ignore, 0
|
||||
CPUSupportAES bool
|
||||
CPUIsX86V2 bool
|
||||
}
|
||||
|
||||
func (h *HardwareCheckResult) Write(filename string) error {
|
||||
content := []byte(fmt.Sprintf(`timestamp=%d
|
||||
disk_size=%d
|
||||
disk_read_speed=%d
|
||||
disk_write_speed=%d
|
||||
memory_size=%d
|
||||
memory_read_speed=%d
|
||||
memory_write_speed=%d
|
||||
cpu_cores=%d
|
||||
cpu_frequency=%d
|
||||
cpu_support_aes=%t
|
||||
cpu_is_x86_v2=%t`,
|
||||
h.Timestamp,
|
||||
h.DiskSize,
|
||||
h.DiskReadSpeed,
|
||||
h.DiskWriteSpeed,
|
||||
h.MemorySize,
|
||||
h.MemoryReadSpeed,
|
||||
h.MemoryWriteSpeed,
|
||||
h.CPUCores,
|
||||
h.CPUFrequency,
|
||||
h.CPUSupportAES,
|
||||
h.CPUIsX86V2,
|
||||
))
|
||||
return os.WriteFile(filename, content, 0644)
|
||||
}
|
||||
|
||||
func (h *HardwareCheckResult) Load(filename string) error {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
switch key {
|
||||
case "timestamp":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.Timestamp = -1
|
||||
} else {
|
||||
h.Timestamp = v
|
||||
}
|
||||
case "disk_size":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.DiskSize = -1
|
||||
} else {
|
||||
h.DiskSize = v
|
||||
}
|
||||
case "disk_read_speed":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.DiskReadSpeed = -1
|
||||
} else {
|
||||
h.DiskReadSpeed = v
|
||||
}
|
||||
case "disk_write_speed":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.DiskWriteSpeed = -1
|
||||
} else {
|
||||
h.DiskWriteSpeed = v
|
||||
}
|
||||
case "memory_size":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.MemorySize = -1
|
||||
} else {
|
||||
h.MemorySize = v
|
||||
}
|
||||
case "memory_read_speed":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.MemoryReadSpeed = -1
|
||||
} else {
|
||||
h.MemoryReadSpeed = v
|
||||
}
|
||||
case "memory_write_speed":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.MemoryWriteSpeed = -1
|
||||
} else {
|
||||
h.MemoryWriteSpeed = v
|
||||
}
|
||||
case "cpu_cores":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.CPUCores = -1
|
||||
} else {
|
||||
h.CPUCores = v
|
||||
}
|
||||
case "cpu_frequency":
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
h.CPUFrequency = -1
|
||||
} else {
|
||||
h.CPUFrequency = v
|
||||
}
|
||||
case "cpu_support_aes":
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err == nil {
|
||||
h.CPUSupportAES = v
|
||||
}
|
||||
// bool fields don't use -1, keep default false on error
|
||||
case "cpu_is_x86_v2":
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err == nil {
|
||||
h.CPUIsX86V2 = v
|
||||
}
|
||||
// bool fields don't use -1, keep default false on error
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HardwareCheckResult) Pass(opts ...CheckOption) error {
|
||||
const (
|
||||
DISK_SIZE int64 = 480 * 1024 * 1024 * 1024 // 480GB
|
||||
DISK_READ_SPEED int64 = 1024 * 1024 * 1024 // 1024MB/s
|
||||
DISK_WRITE_SPEED int64 = 500 * 1024 * 1024 // 500MB/s
|
||||
MEMORY_SIZE int64 = 15.5 * 1024 * 1024 * 1024 // 15.5GB
|
||||
MEMORY_READ_SPEED int64 = 1024 * 1024 * 1024 // 1024MB/s
|
||||
MEMORY_WRITE_SPEED int64 = 500 * 1024 * 1024 // 500MB/s
|
||||
CPUCORES int64 = 8
|
||||
CPU_FREQUENCY int64 = 2.0 * 1024 // 2.0GHz
|
||||
CPU_NEED_V2 = true
|
||||
CPU_NEED_AES = true
|
||||
)
|
||||
|
||||
var (
|
||||
err error
|
||||
now = time.Now()
|
||||
o = &checkOpt{}
|
||||
)
|
||||
|
||||
@@ -43,98 +219,195 @@ func (i *installer) Check(ctx context.Context, opts ...CheckOption) error {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
logger.Info("☑️ installer.Check: Starting system checks...")
|
||||
|
||||
if err = i.targetOK(ctx); err != nil {
|
||||
return err
|
||||
if h.Timestamp < now.AddDate(0, 0, -1).UnixMilli() {
|
||||
return fmt.Errorf("上次检测结果已失效, 请重新检测")
|
||||
}
|
||||
|
||||
// 1. Check disk space: >= 500GB
|
||||
logger.Info("☑️ installer.Check: Checking disk space...")
|
||||
diskSpaceResult, err := syscheck.CheckDiskSpace(ctx, i, 500)
|
||||
if err != nil {
|
||||
logger.Debug("❌ installer.Check: Failed to check disk space: %v", err)
|
||||
return fmt.Errorf("failed to check disk space: %w", err)
|
||||
if o.ignoreDisk || h.DiskSize+h.DiskReadSpeed+h.DiskWriteSpeed == -3 {
|
||||
goto PASS_MEMORY
|
||||
}
|
||||
|
||||
if !diskSpaceResult.Passed {
|
||||
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", diskSpaceResult.Name, diskSpaceResult.Message, diskSpaceResult.Expected, diskSpaceResult.Actual)
|
||||
if !o.ignoreDisk {
|
||||
return fmt.Errorf("disk space check failed: %s", diskSpaceResult.Message)
|
||||
}
|
||||
if h.DiskSize < DISK_SIZE {
|
||||
return fmt.Errorf("磁盘大小不满足, 需要: %s, 实际: %s", human.Size(DISK_SIZE), human.Size(h.DiskSize))
|
||||
}
|
||||
|
||||
logger.Info("✅ %s: %s", diskSpaceResult.Name, diskSpaceResult.Actual)
|
||||
|
||||
// 2. Check disk performance: write >= 500MB/s, read >= 500MB/s
|
||||
logger.Info("☑️ installer.Check: Checking disk performance...")
|
||||
diskPerfResult, err := syscheck.CheckDiskPerformance(ctx, i, 500, 500)
|
||||
if err != nil {
|
||||
logger.Debug("❌ installer.Check: Failed to check disk performance: %v", err)
|
||||
return fmt.Errorf("failed to check disk performance: %w", err)
|
||||
if h.DiskReadSpeed < DISK_READ_SPEED {
|
||||
return fmt.Errorf("磁盘读速度不满足, 需要: %s, 实际: %s", human.Size(DISK_READ_SPEED), human.Size(h.DiskReadSpeed))
|
||||
}
|
||||
|
||||
if !diskPerfResult.Passed {
|
||||
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", diskPerfResult.Name, diskPerfResult.Message, diskPerfResult.Expected, diskPerfResult.Actual)
|
||||
if !o.ignoreDisk {
|
||||
return fmt.Errorf("disk performance check failed: %s", diskPerfResult.Message)
|
||||
}
|
||||
if h.DiskWriteSpeed < DISK_WRITE_SPEED {
|
||||
return fmt.Errorf("磁盘写速度不满足, 需要: %s, 实际: %s", human.Size(DISK_WRITE_SPEED), human.Size(h.DiskWriteSpeed))
|
||||
}
|
||||
|
||||
logger.Info("✅ %s: %s", diskPerfResult.Name, diskPerfResult.Actual)
|
||||
PASS_MEMORY:
|
||||
|
||||
// 3. Check memory size: >= 15.5GB
|
||||
logger.Info("☑️ installer.Check: Checking memory size...")
|
||||
memResult, err := syscheck.CheckMemory(ctx, i, 15.5)
|
||||
if err != nil {
|
||||
logger.Debug("❌ installer.Check: Failed to check memory: %v", err)
|
||||
return fmt.Errorf("failed to check memory: %w", err)
|
||||
if o.ignoreMemory || h.MemorySize+h.MemoryReadSpeed+h.MemoryWriteSpeed == -3 {
|
||||
goto PASS_CPU
|
||||
}
|
||||
|
||||
if !memResult.Passed {
|
||||
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", memResult.Name, memResult.Message, memResult.Expected, memResult.Actual)
|
||||
if !o.ignoreMemory {
|
||||
return fmt.Errorf("memory check failed: %s", memResult.Message)
|
||||
}
|
||||
if h.MemorySize < MEMORY_SIZE {
|
||||
return fmt.Errorf("内存大小不满足, 需要: %s, 实际: %s", human.Size(MEMORY_SIZE), human.Size(h.MemorySize))
|
||||
}
|
||||
|
||||
logger.Info("✅ %s: %s", memResult.Name, memResult.Actual)
|
||||
|
||||
// 4. Check CPU cores: >= 8
|
||||
logger.Info("☑️ installer.Check: Checking CPU cores...")
|
||||
cpuCoresResult, err := syscheck.CheckCPUCores(ctx, i, 8)
|
||||
if err != nil {
|
||||
logger.Debug("❌ installer.Check: Failed to check CPU cores: %v", err)
|
||||
return fmt.Errorf("failed to check CPU cores: %w", err)
|
||||
if h.MemoryReadSpeed < MEMORY_READ_SPEED {
|
||||
return fmt.Errorf("内存读速度不满足, 需要: %s, 实际: %s", human.Size(MEMORY_READ_SPEED), human.Size(h.MemoryReadSpeed))
|
||||
}
|
||||
|
||||
if !cpuCoresResult.Passed {
|
||||
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", cpuCoresResult.Name, cpuCoresResult.Message, cpuCoresResult.Expected, cpuCoresResult.Actual)
|
||||
if !o.ignoreCPU {
|
||||
return fmt.Errorf("CPU cores check failed: %s", cpuCoresResult.Message)
|
||||
}
|
||||
if h.MemoryWriteSpeed < MEMORY_WRITE_SPEED {
|
||||
return fmt.Errorf("内存写速度不满足, 需要: %s, 实际: %s", human.Size(MEMORY_WRITE_SPEED), human.Size(h.MemoryWriteSpeed))
|
||||
}
|
||||
|
||||
logger.Info("✅ %s: %s", cpuCoresResult.Name, cpuCoresResult.Actual)
|
||||
PASS_CPU:
|
||||
|
||||
// 5. Check CPU frequency: >= 2GHz
|
||||
logger.Info("☑️ installer.Check: Checking CPU frequency...")
|
||||
cpuFreqResult, err := syscheck.CheckCPUFrequency(ctx, i, 2.0)
|
||||
if err != nil {
|
||||
logger.Debug("❌ installer.Check: Failed to check CPU frequency: %v", err)
|
||||
return fmt.Errorf("failed to check CPU frequency: %w", err)
|
||||
if o.ignoreCPU || h.CPUCores+h.CPUFrequency == -2 {
|
||||
goto END
|
||||
}
|
||||
if h.CPUCores < CPUCORES {
|
||||
return fmt.Errorf("cpu 核心数不满足, 需要: %d, 实际: %d", CPUCORES, h.CPUCores)
|
||||
}
|
||||
|
||||
if !cpuFreqResult.Passed {
|
||||
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", cpuFreqResult.Name, cpuFreqResult.Message, cpuFreqResult.Expected, cpuFreqResult.Actual)
|
||||
if !o.ignoreCPU {
|
||||
return fmt.Errorf("CPU frequency check failed: %s", cpuFreqResult.Message)
|
||||
}
|
||||
if h.CPUFrequency < CPU_FREQUENCY {
|
||||
return fmt.Errorf("cpu 频率不满足, 需要: %s, 实际: %s", human.Size(CPU_FREQUENCY), human.Size(h.CPUFrequency))
|
||||
}
|
||||
|
||||
logger.Info("✅ %s: %s", cpuFreqResult.Name, cpuFreqResult.Actual)
|
||||
if CPU_NEED_V2 && !h.CPUIsX86V2 {
|
||||
return fmt.Errorf("cpu 不支持aes")
|
||||
}
|
||||
|
||||
logger.Info("✅ installer.Check: All system checks passed successfully!")
|
||||
if CPU_NEED_AES && !h.CPUSupportAES {
|
||||
return fmt.Errorf("cpu 不支持 x86_v2s")
|
||||
}
|
||||
|
||||
END:
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *installer) HardwareCheck(ctx context.Context, opts ...CheckOption) error {
|
||||
var (
|
||||
err error
|
||||
o = &checkOpt{}
|
||||
now = time.Now()
|
||||
result = &HardwareCheckResult{
|
||||
Timestamp: now.UnixMilli(),
|
||||
}
|
||||
cpuInfo syscheck.CPUInfo
|
||||
dir string
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
logger.Info("✅ 开始目标机器系统检测...")
|
||||
|
||||
if o.ignoreDisk {
|
||||
logger.Warn("⚠️ 跳过磁盘检测")
|
||||
result.DiskSize = -1
|
||||
result.DiskReadSpeed = -1
|
||||
result.DiskWriteSpeed = -1
|
||||
goto CHECK_MEMORY
|
||||
}
|
||||
|
||||
if result.DiskSize, err = syscheck.GetDiskSpace(ctx); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to get disk space: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("💾 磁盘容量: %s", human.Size(result.DiskSize))
|
||||
|
||||
if result.DiskReadSpeed, result.DiskWriteSpeed, err = syscheck.GetDiskSpeed(ctx); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to get disk speed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("💾 磁盘速率: 读取(%s/每秒), 写入(%s/每秒)", human.Size(result.DiskReadSpeed), human.Size(result.DiskWriteSpeed))
|
||||
|
||||
CHECK_MEMORY:
|
||||
|
||||
if o.ignoreMemory {
|
||||
logger.Warn("⚠️ 跳过内存检测")
|
||||
result.MemorySize = -1
|
||||
result.MemoryReadSpeed = -1
|
||||
result.MemoryWriteSpeed = -1
|
||||
goto CHECK_CPU
|
||||
}
|
||||
|
||||
if result.MemorySize, err = syscheck.GetMemorySpace(ctx); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to get memory size: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("💿 内存容量: %s", human.Size(result.MemorySize))
|
||||
|
||||
if result.MemoryReadSpeed, result.MemoryWriteSpeed, err = syscheck.GetMemorySpeed(ctx); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to get memory speed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("💿 内存速率: 读取(%s/每秒), 写入(%s/每秒)", human.Size(result.MemoryReadSpeed), human.Size(result.MemoryWriteSpeed))
|
||||
|
||||
CHECK_CPU:
|
||||
if o.ignoreCPU {
|
||||
logger.Warn("⚠️ 跳过 CPU 检测")
|
||||
result.CPUCores = -1
|
||||
result.CPUFrequency = -1
|
||||
result.CPUSupportAES = false
|
||||
result.CPUIsX86V2 = false
|
||||
goto END
|
||||
}
|
||||
|
||||
if cpuInfo, err = syscheck.GetCPUInfo(ctx); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to get CPU info: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
result.CPUCores = cpuInfo.Cores
|
||||
result.CPUFrequency = cpuInfo.FrequencyMHz
|
||||
result.CPUSupportAES = cpuInfo.SupportAES
|
||||
result.CPUIsX86V2 = cpuInfo.IsX86V2
|
||||
|
||||
logger.Info("🧮 CPU 核心数: %d", result.CPUCores)
|
||||
logger.Info("🧮 CPU 频率: %d Mhz", result.CPUFrequency)
|
||||
logger.Info("🧮 CPU 支持 AES: %t", result.CPUSupportAES)
|
||||
END:
|
||||
|
||||
if dir, err = os.UserHomeDir(); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to get user home directory: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = result.Write(filepath.Join(dir, ".hsv2-installation")); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to write installation file: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = result.Pass(opts...); err != nil {
|
||||
logger.Error("❌ %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Info("✅ 检测完成")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*installer) CheckOK(ctx context.Context) error {
|
||||
var (
|
||||
err error
|
||||
h = &HardwareCheckResult{}
|
||||
dir string
|
||||
)
|
||||
|
||||
if dir, err = os.UserHomeDir(); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to get user home directory: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = h.Load(filepath.Join(dir, ".hsv2-installation")); err != nil {
|
||||
logger.Debug("❌ installer.HardwareCheck: Failed to load installation file: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return h.Pass()
|
||||
}
|
||||
|
||||
@@ -2,41 +2,27 @@ package installer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type installer struct {
|
||||
workdir string
|
||||
target string
|
||||
}
|
||||
|
||||
func (i *installer) buildCommand(ctx context.Context, cmds ...string) *exec.Cmd {
|
||||
if len(cmds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if i.target == "self" {
|
||||
return exec.CommandContext(ctx, cmds[0], cmds[1:]...)
|
||||
}
|
||||
|
||||
sshArgs := append([]string{i.target}, cmds...)
|
||||
return exec.CommandContext(ctx, "ssh", sshArgs...)
|
||||
}
|
||||
|
||||
// ExecuteCommand implements syscheck.CommandExecutor interface
|
||||
func (i *installer) ExecuteCommand(ctx context.Context, cmds ...string) (string, error) {
|
||||
cmd := i.buildCommand(ctx, cmds...)
|
||||
if cmd == nil {
|
||||
return "", fmt.Errorf("failed to build command")
|
||||
fcs := lo.Filter(cmds, func(item string, _ int) bool { return item != "" })
|
||||
|
||||
if len(fcs) == 0 {
|
||||
return "", fmt.Errorf("empty commands")
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", strings.Join(fcs, " "))
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("command failed: %w, output: %s", err, string(output))
|
||||
@@ -45,109 +31,6 @@ func (i *installer) ExecuteCommand(ctx context.Context, cmds ...string) (string,
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
type CopyFileOption func(*copyFileOptions)
|
||||
type copyFileOptions struct {
|
||||
addExecutable bool
|
||||
}
|
||||
|
||||
func withCopyFileExecutable() func(*copyFileOptions) {
|
||||
return func(o *copyFileOptions) {
|
||||
o.addExecutable = true
|
||||
}
|
||||
}
|
||||
|
||||
func (i *installer) copyFile(ctx context.Context, src, dst string, opts ...CopyFileOption) error {
|
||||
logger.Debug("☑️ installer.copyFile: Copying file from %s to %s (target: %s)", src, dst, i.target)
|
||||
|
||||
var (
|
||||
err error
|
||||
o = ©FileOptions{}
|
||||
srcFile, dstFile *os.File
|
||||
srcInfo os.FileInfo
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
if i.target == "self" {
|
||||
// Simply copy file locally
|
||||
logger.Debug("Copying file locally: %s -> %s", src, dst)
|
||||
|
||||
// Open source file
|
||||
if srcFile, err = os.Open(src); err != nil {
|
||||
logger.Error("❌ Failed to open source file %s: %v", src, err)
|
||||
return fmt.Errorf("failed to open source file: %w", err)
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// Create destination directory if needed
|
||||
dstDir := filepath.Dir(dst)
|
||||
if err = os.MkdirAll(dstDir, 0755); err != nil {
|
||||
logger.Error("❌ Failed to create destination directory %s: %v", dstDir, err)
|
||||
return fmt.Errorf("failed to create destination directory: %w", err)
|
||||
}
|
||||
|
||||
// Create destination file
|
||||
if dstFile, err = os.Create(dst); err != nil {
|
||||
logger.Error("❌ Failed to create destination file %s: %v", dst, err)
|
||||
return fmt.Errorf("failed to create destination file: %w", err)
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
// Copy file content
|
||||
if _, err = io.Copy(dstFile, srcFile); err != nil {
|
||||
logger.Error("❌ Failed to copy file content: %v", err)
|
||||
return fmt.Errorf("failed to copy file content: %w", err)
|
||||
}
|
||||
|
||||
// Get source file permissions
|
||||
if srcInfo, err = os.Stat(src); err == nil {
|
||||
if err = os.Chmod(dst, srcInfo.Mode()); err != nil {
|
||||
logger.Debug("⚠️ Failed to set file permissions: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("✅ File copied locally: %s -> %s", src, dst)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy file via scp to remote target
|
||||
logger.Debug("Copying file via scp: %s -> %s:%s", src, i.target, dst)
|
||||
|
||||
// Format: scp <src> <target>:<dst>
|
||||
cmd := exec.CommandContext(ctx, "scp", src, fmt.Sprintf("%s:%s", i.target, dst))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
logger.Error("❌ Failed to copy file via scp: %v, output: %s", err, string(output))
|
||||
return fmt.Errorf("failed to copy file via scp: %w, output: %s", err, string(output))
|
||||
}
|
||||
|
||||
logger.Info("✅ File copied via scp: %s -> %s:%s", src, i.target, dst)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *installer) targetOK(ctx context.Context) error {
|
||||
cmd := i.buildCommand(ctx, "whoami")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
logger.Debug("❌ installer.targetOK: check target %s failed, err = %v", i.target, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if string(output) != "root\n" {
|
||||
logger.Debug("❌ installer.targetOK: check target %s failed, output = %s", i.target, string(output))
|
||||
return errors.New("target is not root user")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewInstaller(workdir, target string) *installer {
|
||||
if target == "" {
|
||||
logger.Warn("🎯 NewInstaller: target empty, set to default(self)")
|
||||
target = "self"
|
||||
}
|
||||
|
||||
return &installer{workdir: workdir, target: target}
|
||||
func NewInstaller(workdir string) *installer {
|
||||
return &installer{workdir: workdir}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@ package installer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
@@ -41,83 +37,5 @@ func WithK0sWorkerTokenFile(filename string) K0sOpt {
|
||||
}
|
||||
|
||||
func (i *installer) K0s(ctx context.Context, opts ...K0sOpt) error {
|
||||
var (
|
||||
err error
|
||||
o = &k0sOpt{
|
||||
Type: "controller",
|
||||
controllerAsWorker: false,
|
||||
WorkerTokenFile: "/etc/k0s/worker.token",
|
||||
}
|
||||
)
|
||||
|
||||
if err = i.targetOK(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
binaries := []string{
|
||||
"dependency/bin/k0s",
|
||||
"dependency/bin/k9s", "dependency/bin/kubectl", "dependency/bin/helm"}
|
||||
if err = i.checkFiles(binaries...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check image tar files:
|
||||
images := []string{
|
||||
"dependency/image/k0s.apiserver-network-proxy-agent.tar",
|
||||
"dependency/image/k0s.cni-node.tar",
|
||||
"dependency/image/k0s.coredns.tar",
|
||||
"dependency/image/k0s.kube-proxy.tar",
|
||||
"dependency/image/k0s.kube-router.tar",
|
||||
"dependency/image/k0s.metrics-server.tar",
|
||||
"dependency/image/k0s.pause.tar",
|
||||
}
|
||||
|
||||
if err = i.checkFiles(images...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// copy binaries to /usr/local/bin and add executable permissions
|
||||
if err = i.copyFile(ctx, "dependency/bin/k0s", "/usr/local/bin/k0s", withCopyFileExecutable()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = i.copyFile(ctx, "dependency/bin/k9s", "/usr/local/bin/k9s", withCopyFileExecutable()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = i.copyFile(ctx, "dependency/bin/kubectl", "/usr/local/bin/kubectl", withCopyFileExecutable()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = i.copyFile(ctx, "dependency/bin/helm", "/usr/local/bin/helm", withCopyFileExecutable()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.ExecuteCommand(ctx, "k0s", "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkBinaryFiles checks if the required binary files exist in the dependency/bin directory
|
||||
func (i *installer) checkFiles(fileBaseName ...string) error {
|
||||
logger.Info("☑️ installer.checkFiles: Checking files in %s...", i.workdir)
|
||||
|
||||
for _, file := range fileBaseName {
|
||||
filename := filepath.Join(i.workdir, file)
|
||||
logger.Debug("Checking file: %s", filename)
|
||||
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
logger.Error("❌ File not found: %s", filename)
|
||||
return fmt.Errorf("file not found: %s", filename)
|
||||
} else if err != nil {
|
||||
logger.Error("❌ Failed to check file %s: %v", filename, err)
|
||||
return fmt.Errorf("failed to check file %s: %w", filename, err)
|
||||
}
|
||||
|
||||
logger.Info("✅ File found: %s", file)
|
||||
}
|
||||
|
||||
logger.Info("✅ installer.checkBinaryFiles: All binary files verified successfully!")
|
||||
return nil
|
||||
panic("plz impl")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
func (i *installer) Prepare(ctx context.Context) error {
|
||||
@@ -14,10 +14,6 @@ func (i *installer) Prepare(ctx context.Context) error {
|
||||
|
||||
logger.Info("☑️ installer.Prepare: Starting system preparation...")
|
||||
|
||||
if err = i.targetOK(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 1. Set timezone to Asia/Shanghai
|
||||
logger.Info("☑️ installer.Prepare: Setting timezone to Asia/Shanghai...")
|
||||
if err = i.setTimezone(ctx); err != nil {
|
||||
@@ -65,31 +61,23 @@ func (i *installer) Prepare(ctx context.Context) error {
|
||||
|
||||
// setTimezone sets the system timezone to Asia/Shanghai
|
||||
func (i *installer) setTimezone(ctx context.Context) error {
|
||||
var (
|
||||
err error
|
||||
output string
|
||||
)
|
||||
// Check if timezone file exists
|
||||
cmd := i.buildCommand(ctx, "test", "-f", "/usr/share/zoneinfo/Asia/Shanghai")
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("timezone file /usr/share/zoneinfo/Asia/Shanghai not found")
|
||||
if output, err = i.ExecuteCommand(ctx, "test", "-f", "/usr/share/zoneinfo/Asia/Shanghai"); err != nil {
|
||||
return fmt.Errorf("failed to set timezone, err =%s, raw = %s", err.Error(), output)
|
||||
}
|
||||
|
||||
// Remove old localtime link/file
|
||||
cmd = i.buildCommand(ctx, "rm", "-f", "/etc/localtime")
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
logger.Debug("Failed to remove /etc/localtime: %v", err)
|
||||
if output, err = i.ExecuteCommand(ctx, "rm", "-f", "/etc/localtime"); err != nil {
|
||||
return fmt.Errorf("failed to set timezone, err =%s, raw = %s", err.Error(), output)
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
cmd = i.buildCommand(ctx, "ln", "-s", "/usr/share/zoneinfo/Asia/Shanghai", "/etc/localtime")
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to create symlink: %w", err)
|
||||
if output, err = i.ExecuteCommand(ctx, "ln", "-s", "/usr/share/zoneinfo/Asia/Shanghai", "/etc/localtime"); err != nil {
|
||||
return fmt.Errorf("failed to set timezone, err =%s, raw = %s", err.Error(), output)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -97,22 +85,19 @@ func (i *installer) setTimezone(ctx context.Context) error {
|
||||
|
||||
// disableSwap disables all swap partitions and removes swap entries from /etc/fstab
|
||||
func (i *installer) disableSwap(ctx context.Context) error {
|
||||
var (
|
||||
err error
|
||||
output string
|
||||
)
|
||||
|
||||
// Turn off all swap
|
||||
cmd := i.buildCommand(ctx, "swapoff", "-a")
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
logger.Debug("Failed to swapoff: %v (may be already off)", err)
|
||||
if output, err = i.ExecuteCommand(ctx, "swapoff", "-a"); err != nil {
|
||||
logger.Debug("Failed to swapoff: %v (may be already off), raw = %s", err, output)
|
||||
}
|
||||
|
||||
// Comment out swap entries in /etc/fstab to make it persistent
|
||||
cmd = i.buildCommand(ctx, "sed", "-i", "/swap/s/^/#/", "/etc/fstab")
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
logger.Debug("Failed to comment swap in /etc/fstab: %v", err)
|
||||
if output, err = i.ExecuteCommand(ctx, "sed", "-i", "/swap/s/^/#/", "/etc/fstab"); err != nil {
|
||||
logger.Debug("Failed to comment swap in /etc/fstab: %v, raw = %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -120,23 +105,21 @@ func (i *installer) disableSwap(ctx context.Context) error {
|
||||
|
||||
// loadKernelModule loads a kernel module and ensures it's loaded on boot
|
||||
func (i *installer) loadKernelModule(ctx context.Context, moduleName string) error {
|
||||
var (
|
||||
err error
|
||||
output string
|
||||
)
|
||||
|
||||
// Load the module immediately
|
||||
cmd := i.buildCommand(ctx, "modprobe", moduleName)
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to load module %s: %w", moduleName, err)
|
||||
if output, err = i.ExecuteCommand(ctx, "modprobe", moduleName); err != nil {
|
||||
return fmt.Errorf("failed to load module %s: %w, raw = %s", moduleName, err, output)
|
||||
}
|
||||
|
||||
// Add to /etc/modules-load.d/ to load on boot
|
||||
filePath := fmt.Sprintf("/etc/modules-load.d/%s.conf", moduleName)
|
||||
cmd = i.buildCommand(ctx, "bash", "-c", fmt.Sprintf("echo '%s' > %s", moduleName, filePath))
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
logger.Debug("Failed to add module to modules-load.d: %v", err)
|
||||
command := fmt.Sprintf("echo '%s' > %s", moduleName, filePath)
|
||||
if output, err = i.ExecuteCommand(ctx, "bash", "-c", command); err != nil {
|
||||
logger.Debug("Failed to add module to modules-load.d: %v, raw = %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -144,6 +127,11 @@ func (i *installer) loadKernelModule(ctx context.Context, moduleName string) err
|
||||
|
||||
// applySysctlSettings applies required sysctl settings for Kubernetes
|
||||
func (i *installer) applySysctlSettings(ctx context.Context) error {
|
||||
var (
|
||||
err error
|
||||
output string
|
||||
)
|
||||
|
||||
const sysctlConfig = `# Kubernetes required settings
|
||||
net.bridge.bridge-nf-call-iptables = 1
|
||||
net.bridge.bridge-nf-call-ip6tables = 1
|
||||
@@ -163,21 +151,14 @@ net.ipv4.neigh.default.gc_thresh3 = 8192
|
||||
`
|
||||
|
||||
// Write sysctl config file
|
||||
cmd := i.buildCommand(ctx, "bash", "-c", fmt.Sprintf("cat > /etc/sysctl.d/99-kubernetes.conf << 'EOF'\n%sEOF", sysctlConfig))
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to write sysctl config: %w", err)
|
||||
command := fmt.Sprintf("cat > /etc/sysctl.d/99-kubernetes.conf << 'EOF'\n%sEOF", sysctlConfig)
|
||||
if output, err = i.ExecuteCommand(ctx, "bash", "-c", command); err != nil {
|
||||
return fmt.Errorf("failed to write sysctl config: %w, raw = %s", err, output)
|
||||
}
|
||||
|
||||
// Apply sysctl settings
|
||||
cmd = i.buildCommand(ctx, "sysctl", "--system")
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("failed to build command")
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to apply sysctl settings: %w", err)
|
||||
if output, err = i.ExecuteCommand(ctx, "sysctl", "--system"); err != nil {
|
||||
return fmt.Errorf("failed to apply sysctl settings: %w, raw = %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppClient(ctx context.Context, replica int) error {
|
||||
func (m *maker) AppClient(ctx context.Context, version string, replica int) error {
|
||||
const (
|
||||
_config = `Version: "3"
|
||||
APNs: /yizhisec/hs_nginx/data/443/oem/data.json
|
||||
@@ -206,7 +206,8 @@ kubectl rollout restart deployment client-deployment -n hsv2`
|
||||
workdir = filepath.Join(m.workdir, "app", "client")
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.AppClient: 开始构建 client 应用..., dir = %s", workdir)
|
||||
logger.Info("☑️ maker.AppClient: 开始构建 client 应用..., version = %s, dir = %s", version, workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
logger.Debug("☑️ maker.AppClient: 开始创建工作目录 = %s", workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppClient: 创建目录失败: %v", err)
|
||||
@@ -214,6 +215,17 @@ kubectl rollout restart deployment client-deployment -n hsv2`
|
||||
}
|
||||
logger.Debug("✅ maker.AppClient: 创建工作目录成功 = %s", workdir)
|
||||
|
||||
imgName := "hub.yizhisec.com/hybridscope/client_server:latest"
|
||||
logger.Debug("☑️ maker.AppClient: start pull image = %s", imgName)
|
||||
if err = m.Image(ctx, imgName,
|
||||
WithImageForcePull(true),
|
||||
WithImageSave(filepath.Join(workdir, "client.tar")),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppClient: 拉取镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppClient: pull image success = %s", imgName)
|
||||
|
||||
if replica < 1 {
|
||||
replica = 1
|
||||
}
|
||||
@@ -240,6 +252,11 @@ kubectl rollout restart deployment client-deployment -n hsv2`
|
||||
}
|
||||
logger.Debug("✅ maker.AppClient: 构建 upsert.sh 脚本成功")
|
||||
|
||||
logger.Info("✅ maker.AppClient: 构建 client 应用成功!!!")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppClient: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.AppClient: 构建 client 应用成功!!! version = %s", version)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppFront(ctx context.Context, vendor string, replica int) error {
|
||||
func (m *maker) AppFront(ctx context.Context, version string, vendor string, replica int) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(m.workdir, "app", "front")
|
||||
@@ -19,55 +19,53 @@ func (m *maker) AppFront(ctx context.Context, vendor string, replica int) error
|
||||
_vendor = model.GetVendor(vendor)
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.Front: 开始构建 front app..., workdir = %s", location)
|
||||
logger.Info("☑️ maker.Front: 开始构建 front app[%s]..., version = %s, vendor = %s, workdir = %s", vendor, version, location)
|
||||
|
||||
if _vendor == nil {
|
||||
logger.Debug("❌ maker.Front: vendor not supported, vendor = %s", vendor)
|
||||
return fmt.Errorf("vendor not supported: %s", vendor)
|
||||
}
|
||||
|
||||
_ = os.RemoveAll(location)
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Debug("❌ maker.Front: 创建目录失败: path = %s, err = %v", location, err)
|
||||
return err
|
||||
}
|
||||
|
||||
path := filepath.Join(location, "front.user.yaml")
|
||||
logger.Debug("☑️ maker.Front: writing front.user.yaml, path = %s", path)
|
||||
path := filepath.Join(location, "deployment.user.yaml")
|
||||
logger.Debug("☑️ maker.Front: writing deployment.user.yaml, path = %s", path)
|
||||
bs = []byte(fmt.Sprintf(resource.YAMLAppFrontUser, replica))
|
||||
if err = os.WriteFile(path, bs, 0644); err != nil {
|
||||
logger.Debug("❌ maker.Front: 写入 front.user.yaml 失败: path = %s, err = %v", path, err)
|
||||
logger.Debug("❌ maker.Front: 写入 deployment.user.yaml 失败: path = %s, err = %v", path, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Front: write front.user.yaml success, path = %s", path)
|
||||
logger.Debug("✅ maker.Front: write deployment.user.yaml success, path = %s", path)
|
||||
|
||||
path = filepath.Join(location, "front.admin.yaml")
|
||||
logger.Debug("☑️ maker.Front: writing front.admin.yaml, path = %s", path)
|
||||
path = filepath.Join(location, "deployment.admin.yaml")
|
||||
logger.Debug("☑️ maker.Front: writing deployment.admin.yaml, path = %s", path)
|
||||
bs = []byte(fmt.Sprintf(resource.YAMLAppFrontAdmin, replica))
|
||||
if err = os.WriteFile(path, bs, 0644); err != nil {
|
||||
logger.Debug("❌ maker.Front: 写入 front.admin.yaml 失败: path = %s, err = %v", path, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Front: write front.admin.yaml success, path = %s", path)
|
||||
|
||||
// todo, pull front images
|
||||
// 1. make image dir
|
||||
imgDir := filepath.Join(m.workdir, "dependency", "image")
|
||||
if err = os.MkdirAll(imgDir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.Front: 创建目录失败: path = %s, err = %v", imgDir, err)
|
||||
logger.Debug("❌ maker.Front: 写入 deployment.admin.yaml 失败: path = %s, err = %v", path, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Front: write deployment.admin.yaml success, path = %s", path)
|
||||
|
||||
logger.Debug("☑️ maker.Front: pulling front images, vendor = %s", vendor)
|
||||
if err = m.Image(ctx, _vendor.AppFrontUserImageName, WithImageSave(filepath.Join(imgDir, "app.front.user.tar")), WithImageForcePull(true)); err != nil {
|
||||
if err = m.Image(ctx, _vendor.AppFrontUserImageName, WithImageSave(filepath.Join(location, "front.user.tar")), WithImageForcePull(true)); err != nil {
|
||||
logger.Debug("❌ maker.Front: 拉取 front 用户镜像失败: %s, err = %v", _vendor.AppFrontUserImageName, err)
|
||||
return err
|
||||
}
|
||||
if err = m.Image(ctx, _vendor.AppFrontAdminImageName, WithImageSave(filepath.Join(imgDir, "app.front.admin.tar")), WithImageForcePull(true)); err != nil {
|
||||
if err = m.Image(ctx, _vendor.AppFrontAdminImageName, WithImageSave(filepath.Join(location, "front.admin.tar")), WithImageForcePull(true)); err != nil {
|
||||
logger.Debug("❌ maker.Front: 拉取 front 管理镜像失败: %s, err = %v", _vendor.AppFrontAdminImageName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.Front: 构建 front app 完成")
|
||||
if err = os.WriteFile(filepath.Join(location, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.Front: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.Front: 构建 front app[%s] 完成, version = %s", vendor, version)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppGateway(ctx context.Context, replica int) error {
|
||||
func (m *maker) AppGateway(ctx context.Context, version string, replica int) error {
|
||||
const (
|
||||
_config = `Version: "3"
|
||||
Database:
|
||||
@@ -59,7 +59,8 @@ kubectl rollout restart deployment gateway-deployment -n hsv2`
|
||||
workdir = filepath.Join(m.workdir, "app", "gateway")
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.AppGateway: 开始构建 gateway 应用..., dir = %s", workdir)
|
||||
logger.Info("☑️ maker.AppGateway: 开始构建 gateway 应用..., version = %s, dir = %s", version, workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
logger.Debug("☑️ maker.AppGateway: 开始创建工作目录 = %s", workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppGateway: 创建目录失败: %v", err)
|
||||
@@ -67,6 +68,17 @@ kubectl rollout restart deployment gateway-deployment -n hsv2`
|
||||
}
|
||||
logger.Debug("✅ maker.AppGateway: 创建工作目录成功 = %s", workdir)
|
||||
|
||||
imgName := "hub.yizhisec.com/hybridscope/gateway_controller:latest"
|
||||
logger.Debug("☑️ maker.AppGateway: start pull image = %s", imgName)
|
||||
if err = m.Image(ctx, imgName,
|
||||
WithImageForcePull(true),
|
||||
WithImageSave(filepath.Join(workdir, "gateway.tar")),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppGateway: 拉取镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppGateway: pull image success = %s", imgName)
|
||||
|
||||
if replica < 1 {
|
||||
replica = 1
|
||||
}
|
||||
@@ -93,6 +105,16 @@ kubectl rollout restart deployment gateway-deployment -n hsv2`
|
||||
}
|
||||
logger.Debug("✅ maker.AppGateway: 构建 upsert.sh 脚本成功")
|
||||
|
||||
logger.Info("✅ maker.AppGateway: 构建 gateway 应用成功!!!")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppGateway: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(workdir, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppGateway: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.AppGateway: 构建 gateway 应用成功!!! version = %s", version)
|
||||
return nil
|
||||
}
|
||||
|
||||
73
internal/controller/maker/app.helper.go
Normal file
73
internal/controller/maker/app.helper.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
// AppHelper
|
||||
func (m *maker) AppHelper(ctx context.Context, version string, vendor string, replica int) error {
|
||||
const (
|
||||
// _config = ``
|
||||
_upsert = `#!/bin/bash
|
||||
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl rollout restart deployment app-helper-deployment -n hsv2`
|
||||
)
|
||||
var (
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "app", "helper")
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.AppHelper: 开始构建 helper 应用..., version = %s, vendor = %s, dir = %s", version, vendor, workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
logger.Debug("☑️ maker.AppHelper: 开始创建工作目录 = %s", workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppHelper: 创建目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppHelper: 创建工作目录成功 = %s", workdir)
|
||||
|
||||
imgName := "hub.yizhisec.com/hsv2/app/helper:latest"
|
||||
logger.Debug("☑️ maker.AppHelper: start pull image = %s", imgName)
|
||||
if err = m.Image(ctx, imgName,
|
||||
WithImageForcePull(true),
|
||||
WithImageSave(filepath.Join(workdir, "helper.tar")),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppHelper: 拉取镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppHelper: pull image success = %s", imgName)
|
||||
|
||||
if replica < 1 {
|
||||
replica = 1
|
||||
}
|
||||
|
||||
logger.Debug("☑️ maker.AppHelper: 开始构建 yaml 资源文件")
|
||||
content := []byte(fmt.Sprintf(resource.YAMLAppHelper, replica, version))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), content, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppHelper: 写入 deployment.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppHelper: 开始 yaml 资源文件成功")
|
||||
|
||||
logger.Debug("☑️ maker.AppHelper: 开始构建 upsert.sh 脚本")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppHelper: 写入 upsert.sh 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppHelper: 构建 upsert.sh 脚本成功")
|
||||
|
||||
if err = os.WriteFile(filepath.Join(workdir, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppHelper: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.AppHelper: 构建 helper 应用成功!!! version = %s", version)
|
||||
return nil
|
||||
}
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppMie(ctx context.Context, replica int) error {
|
||||
func (m *maker) AppMie(ctx context.Context, version string, replica int) error {
|
||||
const (
|
||||
_config = `Version: "3"
|
||||
BackupSeafile:
|
||||
@@ -136,7 +136,8 @@ kubectl rollout restart deployment api-deployment -n hsv2`
|
||||
workdir = filepath.Join(m.workdir, "app", "mie")
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.AppMie: 开始构建 mie ... workdir = %s", workdir)
|
||||
logger.Info("☑️ maker.AppMie: 开始构建 mie ... version = %s, workdir = %s", version, workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
logger.Debug("☑️ maker.AppMie: 开始创建工作目录 = %s", workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 创建目录失败: %v", err)
|
||||
@@ -144,6 +145,17 @@ kubectl rollout restart deployment api-deployment -n hsv2`
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: 创建工作目录成功 = %s", workdir)
|
||||
|
||||
imgName := "hub.yizhisec.com/hybridscope/mie-server:latest"
|
||||
logger.Debug("☑️ maker.AppMie: start pull image = %s", imgName)
|
||||
if err = m.Image(ctx, imgName,
|
||||
WithImageForcePull(true),
|
||||
WithImageSave(filepath.Join(workdir, "mie.tar")),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 拉取镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: pull image success = %s", imgName)
|
||||
|
||||
if replica < 1 {
|
||||
replica = 1
|
||||
}
|
||||
@@ -191,6 +203,11 @@ kubectl rollout restart deployment api-deployment -n hsv2`
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: 写入 upsert.sh 文件成功, dest = %s", filepath.Join(workdir, "upsert.sh"))
|
||||
|
||||
logger.Info("✅ maker.AppMie: 构建 mie 成功!!! workdir = %s", workdir)
|
||||
if err = os.WriteFile(filepath.Join(workdir, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.AppMie: 构建 mie 成功!!! version = %s, workdir = %s", version, workdir)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,34 +5,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/samber/lo"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
type NginxOpt func(*nginxOpt)
|
||||
|
||||
type nginxOpt struct {
|
||||
WithoutSeafile bool
|
||||
Replica int
|
||||
}
|
||||
|
||||
func WithNginxReplica(replica int) NginxOpt {
|
||||
return func(o *nginxOpt) {
|
||||
if replica >= 0 {
|
||||
o.Replica = replica
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithoutNginxSeafile() NginxOpt {
|
||||
return func(o *nginxOpt) {
|
||||
o.WithoutSeafile = true
|
||||
}
|
||||
}
|
||||
|
||||
func (m *maker) AppNginx(ctx context.Context, opts ...NginxOpt) error {
|
||||
func (m *maker) AppNginx(ctx context.Context, version string, vendor string, replica int, inlcudeImage bool) error {
|
||||
const (
|
||||
_upsert = `#!/bin/bash
|
||||
|
||||
@@ -59,31 +39,40 @@ kubectl create configmap ssl-client-server-key --namespace hsv2 --from-file=clie
|
||||
kubectl create configmap ssl-web-server-crt --namespace hsv2 --from-file=web.server.crt=./ssl/web.server.crt --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap ssl-web-server-key --namespace hsv2 --from-file=web.server.key=./ssl/web.server.key --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
kubectl apply -f nginx.yaml
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl rollout restart deployment nginx-deployment -n hsv2`
|
||||
)
|
||||
var (
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "app", "nginx")
|
||||
o = nginxOpt{
|
||||
WithoutSeafile: false,
|
||||
Replica: 2,
|
||||
}
|
||||
applySeafile = "kubectl create configmap nginx-seafile --namespace hsv2 --from-file=seafile.conf=./conf/seafile.conf --dry-run=client -o yaml | kubectl apply -f -"
|
||||
)
|
||||
|
||||
logger.Info(" ☑️ maker.AppNginx: 开始构建 nginx, workdir = %s", workdir)
|
||||
logger.Info("☑️ maker.AppNginx: 开始构建 nginx, version = %s, vendor = %s, workdir = %s", version, vendor, workdir)
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(&o)
|
||||
}
|
||||
|
||||
logger.Debug(" ☑️ maker.AppNginx: 创建工作目录 = %s", workdir)
|
||||
logger.Debug("☑️ maker.AppNginx: 创建工作目录 = %s", workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: 创建工作目录成功 = %s", workdir)
|
||||
|
||||
if inlcudeImage {
|
||||
logger.Debug("☑️ maker.AppNginx: start pull image = %s", opt.IMAGE_NGINX)
|
||||
if err = m.Image(ctx, opt.IMAGE_NGINX, WithImageSave(filepath.Join(workdir, "nginx.tar"))); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 拉取镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: pull image success = %s", opt.IMAGE_NGINX)
|
||||
|
||||
tag := strings.Split(opt.IMAGE_NGINX, ":")[1]
|
||||
logger.Debug("☑️ maker.AppNginx: write tag.txt = %s", tag)
|
||||
if err = os.WriteFile(filepath.Join(workdir, "tag.txt"), []byte(tag), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 写入 tag.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 子目录: conf 与 ssl
|
||||
confDir := filepath.Join(workdir, "conf")
|
||||
sslDir := filepath.Join(workdir, "ssl")
|
||||
@@ -113,8 +102,8 @@ kubectl rollout restart deployment nginx-deployment -n hsv2`
|
||||
{"common.conf", resource.NGINXCommon},
|
||||
}
|
||||
|
||||
// 过滤 seafile.conf 文件
|
||||
if !o.WithoutSeafile {
|
||||
// vendor != elink, append seafile conf
|
||||
if vendor != "elink" {
|
||||
confFiles = append(confFiles, struct {
|
||||
name string
|
||||
content []byte
|
||||
@@ -157,23 +146,31 @@ kubectl rollout restart deployment nginx-deployment -n hsv2`
|
||||
}
|
||||
|
||||
// write nginx deployment yaml
|
||||
dest := filepath.Join(workdir, "nginx.yaml")
|
||||
content := []byte(fmt.Sprintf(resource.YAMLAppNGINX, o.Replica))
|
||||
dest := filepath.Join(workdir, "deployment.yaml")
|
||||
content := []byte(fmt.Sprintf(resource.YAMLAppNGINX, replica))
|
||||
if err = os.WriteFile(dest, content, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 写入 nginx.yaml 失败: %v", err)
|
||||
logger.Debug("❌ maker.AppNginx: 写入 deployment.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: 写入 nginx.yaml 成功, dest = %s", dest)
|
||||
logger.Debug("✅ maker.AppNginx: 写入 deployment.yaml 成功, dest = %s", dest)
|
||||
|
||||
// write nginx upsert script
|
||||
dest = filepath.Join(workdir, "upsert.sh")
|
||||
content = []byte(fmt.Sprintf(_upsert, lo.If(o.WithoutSeafile, "").Else(applySeafile)))
|
||||
if vendor == "elink" {
|
||||
applySeafile = ""
|
||||
}
|
||||
content = []byte(fmt.Sprintf(_upsert, applySeafile))
|
||||
if err = os.WriteFile(dest, content, 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 写入 upsert.sh 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: 写入 upsert.sh 成功, dest = %s", dest)
|
||||
|
||||
logger.Info("✅ maker.AppNginx: nginx 构建完成")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.AppNginx: nginx 构建完成, version = %s, vendor = %s", version, vendor)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/archiver"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppOEM(ctx context.Context, vendor string, replica int) error {
|
||||
func (m *maker) AppOEM(ctx context.Context, version string, vendor string, replica int) error {
|
||||
const (
|
||||
_nginx = `user root;
|
||||
worker_processes auto;
|
||||
@@ -61,7 +61,7 @@ CMD ["nginx", "-g", "daemon off;"]`
|
||||
output []byte
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.AppOEM: 开始构建 oem[%s], workdir = %s", vendor, workdir)
|
||||
logger.Info("☑️ maker.AppOEM: 开始构建 oem[%s], version = %s, ,workdir = %s", vendor, version, workdir)
|
||||
|
||||
if _vendor == nil {
|
||||
supported := model.GetVendorNames()
|
||||
@@ -71,6 +71,7 @@ CMD ["nginx", "-g", "daemon off;"]`
|
||||
|
||||
// 1. make workdir
|
||||
logger.Debug("☑️ maker.AppOEM: 开始创建 workdir = %s", workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
if err = os.MkdirAll(workdir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,6 +93,7 @@ CMD ["nginx", "-g", "daemon off;"]`
|
||||
}
|
||||
}
|
||||
logger.Debug("✅ maker.AppOEM: oem[%s] tar 下载成功", vendor)
|
||||
defer os.RemoveAll(filepath.Join(workdir, "oem"))
|
||||
|
||||
// 3. write nginx.conf
|
||||
logger.Debug("☑️ maker.AppOEM: 开始写入 nginx.conf")
|
||||
@@ -127,14 +129,10 @@ CMD ["nginx", "-g", "daemon off;"]`
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppOEM: docker image 构建成功, image = %s", imageName)
|
||||
defer os.RemoveAll(filepath.Join(workdir, "Dockerfile"))
|
||||
defer os.RemoveAll(filepath.Join(workdir, "nginx.conf"))
|
||||
|
||||
// 6. save docker image to image dir
|
||||
logger.Debug("☑️ maker.AppOEM: 开始保存 docker image = %s 到 %s", imageName, filepath.Join(m.workdir, "image", imageName))
|
||||
if err = os.MkdirAll(filepath.Join(m.workdir, "dependency", "image"), 0o755); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: image 目录创建失败, err = %v", err)
|
||||
return err
|
||||
}
|
||||
if err = exec.CommandContext(ctx, "docker", "save", "-o", filepath.Join(m.workdir, "dependency", "image", "app.oem.tar"), imageName).Run(); err != nil {
|
||||
if err = exec.CommandContext(ctx, "docker", "save", "-o", filepath.Join(workdir, "oem.tar"), imageName).Run(); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: docker image 保存失败, err = %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -153,7 +151,12 @@ CMD ["nginx", "-g", "daemon off;"]`
|
||||
}
|
||||
logger.Debug("✅ maker.AppOEM: deployment.yaml 写入成功")
|
||||
|
||||
logger.Info("✅ maker.AppOEM: 开始构建 oem[%s] 成功!!!", vendor)
|
||||
if err = os.WriteFile(filepath.Join(workdir, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.AppOEM: 开始构建 oem[%s] 成功!!! version = %s", vendor, version)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppUser(ctx context.Context, replica int) error {
|
||||
func (m *maker) AppUser(ctx context.Context, version string, replica int) error {
|
||||
const (
|
||||
_config = `Version: "3"
|
||||
Database:
|
||||
@@ -87,15 +87,27 @@ kubectl rollout restart deployment user-deployment -n hsv2`
|
||||
workdir = filepath.Join(m.workdir, "app", "user")
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.AppUser: 开始构建 user 应用..., dir = %s", workdir)
|
||||
logger.Info("☑️ maker.AppUser: 开始构建 user 应用..., version = %s, dir = %s", version, workdir)
|
||||
|
||||
logger.Debug("☑️ maker.AppUser: 开始创建工作目录 = %s", workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppUser: 创建目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppUser: 创建工作目录成功 = %s", workdir)
|
||||
|
||||
imgName := "hub.yizhisec.com/hybridscope/user_management:latest"
|
||||
logger.Debug("☑️ maker.AppUser: start pull image = %s", imgName)
|
||||
if err = m.Image(ctx, imgName,
|
||||
WithImageForcePull(true),
|
||||
WithImageSave(filepath.Join(workdir, "user.tar")),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppUser: 拉取镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppUser: pull image success = %s", imgName)
|
||||
|
||||
if replica < 1 {
|
||||
replica = 1
|
||||
}
|
||||
@@ -123,6 +135,11 @@ kubectl rollout restart deployment user-deployment -n hsv2`
|
||||
}
|
||||
logger.Debug("✅ maker.AppUser: 构建 upsert.sh 脚本成功")
|
||||
|
||||
logger.Info("✅ maker.AppUser: 构建 user 应用成功!!!")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "version.txt"), []byte(version), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppUser: 写入 version.txt 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.AppUser: 构建 user 应用成功!!! version = %s", version)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/archiver"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@ package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/samber/lo"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
@@ -21,6 +22,13 @@ type ClientPKGOption func(*clientPKGOption)
|
||||
type clientPKGOption struct {
|
||||
Downloads []*clientPKGDownload
|
||||
CMDs []string
|
||||
MakePkg bool
|
||||
}
|
||||
|
||||
func WithClientPkgMakePkg(pkg bool) ClientPKGOption {
|
||||
return func(o *clientPKGOption) {
|
||||
o.MakePkg = pkg
|
||||
}
|
||||
}
|
||||
|
||||
func WithClientPKGDownload(url, location string) ClientPKGOption {
|
||||
@@ -36,22 +44,21 @@ func WithClientPKGDownload(url, location string) ClientPKGOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithClientPKGCMD(cmd string) ClientPKGOption {
|
||||
func WithClientPKGCMDs(cmds ...string) ClientPKGOption {
|
||||
return func(o *clientPKGOption) {
|
||||
if cmd == "" {
|
||||
return
|
||||
}
|
||||
o.CMDs = append(o.CMDs, cmd)
|
||||
adds := lo.Filter(cmds, func(cmd string, _ int) bool { return cmd != "" })
|
||||
o.CMDs = append(o.CMDs, adds...)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
FROM hub.yizhisec.com/hsv2/base/nginx:latest
|
||||
|
||||
RUN mkdir -p /data
|
||||
%s
|
||||
COPY version.json /data/version.json
|
||||
|
||||
%s
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
@@ -71,7 +78,6 @@ COPY nginx.conf /etc/nginx/nginx.conf
|
||||
location = filepath.Join(m.workdir, "client", _os)
|
||||
_file string
|
||||
_content string
|
||||
_cmds = ""
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
@@ -80,16 +86,31 @@ COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
logger.Info("☑️ maker.ClientPKG: start build client pkg, os = %s, version = %s, location = %s", _os, _version, location)
|
||||
|
||||
if err = os.RemoveAll(location); err != nil {
|
||||
logger.Debug("❌ maker.ClientPKG: remove directory failed, directory = %s, err = %s", location, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Debug("❌ maker.ClientPKG: create directory failed, directory = %s, err = %s", location, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
vd := map[string]string{
|
||||
"version": _version,
|
||||
}
|
||||
vbs, _ := json.Marshal(vd)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(location, "version.json"), []byte(vbs), 0644); err != nil {
|
||||
logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", filepath.Join(location, "version.json"), 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)
|
||||
@@ -100,15 +121,15 @@ COPY nginx.conf /etc/nginx/nginx.conf
|
||||
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)
|
||||
return fmt.Sprintf("wget -O /data/%s %s", d.Name, d.URL)
|
||||
})
|
||||
|
||||
if len(o.CMDs) > 0 {
|
||||
_cmds = fmt.Sprintf("RUN %s", strings.Join(o.CMDs, " && "))
|
||||
lines = append(lines, o.CMDs...)
|
||||
}
|
||||
|
||||
_file = filepath.Join(location, "Dockerfile")
|
||||
_content = fmt.Sprintf(Dockerfile, strings.Join(lines, "\n"), _cmds)
|
||||
_content = fmt.Sprintf(Dockerfile, "RUN "+strings.Join(lines, " && "))
|
||||
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())
|
||||
@@ -148,6 +169,15 @@ COPY nginx.conf /etc/nginx/nginx.conf
|
||||
return err
|
||||
}
|
||||
|
||||
if o.MakePkg {
|
||||
_cmd := fmt.Sprintf("7za a -pRrPt7Uo9WM1dkXOJmHps56T8BZY2qA4g -mhe=on client.%s.pkg *", _os)
|
||||
logger.Debug("☑️ maker.ClientPKG: start create client.%s.pkg by 7zip(7za), cmd = '%s'", _os, _cmd)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("️✅ maker.ClientPKG: build client pkg success, os = %s, version = %s, location = %s", _os, _version, location)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
func (m *maker) RunCommand(ctx context.Context, dir string, _cmds ...string) error {
|
||||
@@ -11,6 +14,8 @@ func (m *maker) RunCommand(ctx context.Context, dir string, _cmds ...string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Debug("maker.RunCommand: dir = %s, cmds = %v", dir, _cmds)
|
||||
|
||||
for _, cmdStr := range _cmds {
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", cmdStr)
|
||||
|
||||
@@ -18,9 +23,13 @@ func (m *maker) RunCommand(ctx context.Context, dir string, _cmds ...string) err
|
||||
cmd.Dir = dir
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
|
||||
// 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 fmt.Errorf("failed to run command '%s' in directory '%s': %w, output = %s", cmdStr, dir, err, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/samber/lo"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
"yizhisec.com/hsv2/forge/pkg/extractor"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/tool/random"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/archiver"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/archiver"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ After=network.target containerd.service
|
||||
|
||||
[Service]
|
||||
# 启动前清理旧容器
|
||||
# ExecStartPre=-/usr/local/bin/k0s ctr -n hs-net task kill hs-net
|
||||
ExecStartPre=-/usr/local/bin/k0s ctr -n hs-net task kill hs-net
|
||||
ExecStartPre=-/usr/local/bin/k0s ctr namespace create hs-net
|
||||
ExecStartPre=-/usr/local/bin/k0s ctr -n hs-net container rm hs-net
|
||||
|
||||
@@ -59,7 +59,7 @@ StandardError=journal
|
||||
SyslogIdentifier=hs-net
|
||||
|
||||
# 清理退出的容器
|
||||
# ExecStop=/usr/local/bin/k0s ctr -n hs-net task kill hs-net
|
||||
ExecStop=/usr/local/bin/k0s ctr -n hs-net task kill hs-net
|
||||
ExecStopPost=/usr/local/bin/k0s ctr -n hs-net container rm hs-net
|
||||
|
||||
[Install]
|
||||
@@ -166,11 +166,35 @@ tcp_mode_disable: false
|
||||
|
||||
logger.Info("☑️ MakeHSNet: 开始构建 hs-net, workdir = %s", workdir)
|
||||
|
||||
_ = os.RemoveAll(workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 创建目录失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// {Name: "", Fallback: "", Save: "app.less_dns.tar", Force: true},
|
||||
imgName := "hub.yizhisec.com/hybridscope/hsnet:release_2.1.0-std"
|
||||
logger.Debug("☑️ maker.HSNet: start pull image = %s", imgName)
|
||||
if err = m.Image(ctx, imgName,
|
||||
WithImageForcePull(true),
|
||||
WithImageSave(filepath.Join(workdir, "hsnet.tar")),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.HSNet: 拉取镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.HSNet: pull image success = %s", imgName)
|
||||
|
||||
imgName = "hub.yizhisec.com/hybridscope/less_dns_service:latest"
|
||||
logger.Debug("☑️ maker.HSNet: start pull image = %s", imgName)
|
||||
if err = m.Image(ctx, imgName,
|
||||
WithImageForcePull(true),
|
||||
WithImageSave(filepath.Join(workdir, "less-dns.tar")),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.HSNet: 拉取镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.HSNet: pull image success = %s", imgName)
|
||||
|
||||
if err = archiver.DownloadAndExtract(ctx, _url, workdir); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 下载和解压失败: %s", err.Error())
|
||||
return err
|
||||
@@ -225,15 +249,6 @@ tcp_mode_disable: false
|
||||
return err
|
||||
}
|
||||
|
||||
imgName := "hub.yizhisec.com/hybridscope/hsnet:release_2.1.0-std"
|
||||
imgPath := filepath.Join(workdir, "hs-net.tar")
|
||||
logger.Debug("☑️ MakeHSNet: 构建镜像 %s 到 %s", imgName, imgPath)
|
||||
if err = m.Image(ctx, imgName, WithImageSave(imgPath), WithImageForcePull(true)); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 构建镜像失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ MakeHSNet: 构建镜像 %s 到 %s 成功", imgName, imgPath)
|
||||
|
||||
// write hs-net.service
|
||||
if err = os.WriteFile(filepath.Join(workdir, "hs-net.service"), []byte(_service), 0644); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 写入服务文件失败: %s", err.Error())
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/samber/lo"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
)
|
||||
|
||||
@@ -112,14 +112,6 @@ 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 {
|
||||
|
||||
@@ -3,7 +3,7 @@ package maker
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
func TestImage(t *testing.T) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
@@ -15,19 +16,19 @@ import (
|
||||
func (m *maker) Minio(ctx context.Context, storage string) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(m.workdir, "dependency", "minio")
|
||||
workdir = filepath.Join(m.workdir, "dependency", "minio")
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.Minio: 开始构建 minio 依赖, workdir = %s", location)
|
||||
|
||||
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)
|
||||
logger.Info("☑️ maker.Minio: 开始构建 minio 依赖, workdir = %s", workdir)
|
||||
logger.Debug("☑️ maker.Minio: 构建工作目录, workdir = %s", workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.Minio: 创建工作目录失败, workdir = %s, err = %v", workdir, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Minio: 创建工作目录成功, workdir = %s", location)
|
||||
logger.Debug("✅ maker.Minio: 创建工作目录成功, workdir = %s", workdir)
|
||||
|
||||
filename := filepath.Join(location, "minio.yaml")
|
||||
filename := filepath.Join(workdir, "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 {
|
||||
@@ -38,13 +39,13 @@ func (m *maker) Minio(ctx context.Context, storage string) error {
|
||||
|
||||
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"},
|
||||
{Name: opt.IMAGE_MINIO_BASE, Fallback: "", Save: "minio-init.tar"},
|
||||
{Name: opt.IMAGE_MINIO, Fallback: "", Save: "minio.tar"},
|
||||
}
|
||||
for _, image := range images {
|
||||
opts := []ImageOpt{
|
||||
WithImageFallback(image.Fallback),
|
||||
WithImageSave(filepath.Join(location, image.Save)),
|
||||
WithImageSave(filepath.Join(workdir, image.Save)),
|
||||
WithImageForcePull(image.Force),
|
||||
}
|
||||
|
||||
@@ -56,7 +57,7 @@ func (m *maker) Minio(ctx context.Context, storage string) error {
|
||||
}
|
||||
logger.Debug("✅ maker.Minio: 获取所需镜像成功!!!")
|
||||
|
||||
logger.Info("✅ maker.Minio: 构建 minio 依赖成功, workdir = %s", location)
|
||||
logger.Info("✅ maker.Minio: 构建 minio 依赖成功, workdir = %s", workdir)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
@@ -2,34 +2,38 @@ package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
type YosguardOpt func(*yosguardOpt)
|
||||
|
||||
type yosguardOpt struct{}
|
||||
type yosguardOpt struct {
|
||||
Pkg bool
|
||||
}
|
||||
|
||||
func WithYosguardPkg(makePkg bool) YosguardOpt {
|
||||
return func(o *yosguardOpt) {
|
||||
o.Pkg = makePkg
|
||||
}
|
||||
}
|
||||
|
||||
func (m *maker) Yosguard(ctx context.Context, opts ...YosguardOpt) error {
|
||||
const (
|
||||
_config = `
|
||||
AsController: true
|
||||
AsGateway: false
|
||||
ControllerServer:
|
||||
Host: dasheng.zhsftech.debug
|
||||
Port: 443
|
||||
Web:
|
||||
Host: __ip__
|
||||
Port: 7788
|
||||
Database:
|
||||
SQLite:
|
||||
DBPath: /etc/yosguard/db/yosguard.db
|
||||
HeartbeatDuration: 5
|
||||
UUIDFilePath: /etc/yosguard/uuid
|
||||
Web:
|
||||
Host: __ip__
|
||||
Port: 7788
|
||||
`
|
||||
|
||||
systemdService = `
|
||||
@@ -52,10 +56,23 @@ WantedBy=multi-user.target`
|
||||
binURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/yosguard"
|
||||
)
|
||||
|
||||
location := filepath.Join(m.workdir, "dependency", "yosguard")
|
||||
var (
|
||||
location = filepath.Join(m.workdir, "dependency", "yosguard")
|
||||
o = &yosguardOpt{}
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
logger.Info("☑️ maker.Yosguard: 开始构建 yosguard...")
|
||||
logger.Debug("☑️ maker.Yosguard: 创建目录 %s", location)
|
||||
|
||||
if err := os.RemoveAll(location); err != nil {
|
||||
logger.Debug("❌ maker.Yosguard: 删除 yosguard 目录失败, dir = %s, err = %v", location, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Debug("❌ maker.Yosguard: 创建 yosguard 目录失败: %v", err)
|
||||
return err
|
||||
@@ -96,6 +113,15 @@ WantedBy=multi-user.target`
|
||||
}
|
||||
logger.Debug("✅ maker.Yosguard: 写入 yosguard.service 文件成功, dest = %s", filepath.Join(location, "yosguard.service"))
|
||||
|
||||
if o.Pkg {
|
||||
_cmd := fmt.Sprintf("cd %s && 7za a -pRrPt7Uo9WM1dkXOJmHps56T8BZY2qA4g -mhe=on yosguard.pkg *", location)
|
||||
logger.Debug("☑️ maker.Yosguard: start create yosguard.pkg by 7zip, cmd = '%s'", _cmd)
|
||||
if err := m.RunCommand(ctx, location, _cmd); err != nil {
|
||||
logger.Debug("❌ maker.Yosguard: 创建 yosguard.pkg 失败, err = %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("✅ maker.Yosguard: 构建 yosguard 成功!!!")
|
||||
return nil
|
||||
}
|
||||
|
||||
8
internal/opt/var.go
Normal file
8
internal/opt/var.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package opt
|
||||
|
||||
const (
|
||||
IMAGE_NGINX = "docker-mirror.yizhisec.com/library/nginx:1.29.4-alpine3.23"
|
||||
IMAGE_MINIO = "hub.yizhisec.com/external/minio:RELEASE.2025-03-12T18-04-18Z"
|
||||
IMAGE_ALPINE = "docker-mirror.yizhisec.com/library/alpine:3.23.2"
|
||||
IMAGE_MINIO_BASE = "hub.yizhisec.com/hsv2/base/minio-init:latest"
|
||||
)
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
// Options defines options for downloading and extracting archives
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
func TestDownloadAndExtract(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
// Options defines options for downloading files
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
// PullOption is a functional option for configuring image pull
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
// TestPullImage_PublicImage tests pulling a public image from Docker Hub
|
||||
|
||||
53
pkg/logger/ctx.go
Normal file
53
pkg/logger/ctx.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
uuid2 "github.com/google/uuid"
|
||||
)
|
||||
|
||||
type _traceId struct{}
|
||||
|
||||
var TraceId = _traceId{}
|
||||
|
||||
func traceId(ctx context.Context) string {
|
||||
if ctx == nil {
|
||||
uuid, _ := uuid2.NewV7()
|
||||
return uuid.String()
|
||||
}
|
||||
|
||||
if id, _ := ctx.Value(TraceId).(string); id != "" {
|
||||
return id
|
||||
}
|
||||
|
||||
uuid, _ := uuid2.NewV7()
|
||||
return uuid.String()
|
||||
}
|
||||
|
||||
func DebugCtx(ctx context.Context, msg string, data ...any) {
|
||||
msg = traceId(ctx) + " | " + msg
|
||||
DefaultLogger.Debug(msg, data...)
|
||||
}
|
||||
func InfoCtx(ctx context.Context, msg string, data ...any) {
|
||||
msg = traceId(ctx) + " | " + msg
|
||||
DefaultLogger.Info(msg, data...)
|
||||
}
|
||||
|
||||
func WarnCtx(ctx context.Context, msg string, data ...any) {
|
||||
msg = traceId(ctx) + " | " + msg
|
||||
DefaultLogger.Warn(msg, data...)
|
||||
}
|
||||
|
||||
func ErrorCtx(ctx context.Context, msg string, data ...any) {
|
||||
msg = traceId(ctx) + " | " + msg
|
||||
DefaultLogger.Error(msg, data...)
|
||||
}
|
||||
|
||||
func PanicCtx(ctx context.Context, msg string, data ...any) {
|
||||
msg = traceId(ctx) + " | " + msg
|
||||
DefaultLogger.Panic(msg, data...)
|
||||
}
|
||||
|
||||
func FatalCtx(ctx context.Context, msg string, data ...any) {
|
||||
msg = traceId(ctx) + " | " + msg
|
||||
DefaultLogger.Fatal(msg, data...)
|
||||
}
|
||||
17
pkg/logger/ctx_test.go
Normal file
17
pkg/logger/ctx_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCtxLog(t *testing.T) {
|
||||
DebugCtx(nil, "hello %s", "world")
|
||||
InfoCtx(nil, "hello %s", "world")
|
||||
WarnCtx(context.Background(), "hello %s", "world")
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, TraceId, "value")
|
||||
SetLogLevel(LogLevelDebug)
|
||||
DebugCtx(ctx, "hello %s", "world")
|
||||
ErrorCtx(ctx, "hello %s", "world")
|
||||
}
|
||||
67
pkg/logger/default.go
Normal file
67
pkg/logger/default.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
nilLogger = func(prefix, timestamp, msg string, data ...any) {}
|
||||
normalLogger = func(prefix, timestamp, msg string, data ...any) {
|
||||
fmt.Printf(prefix+"| "+timestamp+" | "+msg+"\n", data...)
|
||||
}
|
||||
|
||||
panicLogger = func(prefix, timestamp, msg string, data ...any) {
|
||||
panic(fmt.Sprintf(prefix+"| "+timestamp+" | "+msg+"\n", data...))
|
||||
}
|
||||
|
||||
fatalLogger = func(prefix, timestamp, msg string, data ...any) {
|
||||
fmt.Printf(prefix+"| "+timestamp+" | "+msg+"\n", data...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
DefaultLogger = &logger{
|
||||
Mutex: sync.Mutex{},
|
||||
timeFormat: "2006-01-02T15:04:05",
|
||||
writer: os.Stdout,
|
||||
level: LogLevelInfo,
|
||||
debug: nilLogger,
|
||||
info: normalLogger,
|
||||
warn: normalLogger,
|
||||
error: normalLogger,
|
||||
panic: panicLogger,
|
||||
fatal: fatalLogger,
|
||||
}
|
||||
)
|
||||
|
||||
func SetTimeFormat(format string) {
|
||||
DefaultLogger.SetTimeFormat(format)
|
||||
}
|
||||
|
||||
func SetLogLevel(level LogLevel) {
|
||||
DefaultLogger.SetLogLevel(level)
|
||||
}
|
||||
|
||||
func Debug(msg string, data ...any) {
|
||||
DefaultLogger.Debug(msg, data...)
|
||||
}
|
||||
func Info(msg string, data ...any) {
|
||||
DefaultLogger.Info(msg, data...)
|
||||
}
|
||||
|
||||
func Warn(msg string, data ...any) {
|
||||
DefaultLogger.Warn(msg, data...)
|
||||
}
|
||||
|
||||
func Error(msg string, data ...any) {
|
||||
DefaultLogger.Error(msg, data...)
|
||||
}
|
||||
|
||||
func Panic(msg string, data ...any) {
|
||||
DefaultLogger.Panic(msg, data...)
|
||||
}
|
||||
|
||||
func Fatal(msg string, data ...any) {
|
||||
DefaultLogger.Fatal(msg, data...)
|
||||
}
|
||||
115
pkg/logger/logger.go
Normal file
115
pkg/logger/logger.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"github.com/fatih/color"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LogLevel uint32
|
||||
|
||||
const (
|
||||
LogLevelDebug = iota
|
||||
LogLevelInfo
|
||||
LogLevelWarn
|
||||
LogLevelError
|
||||
LogLevelPanic
|
||||
LogLevelFatal
|
||||
)
|
||||
|
||||
type logger struct {
|
||||
sync.Mutex
|
||||
timeFormat string
|
||||
writer io.Writer
|
||||
level LogLevel
|
||||
debug func(prefix, timestamp, msg string, data ...any)
|
||||
info func(prefix, timestamp, msg string, data ...any)
|
||||
warn func(prefix, timestamp, msg string, data ...any)
|
||||
error func(prefix, timestamp, msg string, data ...any)
|
||||
panic func(prefix, timestamp, msg string, data ...any)
|
||||
fatal func(prefix, timestamp, msg string, data ...any)
|
||||
}
|
||||
|
||||
var (
|
||||
red = color.New(color.FgRed)
|
||||
hired = color.New(color.FgHiRed)
|
||||
green = color.New(color.FgGreen)
|
||||
yellow = color.New(color.FgYellow)
|
||||
white = color.New(color.FgWhite)
|
||||
)
|
||||
|
||||
func (l *logger) SetTimeFormat(format string) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
l.timeFormat = format
|
||||
}
|
||||
|
||||
func (l *logger) SetLogLevel(level LogLevel) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
if level > LogLevelDebug {
|
||||
l.debug = nilLogger
|
||||
} else {
|
||||
l.debug = normalLogger
|
||||
}
|
||||
|
||||
if level > LogLevelInfo {
|
||||
l.info = nilLogger
|
||||
} else {
|
||||
l.info = normalLogger
|
||||
}
|
||||
|
||||
if level > LogLevelWarn {
|
||||
l.warn = nilLogger
|
||||
} else {
|
||||
l.warn = normalLogger
|
||||
}
|
||||
|
||||
if level > LogLevelError {
|
||||
l.error = nilLogger
|
||||
} else {
|
||||
l.error = normalLogger
|
||||
}
|
||||
|
||||
if level > LogLevelPanic {
|
||||
l.panic = nilLogger
|
||||
} else {
|
||||
l.panic = panicLogger
|
||||
}
|
||||
|
||||
if level > LogLevelFatal {
|
||||
l.fatal = nilLogger
|
||||
} else {
|
||||
l.fatal = fatalLogger
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) Debug(msg string, data ...any) {
|
||||
l.debug(white.Sprint("Debug "), time.Now().Format(l.timeFormat), msg, data...)
|
||||
}
|
||||
|
||||
func (l *logger) Info(msg string, data ...any) {
|
||||
l.info(green.Sprint("Info "), time.Now().Format(l.timeFormat), msg, data...)
|
||||
}
|
||||
|
||||
func (l *logger) Warn(msg string, data ...any) {
|
||||
l.warn(yellow.Sprint("Warn "), time.Now().Format(l.timeFormat), msg, data...)
|
||||
}
|
||||
|
||||
func (l *logger) Error(msg string, data ...any) {
|
||||
l.error(red.Sprint("Error "), time.Now().Format(l.timeFormat), msg, data...)
|
||||
}
|
||||
|
||||
func (l *logger) Panic(msg string, data ...any) {
|
||||
l.panic(hired.Sprint("Panic "), time.Now().Format(l.timeFormat), msg, data...)
|
||||
}
|
||||
|
||||
func (l *logger) Fatal(msg string, data ...any) {
|
||||
l.fatal(hired.Sprint("Fatal "), time.Now().Format(l.timeFormat), msg, data...)
|
||||
}
|
||||
|
||||
type WroteLogger interface {
|
||||
Info(msg string, data ...any)
|
||||
}
|
||||
21
pkg/logger/new.go
Normal file
21
pkg/logger/new.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func New() *logger {
|
||||
return &logger{
|
||||
Mutex: sync.Mutex{},
|
||||
timeFormat: "2006-01-02T15:04:05",
|
||||
writer: os.Stdout,
|
||||
level: LogLevelInfo,
|
||||
debug: nilLogger,
|
||||
info: normalLogger,
|
||||
warn: normalLogger,
|
||||
error: normalLogger,
|
||||
panic: panicLogger,
|
||||
fatal: fatalLogger,
|
||||
}
|
||||
}
|
||||
@@ -10,28 +10,28 @@ type Vendor struct {
|
||||
|
||||
var (
|
||||
vendorMap = map[string]*Vendor{
|
||||
"standard": &Vendor{
|
||||
Name: "Standard",
|
||||
"standard": {
|
||||
Name: "standard",
|
||||
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem.tar.gz",
|
||||
OEMDir: "oem",
|
||||
AppFrontUserImageName: "hub.yizhisec.com/hybridscope/v2/front-user:latest",
|
||||
AppFrontAdminImageName: "hub.yizhisec.com/build/hybirdscope/front/admin:latest",
|
||||
},
|
||||
"elink": &Vendor{
|
||||
"elink": {
|
||||
Name: "elink",
|
||||
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_csgElink.tar.gz",
|
||||
OEMDir: "oem_csgElink",
|
||||
AppFrontUserImageName: "hub.yizhisec.com/hybridscope/v2/front-user-elink:latest",
|
||||
AppFrontAdminImageName: "hub.yizhisec.com/build/hybirdscope/front/admin:latest",
|
||||
},
|
||||
"noah": &Vendor{
|
||||
"noah": {
|
||||
Name: "noah",
|
||||
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_noah.tar.gz",
|
||||
OEMDir: "oem_noah",
|
||||
AppFrontUserImageName: "hub.yizhisec.com/hybridscope/v2/front-user:latest",
|
||||
AppFrontAdminImageName: "hub.yizhisec.com/build/hybirdscope/front/admin:latest",
|
||||
},
|
||||
"heishuimeng": &Vendor{
|
||||
"heishuimeng": {
|
||||
Name: "heishuimeng",
|
||||
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_heishuimeng.tar.gz",
|
||||
OEMDir: "oem_heishuimeng",
|
||||
|
||||
@@ -33,16 +33,16 @@ server {
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /api/v2_2 {
|
||||
proxy_pass http://u-api-service;
|
||||
}
|
||||
|
||||
location /api/v1/pkg/archive {
|
||||
proxy_pass http://u-api-service/api/v2_2/client/download/check;
|
||||
proxy_pass http://client-win-service.hsv2/api/v2_2/_client/win/check.json;
|
||||
}
|
||||
|
||||
location /api/v1/version {
|
||||
proxy_pass http://u-api-service/api/v2_2/client/version;
|
||||
location /api/v1/pkg/config/setup {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/_client/win/config/setup.json;
|
||||
}
|
||||
|
||||
location /api/v2_2/_client/win {
|
||||
proxy_pass http://app-helper-service.hsv2;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
@@ -148,24 +148,25 @@ server {
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /api/v2_2 {
|
||||
proxy_pass http://u-api-service;
|
||||
}
|
||||
|
||||
location /api/v1/pkg/archive {
|
||||
proxy_pass http://u-api-service/api/v2_2/client/download/check;
|
||||
location /api/v1/pkg/config/setup {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/_client/win/config/setup.json;
|
||||
}
|
||||
|
||||
location /api/v1/pkg/archive/version {
|
||||
proxy_pass http://u-api-service/api/v2_2/client/version;
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/_client/win/old/version.json;
|
||||
}
|
||||
|
||||
location /api/v1/version {
|
||||
proxy_pass http://u-api-service/api/v2_2/client/version;
|
||||
location /api/v1/pkg/archive {
|
||||
proxy_pass http://client-win-service.hsv2/api/v2_2/_client/win/check.json;
|
||||
}
|
||||
|
||||
|
||||
location /static/config/rc.json {
|
||||
proxy_pass http://u-api-service/api/v2_2/client/rc/json?os=win;
|
||||
proxy_pass http://client-win-service.hsv2/api/v2_2/_client/win/rc.json;
|
||||
}
|
||||
|
||||
location /api/v2_2/_client/win {
|
||||
proxy_pass http://app-helper-service.hsv2;
|
||||
}
|
||||
|
||||
# location = /api/v1/version {
|
||||
|
||||
@@ -47,15 +47,6 @@ location /client/dl/ {
|
||||
}
|
||||
}
|
||||
|
||||
location /user/avatar/ {
|
||||
auth_request /token_auth;
|
||||
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
alias /static/avatar/;
|
||||
expires 7d;
|
||||
add_header Cache-Control public;
|
||||
}
|
||||
|
||||
location /file/share/ {
|
||||
auth_request /token_auth;
|
||||
|
||||
|
||||
@@ -32,19 +32,27 @@ server {
|
||||
}
|
||||
|
||||
location /api/my/sys/client/installer {
|
||||
proxy_pass http://u-api-service/api/v2_2/client/download/list;
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/client/download/list;
|
||||
}
|
||||
|
||||
location /api/system/version {
|
||||
proxy_pass http://u-api-service/api/v2_2/system/version;
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/system/version;
|
||||
}
|
||||
|
||||
location /api/v2_2/client {
|
||||
proxy_pass http://u-api-service;
|
||||
location /api/v2_2/system/elink {
|
||||
proxy_pass http://app-helper-service.hsv2;
|
||||
}
|
||||
|
||||
location /api/v2_2/system {
|
||||
proxy_pass http://u-api-service;
|
||||
location /api/v2_2/_client/win {
|
||||
proxy_pass http://app-helper-service.hsv2;
|
||||
}
|
||||
|
||||
location /api/v2_2/_client/mac{
|
||||
proxy_pass http://client-mac-service;
|
||||
}
|
||||
|
||||
location /api/v2_2/_client/linux{
|
||||
proxy_pass http://client-linux-service;
|
||||
}
|
||||
|
||||
location / {
|
||||
@@ -70,23 +78,48 @@ server {
|
||||
|
||||
server_tokens off;
|
||||
|
||||
location / {
|
||||
proxy_pass http://front-admin-service;
|
||||
}
|
||||
|
||||
location /api/system/version {
|
||||
proxy_pass http://u-api-service/api/v2_2/system/version;
|
||||
proxy_pass http://app-helper-service/api/v2_2/system/version;
|
||||
}
|
||||
|
||||
location /oem {
|
||||
proxy_pass http://oem-service;
|
||||
}
|
||||
|
||||
location /api/v2_2/system/elink {
|
||||
proxy_pass http://app-helper-service.hsv2;
|
||||
}
|
||||
|
||||
# location /wm/ {
|
||||
# alias /data/wm/;
|
||||
# expires 30d;
|
||||
# add_header Cache-Control public;
|
||||
# }
|
||||
location /user/avatar/ {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/_obj/;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://front-admin-service;
|
||||
location /api/account/profile/avatar {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/user/profile/avatar/update;
|
||||
}
|
||||
|
||||
# create layer4 resource
|
||||
location = /api/admin/business-center/network-app/tunnel {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/interceptor/mie/resource4/create/icon$request_uri;
|
||||
}
|
||||
|
||||
# update layer4 resource
|
||||
location ~ ^/api/admin/business-center/network-app/tunnel/\d+/info$ {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/interceptor/mie/resource4/update/icon$request_uri;
|
||||
}
|
||||
|
||||
# create layer7 resource
|
||||
location = /api/admin/strategy/osi-resource {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/interceptor/mie/resource7/create/icon$request_uri;
|
||||
}
|
||||
|
||||
# update layer7 resource
|
||||
location ~ ^/api/admin/strategy/osi-resource/\d+/info$ {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/interceptor/mie/resource7/update/icon$request_uri;
|
||||
}
|
||||
|
||||
location /api/v2_1/user {
|
||||
@@ -97,6 +130,22 @@ server {
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
location /api/v2_2/_client/win {
|
||||
proxy_pass http://app-helper-service.hsv2;
|
||||
}
|
||||
|
||||
location /api/v2_2/_client/mac{
|
||||
proxy_pass http://client-mac-service;
|
||||
}
|
||||
|
||||
location /api/v2_2/_client/linux{
|
||||
proxy_pass http://client-linux-service;
|
||||
}
|
||||
|
||||
location /api/v2_2/yosguard {
|
||||
proxy_pass http://10.118.2.10:7788;
|
||||
}
|
||||
|
||||
include /etc/nginx/common/common.conf;
|
||||
|
||||
location /ws {
|
||||
@@ -107,28 +156,11 @@ server {
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
# location /backup {
|
||||
# proxy_pass http://hs-backup-server;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_read_timeout 300s;
|
||||
# }
|
||||
|
||||
location /api/local/user/import/template {
|
||||
auth_request /token_auth;
|
||||
alias /static/resource/local_user_import_template.xlsx;
|
||||
}
|
||||
|
||||
# location /wm/api {
|
||||
# proxy_pass http://hs-watermark-server;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $proxy_protocol_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_read_timeout 300s;
|
||||
# }
|
||||
|
||||
location @my_401 {
|
||||
default_type text/html;
|
||||
return 401 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>401</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>401 Unauthorized</h1>';
|
||||
|
||||
@@ -71,6 +71,9 @@ var (
|
||||
//go:embed yaml/app.nginx.yaml
|
||||
YAMLAppNGINX string
|
||||
|
||||
//go:embed yaml/app.helper.yaml
|
||||
YAMLAppHelper string
|
||||
|
||||
//go:embed yaml/client.pkg.yaml
|
||||
YAMLClientPKG string
|
||||
|
||||
|
||||
87
pkg/resource/yaml/app.helper.yaml
Normal file
87
pkg/resource/yaml/app.helper.yaml
Normal file
@@ -0,0 +1,87 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: app-helper-sa
|
||||
namespace: hsv2
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: app-helper-role
|
||||
namespace: hsv2
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments", "deployments/status"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: app-helper-rolebinding
|
||||
namespace: hsv2
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: app-helper-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: app-helper-sa
|
||||
namespace: hsv2
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: app-helper-deployment
|
||||
namespace: hsv2
|
||||
spec:
|
||||
replicas: %d
|
||||
selector:
|
||||
matchLabels:
|
||||
app: app-helper
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: app-helper
|
||||
spec:
|
||||
serviceAccountName: app-helper-sa
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app: app-helper
|
||||
containers:
|
||||
- name: app-helper
|
||||
image: hub.yizhisec.com/hsv2/app/helper:%s
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- app_helper
|
||||
- --debug
|
||||
- --redis-host
|
||||
- "redis-master.db-redis"
|
||||
- --redis-password
|
||||
- "HybridScope0xRed1s."
|
||||
ports:
|
||||
- containerPort: 80
|
||||
volumeMounts:
|
||||
- name: config-version
|
||||
mountPath: /app/version
|
||||
volumes:
|
||||
- name: config-version
|
||||
configMap:
|
||||
name: config-version
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: app-helper-service
|
||||
namespace: hsv2
|
||||
spec:
|
||||
selector:
|
||||
app: app-helper
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
type: ClusterIP
|
||||
74
pkg/syscheck/cpu.go
Normal file
74
pkg/syscheck/cpu.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package syscheck
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
type CPUInfo struct {
|
||||
Cores int64
|
||||
FrequencyMHz int64
|
||||
SupportAES bool
|
||||
IsX86V2 bool
|
||||
}
|
||||
|
||||
func GetCPUInfo(ctx context.Context) (CPUInfo, error) {
|
||||
info := CPUInfo{
|
||||
Cores: int64(runtime.NumCPU()),
|
||||
}
|
||||
|
||||
// Parse /proc/cpuinfo to get CPU frequency and model info
|
||||
file, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to open /proc/cpuinfo: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// Parse CPU MHz
|
||||
if strings.HasPrefix(line, "cpu MHz") {
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) == 2 {
|
||||
freqStr := strings.TrimSpace(parts[1])
|
||||
if freq, err := strconv.ParseFloat(freqStr, 64); err == nil {
|
||||
info.FrequencyMHz = int64(freq)
|
||||
break // Get first CPU frequency
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return info, fmt.Errorf("failed to read /proc/cpuinfo: %w", err)
|
||||
}
|
||||
|
||||
// Check CPU features using x/sys/cpu package
|
||||
if runtime.GOARCH == "amd64" || runtime.GOARCH == "386" {
|
||||
// Check AES-NI support
|
||||
info.SupportAES = cpu.X86.HasAES
|
||||
|
||||
// Check x86-64-v2 support
|
||||
// x86-64-v2 requires: SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT
|
||||
info.IsX86V2 = cpu.X86.HasSSE3 &&
|
||||
cpu.X86.HasSSSE3 &&
|
||||
cpu.X86.HasSSE41 &&
|
||||
cpu.X86.HasSSE42 &&
|
||||
cpu.X86.HasPOPCNT
|
||||
} else {
|
||||
// For ARM or other architectures
|
||||
info.SupportAES = false
|
||||
info.IsX86V2 = false
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
44
pkg/syscheck/cpu_test.go
Normal file
44
pkg/syscheck/cpu_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package syscheck
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetCPUInfo(t *testing.T) {
|
||||
info, err := GetCPUInfo(t.Context())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get CPU info: %v", err)
|
||||
}
|
||||
|
||||
// Validate CPU cores
|
||||
if info.Cores <= 0 {
|
||||
t.Errorf("expected CPU cores > 0, got %d", info.Cores)
|
||||
}
|
||||
|
||||
// Validate CPU frequency (should be reasonable, e.g., 500MHz - 10000MHz)
|
||||
if info.FrequencyMHz <= 0 {
|
||||
t.Errorf("expected CPU frequency > 0, got %.2f", info.FrequencyMHz)
|
||||
}
|
||||
if info.FrequencyMHz < 500 || info.FrequencyMHz > 10000 {
|
||||
t.Logf("Warning: CPU frequency seems unusual: %.2f MHz", info.FrequencyMHz)
|
||||
}
|
||||
|
||||
// Log CPU information
|
||||
t.Logf("CPU Cores: %d", info.Cores)
|
||||
t.Logf("CPU Frequency: %.2f MHz (%.2f GHz)", info.FrequencyMHz, info.FrequencyMHz/1000)
|
||||
t.Logf("AES-NI Support: %v", info.SupportAES)
|
||||
t.Logf("x86-64-v2 Compatible: %v", info.IsX86V2)
|
||||
|
||||
// Log feature support
|
||||
if info.SupportAES {
|
||||
t.Log("✓ CPU supports AES-NI hardware acceleration")
|
||||
} else {
|
||||
t.Log("✗ CPU does not support AES-NI")
|
||||
}
|
||||
|
||||
if info.IsX86V2 {
|
||||
t.Log("✓ CPU is x86-64-v2 compatible (SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT)")
|
||||
} else {
|
||||
t.Log("✗ CPU is not x86-64-v2 compatible")
|
||||
}
|
||||
}
|
||||
157
pkg/syscheck/disk.go
Normal file
157
pkg/syscheck/disk.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package syscheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// GetDiskSpace returns available disk space in bytes for the root partition
|
||||
func GetDiskSpace(ctx context.Context) (int64, error) {
|
||||
var stat unix.Statfs_t
|
||||
if err := unix.Statfs("/", &stat); err != nil {
|
||||
return 0, fmt.Errorf("failed to get disk space: %w", err)
|
||||
}
|
||||
|
||||
// Available space = Available blocks * Block size
|
||||
availableSpace := int64(stat.Bavail) * int64(stat.Bsize)
|
||||
return availableSpace, nil
|
||||
}
|
||||
|
||||
// GetDiskSpeed measures disk read/write speed by writing and reading a 1GB test file
|
||||
// Returns read speed and write speed in bytes per second
|
||||
func GetDiskSpeed(ctx context.Context) (int64, int64, error) {
|
||||
const (
|
||||
testSize = 1024 * 1024 * 1024 // 1GB
|
||||
bufferSize = 1024 * 1024 // 1MB buffer
|
||||
)
|
||||
|
||||
tmpFile := "/tmp/diskspeed_test_" + strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
defer func() {
|
||||
// Clean up test file
|
||||
_ = os.Remove(tmpFile)
|
||||
}()
|
||||
|
||||
// Test write speed
|
||||
writeSpeed, err := measureWriteSpeed(ctx, tmpFile, testSize, bufferSize)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("failed to measure write speed: %w", err)
|
||||
}
|
||||
|
||||
// Test read speed
|
||||
readSpeed, err := measureReadSpeed(ctx, tmpFile, bufferSize)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("failed to measure read speed: %w", err)
|
||||
}
|
||||
|
||||
return readSpeed, writeSpeed, nil
|
||||
}
|
||||
|
||||
// measureWriteSpeed writes test data to a file and measures the speed
|
||||
func measureWriteSpeed(ctx context.Context, filename string, totalSize, bufferSize int64) (int64, error) {
|
||||
// Create file
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to create test file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Prepare buffer with random data
|
||||
buffer := make([]byte, bufferSize)
|
||||
if _, err := rand.Read(buffer); err != nil {
|
||||
return 0, fmt.Errorf("failed to generate random data: %w", err)
|
||||
}
|
||||
|
||||
// Start timing
|
||||
startTime := time.Now()
|
||||
var written int64
|
||||
|
||||
// Write data in chunks
|
||||
for written < totalSize {
|
||||
// Check context cancellation
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
// Write buffer
|
||||
n, err := file.Write(buffer)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to write data: %w", err)
|
||||
}
|
||||
written += int64(n)
|
||||
}
|
||||
|
||||
// Sync to ensure data is written to disk
|
||||
if err := file.Sync(); err != nil {
|
||||
return 0, fmt.Errorf("failed to sync file: %w", err)
|
||||
}
|
||||
|
||||
// Calculate speed
|
||||
duration := time.Since(startTime)
|
||||
speed := int64(float64(written) / duration.Seconds())
|
||||
|
||||
return speed, nil
|
||||
}
|
||||
|
||||
// measureReadSpeed reads the test file and measures the speed
|
||||
func measureReadSpeed(ctx context.Context, filename string, bufferSize int64) (int64, error) {
|
||||
// Open file
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to open test file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Get file size
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to stat file: %w", err)
|
||||
}
|
||||
totalSize := fileInfo.Size()
|
||||
|
||||
// Prepare buffer
|
||||
buffer := make([]byte, bufferSize)
|
||||
|
||||
// Start timing
|
||||
startTime := time.Now()
|
||||
var totalRead int64
|
||||
|
||||
// Read data in chunks
|
||||
for {
|
||||
// Check context cancellation
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
// Read buffer
|
||||
n, err := file.Read(buffer)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to read data: %w", err)
|
||||
}
|
||||
totalRead += int64(n)
|
||||
}
|
||||
|
||||
// Calculate speed
|
||||
duration := time.Since(startTime)
|
||||
speed := int64(float64(totalRead) / duration.Seconds())
|
||||
|
||||
// Verify we read the entire file
|
||||
if totalRead != totalSize {
|
||||
return 0, fmt.Errorf("read size mismatch: expected %d, got %d", totalSize, totalRead)
|
||||
}
|
||||
|
||||
return speed, nil
|
||||
}
|
||||
42
pkg/syscheck/disk_test.go
Normal file
42
pkg/syscheck/disk_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package syscheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetDiskSpace(t *testing.T) {
|
||||
size, err := GetDiskSpace(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get disk space: %v", err)
|
||||
}
|
||||
|
||||
if size <= 0 {
|
||||
t.Errorf("expected disk space to be greater than 0, got %d", size)
|
||||
}
|
||||
|
||||
t.Logf("Available disk space: %d bytes (%.2f GB)", size, float64(size)/(1024*1024*1024))
|
||||
}
|
||||
|
||||
func TestGetDiskSpeed(t *testing.T) {
|
||||
// Test with real disk I/O (warning: this writes 1GB to disk)
|
||||
// Skip in short mode
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping disk speed test in short mode")
|
||||
}
|
||||
|
||||
rs, ws, err := GetDiskSpeed(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get disk speed: %v", err)
|
||||
}
|
||||
|
||||
if rs <= 0 {
|
||||
t.Errorf("expected read speed > 0, got %d", rs)
|
||||
}
|
||||
if ws <= 0 {
|
||||
t.Errorf("expected write speed > 0, got %d", ws)
|
||||
}
|
||||
|
||||
t.Logf("Read speed: %d bytes/s (%.2f MB/s)", rs, float64(rs)/(1024*1024))
|
||||
t.Logf("Write speed: %d bytes/s (%.2f MB/s)", ws, float64(ws)/(1024*1024))
|
||||
}
|
||||
144
pkg/syscheck/mem.go
Normal file
144
pkg/syscheck/mem.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package syscheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// GetMemorySpace returns total physical memory (RAM) in bytes, excluding swap
|
||||
func GetMemorySpace(ctx context.Context) (int64, error) {
|
||||
var info unix.Sysinfo_t
|
||||
if err := unix.Sysinfo(&info); err != nil {
|
||||
return 0, fmt.Errorf("failed to get memory info: %w", err)
|
||||
}
|
||||
|
||||
// Total physical RAM (excluding swap)
|
||||
// info.Totalram is in memory unit size (info.Unit)
|
||||
totalMemory := int64(info.Totalram) * int64(info.Unit)
|
||||
return totalMemory, nil
|
||||
}
|
||||
|
||||
// GetMemorySpeed measures memory read/write speed by allocating and accessing memory
|
||||
// Returns read speed and write speed in bytes per second
|
||||
func GetMemorySpeed(ctx context.Context) (int64, int64, error) {
|
||||
const (
|
||||
testSize = 512 * 1024 * 1024 // 512MB test size
|
||||
iterations = 5 // Number of iterations for averaging
|
||||
)
|
||||
|
||||
// Test write speed
|
||||
writeSpeed, err := measureMemoryWriteSpeed(ctx, testSize, iterations)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("failed to measure memory write speed: %w", err)
|
||||
}
|
||||
|
||||
// Test read speed
|
||||
readSpeed, err := measureMemoryReadSpeed(ctx, testSize, iterations)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("failed to measure memory read speed: %w", err)
|
||||
}
|
||||
|
||||
return readSpeed, writeSpeed, nil
|
||||
}
|
||||
|
||||
// measureMemoryWriteSpeed measures memory write speed
|
||||
func measureMemoryWriteSpeed(ctx context.Context, size int64, iterations int) (int64, error) {
|
||||
var totalDuration time.Duration
|
||||
|
||||
for i := 0; i < iterations; i++ {
|
||||
// Check context cancellation
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
// Allocate memory buffer
|
||||
buffer := make([]byte, size)
|
||||
|
||||
// Generate random data
|
||||
source := make([]byte, 1024*1024) // 1MB source buffer
|
||||
if _, err := rand.Read(source); err != nil {
|
||||
return 0, fmt.Errorf("failed to generate random data: %w", err)
|
||||
}
|
||||
|
||||
// Start timing
|
||||
startTime := time.Now()
|
||||
|
||||
// Write data to memory buffer
|
||||
for offset := int64(0); offset < size; offset += int64(len(source)) {
|
||||
remaining := size - offset
|
||||
if remaining < int64(len(source)) {
|
||||
copy(buffer[offset:], source[:remaining])
|
||||
} else {
|
||||
copy(buffer[offset:offset+int64(len(source))], source)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop timing
|
||||
duration := time.Since(startTime)
|
||||
totalDuration += duration
|
||||
|
||||
// Force the buffer to be used to prevent optimization
|
||||
_ = buffer[0]
|
||||
}
|
||||
|
||||
// Calculate average speed
|
||||
avgDuration := totalDuration / time.Duration(iterations)
|
||||
speed := int64(float64(size) / avgDuration.Seconds())
|
||||
|
||||
return speed, nil
|
||||
}
|
||||
|
||||
// measureMemoryReadSpeed measures memory read speed
|
||||
func measureMemoryReadSpeed(ctx context.Context, size int64, iterations int) (int64, error) {
|
||||
var totalDuration time.Duration
|
||||
|
||||
// Pre-allocate and fill buffer
|
||||
buffer := make([]byte, size)
|
||||
if _, err := rand.Read(buffer); err != nil {
|
||||
return 0, fmt.Errorf("failed to initialize buffer: %w", err)
|
||||
}
|
||||
|
||||
for i := 0; i < iterations; i++ {
|
||||
// Check context cancellation
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
// Start timing
|
||||
startTime := time.Now()
|
||||
|
||||
// Read data from memory buffer
|
||||
var sum int64
|
||||
for offset := int64(0); offset < size; offset += 1024 {
|
||||
// Read in chunks to simulate real access patterns
|
||||
end := offset + 1024
|
||||
if end > size {
|
||||
end = size
|
||||
}
|
||||
for j := offset; j < end; j++ {
|
||||
sum += int64(buffer[j])
|
||||
}
|
||||
}
|
||||
|
||||
// Stop timing
|
||||
duration := time.Since(startTime)
|
||||
totalDuration += duration
|
||||
|
||||
// Use sum to prevent optimization
|
||||
_ = sum
|
||||
}
|
||||
|
||||
// Calculate average speed
|
||||
avgDuration := totalDuration / time.Duration(iterations)
|
||||
speed := int64(float64(size) / avgDuration.Seconds())
|
||||
|
||||
return speed, nil
|
||||
}
|
||||
42
pkg/syscheck/mem_test.go
Normal file
42
pkg/syscheck/mem_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package syscheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetMemorySpace(t *testing.T) {
|
||||
size, err := GetMemorySpace(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get memory space: %v", err)
|
||||
}
|
||||
|
||||
if size <= 0 {
|
||||
t.Errorf("expected memory space to be greater than 0, got %d", size)
|
||||
}
|
||||
|
||||
t.Logf("Total physical memory: %d bytes (%.2f GB)", size, float64(size)/(1024*1024*1024))
|
||||
}
|
||||
|
||||
func TestGetMemorySpeed(t *testing.T) {
|
||||
// Test memory speed (may take a few seconds)
|
||||
// Skip in short mode
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping memory speed test in short mode")
|
||||
}
|
||||
|
||||
rs, ws, err := GetMemorySpeed(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get memory speed: %v", err)
|
||||
}
|
||||
|
||||
if rs <= 0 {
|
||||
t.Errorf("expected read speed > 0, got %d", rs)
|
||||
}
|
||||
if ws <= 0 {
|
||||
t.Errorf("expected write speed > 0, got %d", ws)
|
||||
}
|
||||
|
||||
t.Logf("Memory read speed: %d bytes/s (%.2f GB/s)", rs, float64(rs)/(1024*1024*1024))
|
||||
t.Logf("Memory write speed: %d bytes/s (%.2f GB/s)", ws, float64(ws)/(1024*1024*1024))
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
package syscheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CheckResult represents the result of a system check
|
||||
type CheckResult struct {
|
||||
Name string
|
||||
Passed bool
|
||||
Actual string
|
||||
Expected string
|
||||
Message string
|
||||
}
|
||||
|
||||
// DiskInfo represents disk information
|
||||
type DiskInfo struct {
|
||||
AvailableGB float64
|
||||
WriteSpeed float64 // MB/s
|
||||
ReadSpeed float64 // MB/s
|
||||
}
|
||||
|
||||
// MemInfo represents memory information
|
||||
type MemInfo struct {
|
||||
TotalGB float64
|
||||
}
|
||||
|
||||
// CPUInfo represents CPU information
|
||||
type CPUInfo struct {
|
||||
Cores int
|
||||
FrequencyMHz float64
|
||||
}
|
||||
|
||||
// CommandExecutor defines interface for executing commands
|
||||
type CommandExecutor interface {
|
||||
ExecuteCommand(ctx context.Context, cmds ...string) (string, error)
|
||||
}
|
||||
|
||||
// CheckDiskSpace checks if disk space meets minimum requirements
|
||||
func CheckDiskSpace(ctx context.Context, executor CommandExecutor, minGB float64) (*CheckResult, error) {
|
||||
// Use df to check available disk space on root partition
|
||||
output, err := executor.ExecuteCommand(ctx, "df", "-BG", "/")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check disk space: %w", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(output), "\n")
|
||||
if len(lines) < 2 {
|
||||
return nil, fmt.Errorf("unexpected df output format")
|
||||
}
|
||||
|
||||
fields := strings.Fields(lines[1])
|
||||
if len(fields) < 4 {
|
||||
return nil, fmt.Errorf("unexpected df fields count")
|
||||
}
|
||||
|
||||
// Parse available space (4th field, format: "500G")
|
||||
availableStr := strings.TrimSuffix(fields[3], "G")
|
||||
available, err := strconv.ParseFloat(availableStr, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse available disk space: %w", err)
|
||||
}
|
||||
|
||||
result := &CheckResult{
|
||||
Name: "Disk Space",
|
||||
Passed: available >= minGB,
|
||||
Actual: fmt.Sprintf("%.2f GB", available),
|
||||
Expected: fmt.Sprintf(">= %.2f GB", minGB),
|
||||
}
|
||||
|
||||
if !result.Passed {
|
||||
result.Message = fmt.Sprintf("Insufficient disk space: %.2f GB available, %.2f GB required", available, minGB)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CheckDiskPerformance checks disk read/write performance
|
||||
func CheckDiskPerformance(ctx context.Context, executor CommandExecutor, minWriteMBps, minReadMBps float64) (*CheckResult, error) {
|
||||
// Use dd to test write performance
|
||||
writeCmd := "dd if=/dev/zero of=/tmp/test_write bs=1M count=1024 oflag=direct 2>&1 | tail -1"
|
||||
writeOutput, err := executor.ExecuteCommand(ctx, "bash", "-c", writeCmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check disk write performance: %w", err)
|
||||
}
|
||||
|
||||
// Parse write speed from dd output (format: "... copied, X.XX s, XXX MB/s")
|
||||
writeSpeed, err := parseDDSpeed(writeOutput)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse write speed: %w, output: %s", err, writeOutput)
|
||||
}
|
||||
|
||||
// Test read performance and clean up test file
|
||||
readCmd := "dd if=/tmp/test_write of=/dev/null bs=1M count=1024 iflag=direct 2>&1 | tail -1; rm -f /tmp/test_write"
|
||||
readOutput, err := executor.ExecuteCommand(ctx, "bash", "-c", readCmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check disk read performance: %w", err)
|
||||
}
|
||||
|
||||
// Parse read speed from dd output
|
||||
readSpeed, err := parseDDSpeed(readOutput)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse read speed: %w, output: %s", err, readOutput)
|
||||
}
|
||||
|
||||
passed := writeSpeed >= minWriteMBps && readSpeed >= minReadMBps
|
||||
result := &CheckResult{
|
||||
Name: "Disk Performance",
|
||||
Passed: passed,
|
||||
Actual: fmt.Sprintf("Write: %.2f MB/s, Read: %.2f MB/s", writeSpeed, readSpeed),
|
||||
Expected: fmt.Sprintf("Write: >= %.2f MB/s, Read: >= %.2f MB/s", minWriteMBps, minReadMBps),
|
||||
}
|
||||
|
||||
if !passed {
|
||||
result.Message = fmt.Sprintf("Insufficient disk performance")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// parseDDSpeed parses the speed from dd command output
|
||||
// Expected format: "104857600 bytes (105 MB, 100 MiB) copied, 0.125749 s, 834 MB/s"
|
||||
func parseDDSpeed(output string) (float64, error) {
|
||||
output = strings.TrimSpace(output)
|
||||
if output == "" {
|
||||
return 0, fmt.Errorf("empty output")
|
||||
}
|
||||
|
||||
// Find the last occurrence of "MB/s" or "GB/s"
|
||||
var speed float64
|
||||
var unit string
|
||||
|
||||
// Try to match "XXX MB/s" or "XXX GB/s" pattern
|
||||
if idx := strings.LastIndex(output, " MB/s"); idx != -1 {
|
||||
// Extract the number before " MB/s"
|
||||
fields := strings.Fields(output[:idx])
|
||||
if len(fields) == 0 {
|
||||
return 0, fmt.Errorf("no speed value found")
|
||||
}
|
||||
speedStr := fields[len(fields)-1]
|
||||
var err error
|
||||
speed, err = strconv.ParseFloat(speedStr, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse speed value '%s': %w", speedStr, err)
|
||||
}
|
||||
unit = "MB/s"
|
||||
} else if idx := strings.LastIndex(output, " GB/s"); idx != -1 {
|
||||
// Extract the number before " GB/s"
|
||||
fields := strings.Fields(output[:idx])
|
||||
if len(fields) == 0 {
|
||||
return 0, fmt.Errorf("no speed value found")
|
||||
}
|
||||
speedStr := fields[len(fields)-1]
|
||||
var err error
|
||||
speed, err = strconv.ParseFloat(speedStr, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse speed value '%s': %w", speedStr, err)
|
||||
}
|
||||
unit = "GB/s"
|
||||
speed *= 1024 // Convert GB/s to MB/s
|
||||
} else {
|
||||
return 0, fmt.Errorf("no MB/s or GB/s found in output")
|
||||
}
|
||||
|
||||
if unit == "MB/s" || unit == "GB/s" {
|
||||
return speed, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unexpected unit: %s", unit)
|
||||
}
|
||||
|
||||
// CheckMemory checks if system memory meets minimum requirements
|
||||
func CheckMemory(ctx context.Context, executor CommandExecutor, minGB float64) (*CheckResult, error) {
|
||||
// Use free -m to check memory in MB for better precision
|
||||
output, err := executor.ExecuteCommand(ctx, "free", "-m")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check memory: %w", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(output), "\n")
|
||||
if len(lines) < 2 {
|
||||
return nil, fmt.Errorf("unexpected free output format")
|
||||
}
|
||||
|
||||
fields := strings.Fields(lines[1])
|
||||
if len(fields) < 2 {
|
||||
return nil, fmt.Errorf("unexpected free fields count")
|
||||
}
|
||||
|
||||
// Parse total memory in MB
|
||||
totalMB, err := strconv.ParseFloat(fields[1], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse memory size: %w", err)
|
||||
}
|
||||
|
||||
// Convert MB to GB (1 GB = 1024 MB)
|
||||
totalGB := totalMB / 1024.0
|
||||
|
||||
result := &CheckResult{
|
||||
Name: "Memory Size",
|
||||
Passed: totalGB >= minGB,
|
||||
Actual: fmt.Sprintf("%.2f GB", totalGB),
|
||||
Expected: fmt.Sprintf(">= %.2f GB", minGB),
|
||||
}
|
||||
|
||||
if !result.Passed {
|
||||
result.Message = fmt.Sprintf("Insufficient memory: %.2f GB available, %.2f GB required", totalGB, minGB)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CheckCPUCores checks if CPU core count meets minimum requirements
|
||||
func CheckCPUCores(ctx context.Context, executor CommandExecutor, minCores int) (*CheckResult, error) {
|
||||
// Read /proc/cpuinfo to get CPU core count (more universal than nproc)
|
||||
output, err := executor.ExecuteCommand(ctx, "cat", "/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check CPU cores: %w", err)
|
||||
}
|
||||
|
||||
// Count the number of "processor" lines
|
||||
cores := 0
|
||||
lines := strings.Split(output, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "processor") {
|
||||
cores++
|
||||
}
|
||||
}
|
||||
|
||||
if cores == 0 {
|
||||
return nil, fmt.Errorf("failed to parse CPU cores from /proc/cpuinfo")
|
||||
}
|
||||
|
||||
result := &CheckResult{
|
||||
Name: "CPU Cores",
|
||||
Passed: cores >= minCores,
|
||||
Actual: fmt.Sprintf("%d cores", cores),
|
||||
Expected: fmt.Sprintf(">= %d cores", minCores),
|
||||
}
|
||||
|
||||
if !result.Passed {
|
||||
result.Message = fmt.Sprintf("Insufficient CPU cores: %d cores available, %d cores required", cores, minCores)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CheckCPUFrequency checks if CPU frequency meets minimum requirements
|
||||
func CheckCPUFrequency(ctx context.Context, executor CommandExecutor, minGHz float64) (*CheckResult, error) {
|
||||
// Read /proc/cpuinfo to get CPU frequency (more universal than lscpu)
|
||||
output, err := executor.ExecuteCommand(ctx, "cat", "/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check CPU frequency: %w", err)
|
||||
}
|
||||
|
||||
var maxFreqMHz float64
|
||||
lines := strings.Split(output, "\n")
|
||||
|
||||
// Try to parse from "cpu MHz" field (runtime frequency)
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "cpu MHz") {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) >= 2 {
|
||||
freqStr := strings.TrimSpace(fields[1])
|
||||
freq, err := strconv.ParseFloat(freqStr, 64)
|
||||
if err == nil && freq > maxFreqMHz {
|
||||
maxFreqMHz = freq
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not found, try to parse from "model name" field (base frequency)
|
||||
if maxFreqMHz == 0 {
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "model name") {
|
||||
// Look for pattern like "@ 2.60GHz"
|
||||
if idx := strings.Index(line, "@"); idx != -1 {
|
||||
freqPart := line[idx+1:]
|
||||
// Extract GHz value
|
||||
if ghzIdx := strings.Index(freqPart, "GHz"); ghzIdx != -1 {
|
||||
freqStr := strings.TrimSpace(freqPart[:ghzIdx])
|
||||
freqGHz, err := strconv.ParseFloat(freqStr, 64)
|
||||
if err == nil {
|
||||
maxFreqMHz = freqGHz * 1000.0
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if maxFreqMHz == 0 {
|
||||
return nil, fmt.Errorf("failed to parse CPU frequency from /proc/cpuinfo")
|
||||
}
|
||||
|
||||
freqGHz := maxFreqMHz / 1000.0
|
||||
minMHz := minGHz * 1000.0
|
||||
|
||||
result := &CheckResult{
|
||||
Name: "CPU Frequency",
|
||||
Passed: maxFreqMHz >= minMHz,
|
||||
Actual: fmt.Sprintf("%.2f GHz", freqGHz),
|
||||
Expected: fmt.Sprintf(">= %.2f GHz", minGHz),
|
||||
}
|
||||
|
||||
if !result.Passed {
|
||||
result.Message = fmt.Sprintf("Insufficient CPU frequency: %.2f GHz available, %.2f GHz required", freqGHz, minGHz)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
50
pkg/tool/human/size.go
Normal file
50
pkg/tool/human/size.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package human
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Duration(nano int64) string {
|
||||
duration := float64(nano)
|
||||
unit := "ns"
|
||||
if duration >= 1000 {
|
||||
duration /= 1000
|
||||
unit = "us"
|
||||
}
|
||||
|
||||
if duration >= 1000 {
|
||||
duration /= 1000
|
||||
unit = "ms"
|
||||
}
|
||||
|
||||
if duration >= 1000 {
|
||||
duration /= 1000
|
||||
unit = " s"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%6.2f%s", duration, unit)
|
||||
}
|
||||
|
||||
func Size(size int64) string {
|
||||
const (
|
||||
_ = iota
|
||||
KB = 1 << (10 * iota) // 1 KB = 1024 bytes
|
||||
MB // 1 MB = 1024 KB
|
||||
GB // 1 GB = 1024 MB
|
||||
TB // 1 TB = 1024 GB
|
||||
PB // 1 PB = 1024 TB
|
||||
)
|
||||
|
||||
switch {
|
||||
case size >= PB:
|
||||
return fmt.Sprintf("%.2f PB", float64(size)/PB)
|
||||
case size >= TB:
|
||||
return fmt.Sprintf("%.2f TB", float64(size)/TB)
|
||||
case size >= GB:
|
||||
return fmt.Sprintf("%.2f GB", float64(size)/GB)
|
||||
case size >= MB:
|
||||
return fmt.Sprintf("%.2f MB", float64(size)/MB)
|
||||
case size >= KB:
|
||||
return fmt.Sprintf("%.2f KB", float64(size)/KB)
|
||||
default:
|
||||
return fmt.Sprintf("%d bytes", size)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user