Compare commits
13 Commits
62b680dca8
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da6a846550 | ||
|
|
ce6ab8ab5f | ||
|
|
aafe60ee35 | ||
|
|
eb87d6fbed | ||
|
|
0bcb138fd5 | ||
|
|
c53c15fa8c | ||
|
|
f4f3590aec | ||
|
|
3a29e6221d | ||
|
|
38def02bf4 | ||
|
|
fdad0eb36c | ||
|
|
11523e3e48 | ||
|
|
4ec58ce4e5 | ||
|
|
1d3c159c00 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -4,3 +4,8 @@ dist
|
||||
.trae
|
||||
.vscode
|
||||
.idea
|
||||
forge
|
||||
dev_*.sh
|
||||
*.tar
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
47
AGENTS.md
47
AGENTS.md
@@ -1,47 +0,0 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to Qoder (qoder.com) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a Go project (`yizhisec.com/hsv2/forge`) using Go 1.25.2.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Build
|
||||
```bash
|
||||
go build ./...
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
### Run Single Test
|
||||
```bash
|
||||
go test -run TestName ./path/to/package
|
||||
```
|
||||
|
||||
### Lint
|
||||
```bash
|
||||
go vet ./...
|
||||
```
|
||||
|
||||
### Format Code
|
||||
```bash
|
||||
go fmt ./...
|
||||
```
|
||||
|
||||
### Tidy Dependencies
|
||||
```bash
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The codebase structure has not yet been established. As the project grows, update this section with:
|
||||
- Main package organization
|
||||
- Key architectural patterns
|
||||
- Important design decisions
|
||||
- Module relationships
|
||||
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=
|
||||
|
||||
3
helper/nginx-with-tool/Dockerfile
Normal file
3
helper/nginx-with-tool/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM docker-mirror.yizhisec.com/library/nginx:1.29.4-alpine3.23
|
||||
|
||||
RUN apk add curl wget tzdata unzip
|
||||
@@ -1,9 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/cmd/installcmd"
|
||||
)
|
||||
|
||||
func installCmd() *cobra.Command {
|
||||
@@ -11,15 +10,12 @@ func installCmd() *cobra.Command {
|
||||
Use: "install",
|
||||
Short: "Install the project",
|
||||
Long: `Install the built project to the specified location.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runInstall(args)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.AddCommand(
|
||||
installcmd.Check(),
|
||||
installcmd.Prepare(),
|
||||
)
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func runInstall(args []string) error {
|
||||
fmt.Println("Running install command...")
|
||||
return nil
|
||||
}
|
||||
|
||||
39
internal/cmd/installcmd/check.go
Normal file
39
internal/cmd/installcmd/check.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package installcmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/installer"
|
||||
)
|
||||
|
||||
func Check() *cobra.Command {
|
||||
var (
|
||||
workdir string
|
||||
target string
|
||||
ignoreDisk bool
|
||||
ignoreMemory bool
|
||||
ignoreCPU bool
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Check system requirements",
|
||||
Long: `Check system requirements for the project.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_installer := installer.NewInstaller(workdir, target)
|
||||
return _installer.Check(
|
||||
cmd.Context(),
|
||||
installer.WithIgnoreDiskCheck(ignoreDisk),
|
||||
installer.WithIgnoreMemoryCheck(ignoreMemory),
|
||||
installer.WithIgnoreCPUCheck(ignoreCPU),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().StringVar(&workdir, "workdir", "/root/hs-installation", "Working directory")
|
||||
_cmd.Flags().StringVar(&target, "target", "self", "Target")
|
||||
_cmd.Flags().BoolVar(&ignoreDisk, "ignore-kheck-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
|
||||
}
|
||||
29
internal/cmd/installcmd/k0s.go
Normal file
29
internal/cmd/installcmd/k0s.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package installcmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/installer"
|
||||
)
|
||||
|
||||
func K0s() *cobra.Command {
|
||||
|
||||
var (
|
||||
workdir string
|
||||
target string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "k0s",
|
||||
Short: "Install k0s",
|
||||
Long: "Install k0s",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_installer := installer.NewInstaller(workdir, target)
|
||||
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
|
||||
}
|
||||
30
internal/cmd/installcmd/prepare.go
Normal file
30
internal/cmd/installcmd/prepare.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package installcmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/installer"
|
||||
)
|
||||
|
||||
func Prepare() *cobra.Command {
|
||||
|
||||
var (
|
||||
workdir string
|
||||
target string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "prepare",
|
||||
Short: "Prepare for installation",
|
||||
Long: "Prepare for installation",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_installer := installer.NewInstaller(workdir, target)
|
||||
_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 {
|
||||
@@ -35,7 +35,7 @@ func makeCmd() *cobra.Command {
|
||||
return nil
|
||||
}
|
||||
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
if err = mk.DependencyCheck(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,20 +50,26 @@ func makeCmd() *cobra.Command {
|
||||
_cmd.PersistentFlags().StringVar(&opt.Cfg.Make.Dir, "dir", "/root/hsv2-installation", "make base directory")
|
||||
|
||||
_cmd.AddCommand(
|
||||
makecmd.ALL(),
|
||||
makecmd.Images(),
|
||||
makecmd.Binaries(),
|
||||
makecmd.Debs(),
|
||||
makecmd.K0s(),
|
||||
makecmd.Flannel(),
|
||||
makecmd.Longhorn(),
|
||||
makecmd.Mysql(),
|
||||
makecmd.Redis(),
|
||||
makecmd.ES(),
|
||||
makecmd.EMQX(),
|
||||
makecmd.Minio(),
|
||||
makecmd.Yosguard(),
|
||||
makecmd.Registry(),
|
||||
makecmd.LessDNS(),
|
||||
makecmd.HSNet(),
|
||||
makecmd.ConfigMap(),
|
||||
makecmd.Proxy(),
|
||||
makecmd.Seafile(),
|
||||
makecmd.App(),
|
||||
makecmd.Apps(),
|
||||
makecmd.Client(),
|
||||
)
|
||||
|
||||
return _cmd
|
||||
|
||||
139
internal/cmd/makecmd/all.go
Normal file
139
internal/cmd/makecmd/all.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func ALL() *cobra.Command {
|
||||
var (
|
||||
_workdir string
|
||||
_vendor string
|
||||
_version string
|
||||
_replica int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "all",
|
||||
Short: "Make all",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
mk = maker.NewMaker(_workdir)
|
||||
)
|
||||
|
||||
if err = mk.K0s(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Registry(cmd.Context(), "50Gi"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Flannel(cmd.Context(), "host-gw"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Longhorn(cmd.Context(), 2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.MySQL(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Redis(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Elastic(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.EMQX(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Minio(cmd.Context(), "100Gi"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Yosguard(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.LessDNS(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.HSNet(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.ConfigMap(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Proxy(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Seafile(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Proxy(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.Seafile(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppOEM(cmd.Context(), _version, _vendor, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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.AppFront(cmd.Context(), _version, _vendor, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mk.AppMie(cmd.Context(), _version, _replica); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_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,7 +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 {
|
||||
@@ -12,7 +19,272 @@ func App() *cobra.Command {
|
||||
|
||||
_cmd.AddCommand(
|
||||
appUser(),
|
||||
appClient(),
|
||||
appGateway(),
|
||||
appMie(),
|
||||
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{
|
||||
Use: "user",
|
||||
Short: "Make User App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
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
|
||||
}
|
||||
|
||||
func appClient() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "client",
|
||||
Short: "Make Client App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
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
|
||||
}
|
||||
|
||||
func appGateway() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "gateway",
|
||||
Short: "Make Gateway App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
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
|
||||
}
|
||||
|
||||
func appMie() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "mie",
|
||||
Short: "Make Mie App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
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
|
||||
}
|
||||
|
||||
func appOEM() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
vendor string
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "oem",
|
||||
Short: "Make OEM App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
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
|
||||
}
|
||||
|
||||
func appFront() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
vendor string
|
||||
_version string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "front",
|
||||
Short: "Make Front App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
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
|
||||
}
|
||||
|
||||
func appNginx() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
vendor string
|
||||
_version string
|
||||
_include_image bool
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "nginx",
|
||||
Short: "Make Nginx App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.AppNginx(cmd.Context(), _version, vendor, replica, _include_image)
|
||||
},
|
||||
}
|
||||
|
||||
_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 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
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
)
|
||||
|
||||
func appUser() *cobra.Command {
|
||||
var (
|
||||
replica int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "user",
|
||||
Short: "Make User App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
return mk.AppUser(cmd.Context(), replica)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -3,17 +3,16 @@ package makecmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Binaries() *cobra.Command {
|
||||
func K0s() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "binaries",
|
||||
Aliases: []string{"bin", "B"},
|
||||
Short: "Build binary files",
|
||||
Long: `Build all required binary files for the project.`,
|
||||
Use: "k0s",
|
||||
Short: "make k0s files(binary, images)",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
return mk.Binary(cmd.Context())
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.K0s(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
298
internal/cmd/makecmd/client.go
Normal file
298
internal/cmd/makecmd/client.go
Normal file
@@ -0,0 +1,298 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func Client() *cobra.Command {
|
||||
_cmd := &cobra.Command{
|
||||
Use: "client",
|
||||
Short: "make client pkg",
|
||||
}
|
||||
|
||||
_cmd.AddCommand(
|
||||
clientMac(),
|
||||
clientWin(),
|
||||
clientLinux(),
|
||||
clientBaseImage(),
|
||||
)
|
||||
|
||||
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-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 (
|
||||
err error
|
||||
version string
|
||||
)
|
||||
|
||||
if version, err = clientVersion(VERSION_URL); err != nil {
|
||||
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(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 clientMac() *cobra.Command {
|
||||
|
||||
var (
|
||||
_version string
|
||||
_pkg bool
|
||||
)
|
||||
|
||||
_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-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 (
|
||||
err error
|
||||
version string
|
||||
)
|
||||
|
||||
if version, err = clientVersion(VERSION_URL); err != nil {
|
||||
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"),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().StringVar(&_version, "version", "", "手动指定版本")
|
||||
_cmd.Flags().BoolVar(&_pkg, "pkg", false, "是否生成升级 pkg")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func clientVersion(_url string) (string, error) {
|
||||
type Res struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
rr *http.Response
|
||||
res Res
|
||||
bs []byte
|
||||
)
|
||||
|
||||
logger.Debug("clientVersion: getting client version with url = %s", _url)
|
||||
if rr, err = tc.HttpClient().Get(_url); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer rr.Body.Close()
|
||||
|
||||
if bs, err = io.ReadAll(rr.Body); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
logger.Debug("clientVersion: got client version body raw: %s, url = %s", string(bs), _url)
|
||||
|
||||
if err = json.Unmarshal(bs, &res); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if res.Version == "" {
|
||||
return "", fmt.Errorf("get version empty, url = %s", _url)
|
||||
}
|
||||
|
||||
return res.Version, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package makecmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func ConfigMap() *cobra.Command {
|
||||
@@ -11,7 +12,7 @@ func ConfigMap() *cobra.Command {
|
||||
Aliases: []string{"cm"},
|
||||
Short: "构建 ConfigMap",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.ConfigMap(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
)
|
||||
|
||||
func Debs() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "debs",
|
||||
Aliases: []string{"deb"},
|
||||
Short: "Build Debian packages",
|
||||
Long: `Build all required Debian packages for the project.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
return mk.Deb(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package makecmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func EMQX() *cobra.Command {
|
||||
@@ -10,7 +11,7 @@ func EMQX() *cobra.Command {
|
||||
Use: "emqx",
|
||||
Short: "Make EMQX",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.EMQX(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func ES() *cobra.Command {
|
||||
@@ -18,7 +19,7 @@ func ES() *cobra.Command {
|
||||
Use: "es",
|
||||
Aliases: []string{"elastic", "elasticsearch"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Elastic(
|
||||
cmd.Context(),
|
||||
maker.WithElasticMakeHelper(makeHelper),
|
||||
|
||||
@@ -3,6 +3,7 @@ package makecmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Flannel() *cobra.Command {
|
||||
@@ -15,7 +16,7 @@ func Flannel() *cobra.Command {
|
||||
Short: "Build Flannel resources",
|
||||
Long: `Build and prepare Flannel network resources.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Flannel(cmd.Context(), _mode)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package makecmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func HSNet() *cobra.Command {
|
||||
@@ -11,7 +12,7 @@ func HSNet() *cobra.Command {
|
||||
Short: "Build hs-net",
|
||||
Long: `Build hs-net`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.HSNet(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func Images() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
|
||||
return mk.Images(cmd.Context())
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ package makecmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func LessDNS() *cobra.Command {
|
||||
@@ -11,7 +12,7 @@ func LessDNS() *cobra.Command {
|
||||
Short: "Build lessdns",
|
||||
Long: `Build lessdns`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.LessDNS(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package makecmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Longhorn() *cobra.Command {
|
||||
@@ -15,7 +16,7 @@ func Longhorn() *cobra.Command {
|
||||
Short: "Build Longhorn resources",
|
||||
Long: `Build and prepare Longhorn storage resources.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Longhorn(cmd.Context(), replicaCount)
|
||||
},
|
||||
}
|
||||
|
||||
109
internal/cmd/makecmd/minio.go
Normal file
109
internal/cmd/makecmd/minio.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Minio() *cobra.Command {
|
||||
var (
|
||||
storage int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "minio",
|
||||
Short: "Make Minio",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Minio(cmd.Context(), fmt.Sprintf("%dGi", storage))
|
||||
},
|
||||
}
|
||||
|
||||
_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
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Mysql() *cobra.Command {
|
||||
@@ -23,7 +24,7 @@ func Mysql() *cobra.Command {
|
||||
maker.WithMySQLStorage(fmt.Sprintf("%dGi", storage)),
|
||||
}
|
||||
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.MySQL(cmd.Context(), opts...)
|
||||
},
|
||||
}
|
||||
|
||||
20
internal/cmd/makecmd/proxy.go
Normal file
20
internal/cmd/makecmd/proxy.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Proxy() *cobra.Command {
|
||||
_cmd := &cobra.Command{
|
||||
Use: "proxy",
|
||||
Short: "Make Proxy(by caddy)",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Proxy(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Redis() *cobra.Command {
|
||||
var (
|
||||
replicas int
|
||||
password string
|
||||
storage string
|
||||
storage int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
@@ -17,19 +20,19 @@ func Redis() *cobra.Command {
|
||||
Short: "Build Redis resources",
|
||||
Long: `Build and prepare Redis cache resources.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker()
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Redis(
|
||||
cmd.Context(),
|
||||
maker.WithRedisReplicaCount(replicas),
|
||||
maker.WithRedisPassword(password),
|
||||
maker.WithRedisStorage(storage),
|
||||
maker.WithRedisStorage(fmt.Sprintf("%dGi", storage)),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replicas, "replica-count", 2, "Redis 副本数")
|
||||
_cmd.Flags().StringVar(&password, "password", "", "Redis 密码")
|
||||
_cmd.Flags().StringVar(&storage, "storage-size", "5Gi", "Redis 存储大小(如: 5Gi)")
|
||||
_cmd.Flags().IntVar(&storage, "storage-size", 5, "Redis 存储大小(单位Gi)如: 5")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
28
internal/cmd/makecmd/registry.go
Normal file
28
internal/cmd/makecmd/registry.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Registry() *cobra.Command {
|
||||
var (
|
||||
size int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "registry",
|
||||
Short: "Make registry dependency",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Registry(cmd.Context(), fmt.Sprintf("%dGi", size))
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&size, "storage-size", 50, "Redis 存储大小(单位Gi)如: 100")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
27
internal/cmd/makecmd/seafile.go
Normal file
27
internal/cmd/makecmd/seafile.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package makecmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func Seafile() *cobra.Command {
|
||||
|
||||
var (
|
||||
storage int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "seafile",
|
||||
Short: "make seafile dependency",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
return mk.Seafile(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&storage, "storage-size", 50, "指定 seafile 空间大小(单位GB)")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
@@ -3,18 +3,25 @@ package makecmd
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
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()
|
||||
return mk.Yosguard(cmd.Context())
|
||||
mk := maker.NewMaker(opt.Cfg.Make.Dir)
|
||||
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 {
|
||||
|
||||
140
internal/controller/installer/installer.check.go
Normal file
140
internal/controller/installer/installer.check.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package installer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/syscheck"
|
||||
)
|
||||
|
||||
type CheckOption func(*checkOpt)
|
||||
type checkOpt struct {
|
||||
ignoreDisk bool
|
||||
ignoreMemory bool
|
||||
ignoreCPU bool
|
||||
}
|
||||
|
||||
func WithIgnoreDiskCheck(ignore bool) CheckOption {
|
||||
return func(o *checkOpt) {
|
||||
o.ignoreDisk = ignore
|
||||
}
|
||||
}
|
||||
|
||||
func WithIgnoreMemoryCheck(ignore bool) CheckOption {
|
||||
return func(o *checkOpt) {
|
||||
o.ignoreMemory = ignore
|
||||
}
|
||||
}
|
||||
|
||||
func WithIgnoreCPUCheck(ignore bool) CheckOption {
|
||||
return func(o *checkOpt) {
|
||||
o.ignoreCPU = ignore
|
||||
}
|
||||
}
|
||||
|
||||
func (i *installer) Check(ctx context.Context, opts ...CheckOption) error {
|
||||
var (
|
||||
err error
|
||||
o = &checkOpt{}
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
logger.Info("☑️ installer.Check: Starting system checks...")
|
||||
|
||||
if err = i.targetOK(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 !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)
|
||||
}
|
||||
}
|
||||
|
||||
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 !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)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("✅ %s: %s", diskPerfResult.Name, diskPerfResult.Actual)
|
||||
|
||||
// 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 !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)
|
||||
}
|
||||
}
|
||||
|
||||
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 !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)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("✅ %s: %s", cpuCoresResult.Name, cpuCoresResult.Actual)
|
||||
|
||||
// 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 !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)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("✅ %s: %s", cpuFreqResult.Name, cpuFreqResult.Actual)
|
||||
|
||||
logger.Info("✅ installer.Check: All system checks passed successfully!")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,8 +1,153 @@
|
||||
package controller
|
||||
package installer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
type installer struct {
|
||||
workdir string
|
||||
target string
|
||||
}
|
||||
|
||||
func NewInstaller() *installer {
|
||||
return &installer{}
|
||||
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")
|
||||
}
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("command failed: %w, output: %s", err, string(output))
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
123
internal/controller/installer/installer.k0s.go
Normal file
123
internal/controller/installer/installer.k0s.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package installer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
type K0sOpt func(*k0sOpt)
|
||||
type k0sOpt struct {
|
||||
Type string // controller, worker
|
||||
controllerAsWorker bool
|
||||
WorkerTokenFile string
|
||||
}
|
||||
|
||||
func WithK0sType(t string) K0sOpt {
|
||||
types := []string{"controller", "worker"}
|
||||
return func(o *k0sOpt) {
|
||||
if lo.Contains(types, t) {
|
||||
o.Type = t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithK0sControllerAsWorker() K0sOpt {
|
||||
return func(o *k0sOpt) {
|
||||
o.controllerAsWorker = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithK0sWorkerTokenFile(filename string) K0sOpt {
|
||||
return func(o *k0sOpt) {
|
||||
if filename != "" {
|
||||
o.WorkerTokenFile = filename
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
184
internal/controller/installer/installer.prepare.go
Normal file
184
internal/controller/installer/installer.prepare.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package installer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
func (i *installer) Prepare(ctx context.Context) error {
|
||||
var (
|
||||
err 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 {
|
||||
logger.Debug("❌ installer.Prepare: Failed to set timezone: %v", err)
|
||||
return fmt.Errorf("failed to set timezone: %w", err)
|
||||
}
|
||||
logger.Info("✅ installer.Prepare: Timezone set successfully")
|
||||
|
||||
// 2. Disable swap
|
||||
logger.Info("☑️ installer.Prepare: Disabling swap...")
|
||||
if err = i.disableSwap(ctx); err != nil {
|
||||
logger.Debug("❌ installer.Prepare: Failed to disable swap: %v", err)
|
||||
return fmt.Errorf("failed to disable swap: %w", err)
|
||||
}
|
||||
logger.Info("✅ installer.Prepare: Swap disabled successfully")
|
||||
|
||||
// 3. Load module: iscsi_tcp
|
||||
logger.Info("☑️ installer.Prepare: Loading kernel module iscsi_tcp...")
|
||||
if err = i.loadKernelModule(ctx, "iscsi_tcp"); err != nil {
|
||||
logger.Debug("❌ installer.Prepare: Failed to load iscsi_tcp module: %v", err)
|
||||
return fmt.Errorf("failed to load iscsi_tcp module: %w", err)
|
||||
}
|
||||
logger.Info("✅ installer.Prepare: iscsi_tcp module loaded successfully")
|
||||
|
||||
// 4. Load module: br_netfilter
|
||||
logger.Info("☑️ installer.Prepare: Loading kernel module br_netfilter...")
|
||||
if err = i.loadKernelModule(ctx, "br_netfilter"); err != nil {
|
||||
logger.Debug("❌ installer.Prepare: Failed to load br_netfilter module: %v", err)
|
||||
return fmt.Errorf("failed to load br_netfilter module: %w", err)
|
||||
}
|
||||
logger.Info("✅ installer.Prepare: br_netfilter module loaded successfully")
|
||||
|
||||
// 5. Apply sysctl settings
|
||||
logger.Info("☑️ installer.Prepare: Applying sysctl settings...")
|
||||
if err = i.applySysctlSettings(ctx); err != nil {
|
||||
logger.Debug("❌ installer.Prepare: Failed to apply sysctl settings: %v", err)
|
||||
return fmt.Errorf("failed to apply sysctl settings: %w", err)
|
||||
}
|
||||
logger.Info("✅ installer.Prepare: Sysctl settings applied successfully")
|
||||
|
||||
logger.Info("✅ installer.Prepare: System preparation completed successfully!")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setTimezone sets the system timezone to Asia/Shanghai
|
||||
func (i *installer) setTimezone(ctx context.Context) error {
|
||||
// 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")
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// disableSwap disables all swap partitions and removes swap entries from /etc/fstab
|
||||
func (i *installer) disableSwap(ctx context.Context) error {
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadKernelModule loads a kernel module and ensures it's loaded on boot
|
||||
func (i *installer) loadKernelModule(ctx context.Context, moduleName string) error {
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// applySysctlSettings applies required sysctl settings for Kubernetes
|
||||
func (i *installer) applySysctlSettings(ctx context.Context) error {
|
||||
const sysctlConfig = `# Kubernetes required settings
|
||||
net.bridge.bridge-nf-call-iptables = 1
|
||||
net.bridge.bridge-nf-call-ip6tables = 1
|
||||
net.ipv4.ip_forward = 1
|
||||
net.ipv4.conf.all.forwarding = 1
|
||||
net.ipv6.conf.all.forwarding = 1
|
||||
vm.swappiness = 0
|
||||
vm.overcommit_memory = 1
|
||||
vm.panic_on_oom = 0
|
||||
fs.file-max = 1000000
|
||||
fs.inotify.max_user_watches = 2099999999
|
||||
fs.inotify.max_user_instances = 2099999999
|
||||
fs.inotify.max_queued_events = 2099999999
|
||||
net.ipv4.neigh.default.gc_thresh1 = 1024
|
||||
net.ipv4.neigh.default.gc_thresh2 = 4096
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
5
internal/controller/installer/installer.redis.go
Normal file
5
internal/controller/installer/installer.redis.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package installer
|
||||
|
||||
func (i *installer) Redis() error {
|
||||
return nil
|
||||
}
|
||||
7
internal/controller/installer/installer.yosguard.go
Normal file
7
internal/controller/installer/installer.yosguard.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package installer
|
||||
|
||||
import "context"
|
||||
|
||||
func (i *installer) YosGuard(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
262
internal/controller/maker/app.client.go
Normal file
262
internal/controller/maker/app.client.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
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
|
||||
BackupSeafile:
|
||||
Host: hs-resource-server
|
||||
Port: 19980
|
||||
ClientDir:
|
||||
CompatibleAppFile: /yizhisec/hs_nginx/resource/compatible_apps.csv
|
||||
OEMFilePath: /yizhisec/hs_nginx/data/443/oem/data.json
|
||||
StorageDir: /data/storage/client_pkg
|
||||
WindowsClient:
|
||||
App7zDir: app7z_0
|
||||
Dir: windows
|
||||
Database:
|
||||
Elastic:
|
||||
Address: http://es-service.db-es:9200
|
||||
IPDBFile: /etc/client_server/ipv4.ipdb
|
||||
Mysql:
|
||||
Address: mysql.db-mysql:3306
|
||||
DBName: mie
|
||||
Password: L0hMysql.
|
||||
UserName: root
|
||||
Redis:
|
||||
Address: redis-master.db-redis:6379
|
||||
Password: HybridScope0xRed1s.
|
||||
DisabledFeatureFilePath: /etc/yizhisec/disabled_features
|
||||
ExportWithBlindWatermark: 1
|
||||
ExternalOA:
|
||||
Host: ""
|
||||
Port: 0
|
||||
ExternalOASecret:
|
||||
HsID: ""
|
||||
HsSecret: ""
|
||||
GatewayLinkPort: 23209
|
||||
Key:
|
||||
Token: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
|
||||
LicensePubKey: /etc/yizhisec/license/pub_key
|
||||
Log:
|
||||
Dir: ./log
|
||||
Level: 1
|
||||
Name: client_server
|
||||
MQTTServer:
|
||||
Host: emqx-service.db-emqx
|
||||
Port: 1883
|
||||
NginxEnvFilePath: /yizhisec/hs_nginx/.env
|
||||
Pipelines:
|
||||
- processor:
|
||||
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpjID0ganNvbi5kZWNvZGUoR2V0UmVzb3VyY2VDb25maWcoImNvbnRyb2xsZXIiKSkKY3B1dCA9IGNbImhhcmR3YXJlX3Jlc291cmNlX3RocmVob2xkIl1bImNwdSJdCm1lbXQgPSBjWyJoYXJkd2FyZV9yZXNvdXJjZV90aHJlaG9sZCJdWyJtZW1vcnkiXQpkaXNrdCA9IGNbImhhcmR3YXJlX3Jlc291cmNlX3RocmVob2xkIl1bImRpc2siXQpzd2l0Y2ggPSBjWyJzd2l0Y2giXQpkZWJ1Z19pbmZvID0ge30KZGVmIGNoZWNrKGNwdSwgbWVtb3J5LCBkaXNrKToKICBpZiBzd2l0Y2ggPT0gRmFsc2U6CiAgICByZXR1cm4KICBtc2cgPSAiIgogIGV2dCA9IHt9CiAgaWYgY3B1ID4gY3B1dDoKICAgICAgbXNnID0gIkNQVeWNoOeUqOi2hei/hyIgKyBzdHIoY3B1dCkgKyAiJSIKICBpZiBtZW1vcnkgPiBtZW10OgogICAgaWYgbGVuKG1zZykgIT0gMDoKICAgICAgbXNnID0gbXNnICsgIu+8jCAiCiAgICBtc2cgPSBtc2cgKyAi5YaF5a2Y5Y2g55So6LaF6L+HIiArIHN0cihtZW10KSArICIlIgogIGlmIGRpc2sgPiBkaXNrdDoKICAgIGlmIGxlbihtc2cpICE9IDA6CiAgICAgIG1zZyA9IG1zZyArICLvvIwgIgogICAgbXNnID0gbXNnICsgIuejgeebmOWNoOeUqOi2hei/hyIgKyBzdHIoZGlza3QpICsgIiUiCiAgaWYgbGVuKG1zZykgIT0gMDoKICAgIG1zZyA9IG1zZyArICLjgILor7flj4rml7bmn6XnnIvmnI3liqHlmajnmoTkvb/nlKjmg4XlhrXmiJbph43mlrDosIPmlbTpmIjlgLzjgIIiCiAgaWYgbGVuKG1zZykgPT0gMDoKICAgIHJldHVybiBldnQKICBpZ25vcmUgPSBDb21wYXJlQW5kU2V0KCJfcGlwZWxpbmVfbHN0X2N0IiwgR2V0VGltZVNlYygpLCA2MCo2MCkKICBpZiBpZ25vcmUgPT0gMDoKICAgIGRlYnVnX2luZm9bImluZm8iXSA9ICJyYXRlIGxpbWl0IGlnbm9yZSIKICAgIHJldHVybgogIG1zZyA9ICLnrZbnlaXmjqfliLblmajnmoQiICsgbXNnCiAgZXZ0WyJkZXRhaWwiXSA9IG1zZwogIGV2dFsiZG9tYWluX2lkIl0gPSAwCiAgZXZ0WyJtZXNzYWdlX3R5cGUiXSA9IDEwMQogIGV2dFsiY3JlYXRlX3RpbWUiXSA9IEdldFRpbWVTZWMoKQogIHJldHVybiBldnQKCm91dHB1dCA9IGpzb24uZW5jb2RlKGNoZWNrKHZbImNwdSJdWyJwZXJjZW50Il0sIHZbIm1lbSJdWyJwZXJjZW50Il0sIHZbImRpc2siXVsicGVyY2VudCJdKSk=
|
||||
Type: starlark
|
||||
sink:
|
||||
Cols:
|
||||
- create_time
|
||||
- detail
|
||||
- domain_id
|
||||
- message_type
|
||||
DB: mie
|
||||
Host: mysql.db-mysql:3306
|
||||
Pwd: L0hMysql.
|
||||
Table: message_content
|
||||
Type: mysql_sink
|
||||
User: root
|
||||
source:
|
||||
Host: redis-master.db-redis:6379
|
||||
Key: evt_server_state:controller
|
||||
Pwd: HybridScope0xRed1s.
|
||||
Tick: 3
|
||||
Type: redis_source
|
||||
- processor:
|
||||
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpjID0ganNvbi5kZWNvZGUoR2V0UmVzb3VyY2VDb25maWcoImdhdGV3YXkiKSkKY3B1dCA9IGNbImhhcmR3YXJlX3Jlc291cmNlX3RocmVob2xkIl1bImNwdSJdCm1lbXQgPSBjWyJoYXJkd2FyZV9yZXNvdXJjZV90aHJlaG9sZCJdWyJtZW1vcnkiXQpkaXNrdCA9IGNbImhhcmR3YXJlX3Jlc291cmNlX3RocmVob2xkIl1bImRpc2siXQpzd2l0Y2ggPSBjWyJzd2l0Y2giXQpkZWJ1Z19pbmZvID0ge30KZGVmIGNoZWNrKCk6CiAgaWYgc3dpdGNoID09IEZhbHNlOgogICAgcmV0dXJuCiAga3MgPSB2LmtleXMoKQogIGlmIGxlbihrcykgPiAwOgogICAgayA9IGtzWzBdCiAgY3B1ID0gdltrXVsiY3B1Il0KICBtZW0gPSB2W2tdWyJtZW0iXQogIGRpc2sgPSB2W2tdWyJkaXNrIl0KICBtc2cgPSAiIgogIGV2dCA9IHt9CiAgaWYgY3B1ID4gY3B1dDoKICAgICAgbXNnID0gIkNQVeWNoOeUqOi2hei/hyIgKyBzdHIoY3B1dCkgKyAiJSIKICBpZiBtZW0gPiBtZW10OgogICAgaWYgbGVuKG1zZykgIT0gMDoKICAgICAgbXNnID0gbXNnICsgIu+8jCAiCiAgICBtc2cgPSBtc2cgKyAi5YaF5a2Y5Y2g55So6LaF6L+HIiArIHN0cihtZW10KSArICIlIgogIGlmIGRpc2sgPiBkaXNrdDoKICAgIGlmIGxlbihtc2cpICE9IDA6CiAgICAgIG1zZyA9IG1zZyArICLvvIwgIgogICAgbXNnID0gbXNnICsgIuejgeebmOWNoOeUqOi2hei/hyIgKyBzdHIoZGlza3QpICsgIiUiCiAgaWYgbGVuKG1zZykgIT0gMDoKICAgIG1zZyA9IG1zZyArICLjgILor7flj4rml7bmn6XnnIvmnI3liqHlmajnmoTkvb/nlKjmg4XlhrXmiJbph43mlrDosIPmlbTpmIjlgLzjgIIiCiAgaWYgbGVuKG1zZykgPT0gMDoKICAgIHJldHVybiBldnQKICBpZ25vcmUgPSBDb21wYXJlQW5kU2V0KCJfcGlwZWxpbmVfbHN0X2d0IiwgR2V0VGltZVNlYygpLCA2MCo2MCkKICBpZiBpZ25vcmUgPT0gMDoKICAgIGRlYnVnX2luZm9bImluZm8iXSA9ICJyYXRlIGxpbWl0IGlnbm9yZSIKICAgIHJldHVybgogIG1zZyA9ICLnvZHlhbPnmoQiICsgbXNnCiAgZXZ0WyJkZXRhaWwiXSA9IG1zZwogIGV2dFsiZG9tYWluX2lkIl0gPSAwCiAgZXZ0WyJtZXNzYWdlX3R5cGUiXSA9IDEwMgogIGV2dFsiY3JlYXRlX3RpbWUiXSA9IEdldFRpbWVTZWMoKQogIHJldHVybiBldnQKCm91dHB1dCA9IGpzb24uZW5jb2RlKGNoZWNrKCkp
|
||||
Type: starlark
|
||||
sink:
|
||||
Cols:
|
||||
- create_time
|
||||
- detail
|
||||
- domain_id
|
||||
- message_type
|
||||
DB: mie
|
||||
Host: mysql.db-mysql:3306
|
||||
Pwd: L0hMysql.
|
||||
Table: message_content
|
||||
Type: mysql_sink
|
||||
User: root
|
||||
source:
|
||||
Host: redis-master.db-redis:6379
|
||||
Key: evt_server_state:gateway
|
||||
Pwd: HybridScope0xRed1s.
|
||||
Tick: 3
|
||||
Type: redis_source
|
||||
- processor:
|
||||
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpjID0ganNvbi5kZWNvZGUoR2V0TGljZW5zZUNvbmZpZygibGljZW5zZSIpKQpsZWZ0ID0gY1sibGljZW5zZV90aHJlaG9sZCJdWyJyZW1haW5pbmdfZGF5Il0Kc3dpdGNoID0gY1sic3dpdGNoIl0KZGVidWdfaW5mbyA9IHt9CmRlZiBjaGVjaygpOgogIGlmIHN3aXRjaCA9PSBGYWxzZToKICAgIHJldHVybgogIAogIGV0ID0gdlsiZXhwaXJlX3RpbWUiXQogIGV2dCA9IHt9CiAgY3VyID0gR2V0VGltZVNlYygpCiAgaWYgZXQgPCAoY3VyICsgbGVmdCAqIDg2NDAwKToKICAgIGlnbm9yZSA9IENvbXBhcmVBbmRTZXQoIl9waXBlbGluZV9sc3RfbCIsIEdldFRpbWVTZWMoKSwgNjAqNjAqMjQpCiAgICBpZiBpZ25vcmUgPT0gMDoKICAgICAgZGVidWdfaW5mb1siaW5mbyJdID0gInJhdGUgbGltaXQgaWdub3JlIgogICAgICByZXR1cm4KICAgIGV2dFsiZGV0YWlsIl0gPSAi5Lqn5ZOB5o6I5p2D5Ymp5L2Z5aSp5pWw5bCP5LqOIiArIHN0cihsZWZ0KSArICLlpKnjgILor7flj4rml7bmn6XnnIvkvb/nlKjmg4XlhrXjgIHph43mlrDosIPmlbTpmIjlgLzmiJbogZTns7vnrqHnkIblkZjmt7vliqDmjojmnYPjgIIiCiAgICBldnRbImRvbWFpbl9pZCJdID0gMAogICAgZXZ0WyJtZXNzYWdlX3R5cGUiXSA9IDIwMgogICAgZXZ0WyJjcmVhdGVfdGltZSJdID0gR2V0VGltZVNlYygpCiAgICByZXR1cm4gZXZ0Cm91dHB1dCA9IGpzb24uZW5jb2RlKGNoZWNrKCkp
|
||||
Type: starlark
|
||||
sink:
|
||||
Cols:
|
||||
- create_time
|
||||
- detail
|
||||
- domain_id
|
||||
- message_type
|
||||
DB: mie
|
||||
Host: mysql.db-mysql:3306
|
||||
Pwd: L0hMysql.
|
||||
Table: message_content
|
||||
Type: mysql_sink
|
||||
User: root
|
||||
source:
|
||||
Host: redis-master.db-redis:6379
|
||||
Key: license_state_cache:expire
|
||||
Pwd: HybridScope0xRed1s.
|
||||
Tick: 3
|
||||
Type: redis_source
|
||||
- processor:
|
||||
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpjID0ganNvbi5kZWNvZGUoR2V0TGljZW5zZUNvbmZpZygibGljZW5zZV9kZXZpY2UiKSkKY2wgPSBjWyJsaWNlbnNlX2RldmljZV90aHJlaG9sZCJdWyJjbGllbnRfbGVmdCJdCm1sID0gY1sibGljZW5zZV9kZXZpY2VfdGhyZWhvbGQiXVsibW9iaWxlX2xlZnQiXQptYyA9IHZbIm1heF9jbGllbnRfY291bnQiXQptYiA9IHZbIm1heF9tb2JpbGVfY2xpZW50X2NvdW50Il0KY2MgPSB2WyJjdXJyZW50X2NsaWVudF9jb3VudCJdCmNtID0gdlsiY3VycmVudF9tb2JpbGVfY2xpZW50X2NvdW50Il0Kc3dpdGNoID0gY1sic3dpdGNoIl0KZGVidWdfaW5mbyA9IHt9CmRlZiBjaGVjaygpOgogIGlmIHN3aXRjaCA9PSBGYWxzZToKICAgIHJldHVybgogIGV2dCA9IHt9CiAgbXNnID0gIiIKICBpZiAobWMtY2MpIDwgY2w6CiAgICBtc2cgPSBtc2cgKyAi5a6i5oi356uv5L2/55So5pWw6YeP5bCR5LqOIitzdHIoY2wpKyLkuKoiCiAgaWYgbGVuKG1zZykgPiAwOgogICAgbXNnID0gbXNnICsgIu+8jCIKICBpZiAobWItY20pIDwgbWw6CiAgICBtc2cgPSBtc2cgKyAi56e75Yqo56uv5L2/55So5pWw6YeP5bCR5LqOIitzdHIobWwpKyLkuKrjgIIiCiAgaWYgbGVuKG1zZykgPiAwOgogICAgaWdub3JlID0gQ29tcGFyZUFuZFNldCgiX3BpcGVsaW5lX2xzdF9sZCIsIEdldFRpbWVTZWMoKSwgNjAqNjApCiAgICBpZiBpZ25vcmUgPT0gMDoKICAgICAgZGVidWdfaW5mb1siaW5mbyJdID0gInJhdGUgbGltaXQgaWdub3JlIgogICAgICByZXR1cm4KICAgIGV2dFsiZGV0YWlsIl0gPSBtc2cgKyAi6K+35Y+K5pe25p+l55yL5L2/55So5oOF5Ya144CB6YeN5paw6LCD5pW06ZiI5YC85oiW6IGU57O7566h55CG5ZGY5re75Yqg5o6I5p2D44CCIgogICAgZXZ0WyJkb21haW5faWQiXSA9IDAKICAgIGV2dFsibWVzc2FnZV90eXBlIl0gPSAyMDIKICAgIGV2dFsiY3JlYXRlX3RpbWUiXSA9IEdldFRpbWVTZWMoKQogICAgcmV0dXJuIGV2dApvdXRwdXQgPSBqc29uLmVuY29kZShjaGVjaygpKQ==
|
||||
Type: starlark
|
||||
sink:
|
||||
Cols:
|
||||
- create_time
|
||||
- detail
|
||||
- domain_id
|
||||
- message_type
|
||||
DB: mie
|
||||
Host: mysql.db-mysql:3306
|
||||
Pwd: L0hMysql.
|
||||
Table: message_content
|
||||
Type: mysql_sink
|
||||
User: root
|
||||
source:
|
||||
Host: redis-master.db-redis:6379
|
||||
Key: license_state_cache:online
|
||||
Pwd: HybridScope0xRed1s.
|
||||
Tick: 3
|
||||
Type: redis_source
|
||||
- processor:
|
||||
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpkZWJ1Z19pbmZvID0ge30KZGVmIGNoZWNrKCk6CiAgZXZ0ID0ge30KICBtc2cgPSAiIgogIAogIGRpZCA9IHZbImRvbWFpbl9pZCJdCiAgdWlkID0gdlsidXNlcl9pZCJdCiAgdW5hbWUgPSBHZXRVc2VyTmFtZSh1aWQpCiAgbGltaXQgPSB2WyJsaW1pdCJdCiAgcmFuZ2UgPSB2WyJyYW5nZSJdCiAgcnUgPSB2WyJyYW5nZV91bml0Il0KICBzdSA9IHZbInNpemVfdW5pdCJdCiAgZSA9IHZbImV2dCJdCiAgaWYgZSA9PSAiY291bnQiOgogICAgZXZ0WyJtZXNzYWdlX3R5cGUiXSA9IDMwMQogICAgdW5pdCA9IHN0cihyYW5nZSkKICAgIGlmIHJ1ID09ICJIIiBvciBydSA9PSAiaCI6CiAgICAgICAgdW5pdCA9IHVuaXQgKyAi5bCP5pe2IgogICAgaWYgcnUgPT0gIkQiIG9yIHJ1ID09ICJkIjoKICAgICAgICB1bml0ID0gdW5pdCArICLlpKkiCiAgICBtc2cgPSAi5ZyoIiArIHVuaXQgKyAi5YaF77yMIiArIHVuYW1lICsgIueahOWfn+WGheaWh+S7tuWvvOWHuuaVsOmHj+i2hei/hyIgKyBzdHIobGltaXQpICsgIuS4qu+8jCIKICBpZiBlID09ICJzaXplIjoKICAgIGV2dFsibWVzc2FnZV90eXBlIl0gPSAzMDIKICAgIHVuaXQgPSBzdHIocmFuZ2UpCiAgICBpZiBydSA9PSAiSCIgb3IgcnUgPT0gImgiOgogICAgICAgIHVuaXQgPSB1bml0ICsgIuWwj+aXtiIKICAgIGlmIHJ1ID09ICJEIiBvciBydSA9PSAiZCI6CiAgICAgICAgdW5pdCA9IHVuaXQgKyAi5aSpIgogICAgbXNnID0gIuWcqCIgKyB1bml0ICsgIuWGhe+8jCIgKyB1bmFtZSArICLnmoTln5/lhoXmlofku7blr7zlh7rlpKflsI/otoXov4ciICsgc3RyKGxpbWl0KQogICAgaWYgc3UgPT0gIk0iIG9yIHN1ID09ICJtIjoKICAgICAgICBtc2cgPSBtc2cgKyAiTULvvIwiCiAgICBpZiBzdSA9PSAiRyIgb3Igc3UgPT0gImciOgogICAgICAgIG1zZyA9IG1zZyArICJHQu+8jCIKICAgIGlmIHN1ID09ICJUIiBvciBzdSA9PSAidCI6CiAgICAgICAgbXNnID0gbXNnICsgIlRC77yMIgoKICBpZiBsZW4obXNnKSA+IDA6CiAgICBldnRbImRldGFpbCJdID0gbXNnICsgIuivt+WPiuaXtui3n+i4quWvvOWHuuaWh+S7tueahOaVj+aEn+eoi+W6puaIlumHjeaWsOiwg+aVtOmYiOWAvOOAgiIKICAgIGV2dFsiZG9tYWluX2lkIl0gPSBkaWQKICAgIGV2dFsiY3JlYXRlX3RpbWUiXSA9IEdldFRpbWVTZWMoKQogICAgcmV0dXJuIGV2dApvdXRwdXQgPSBqc29uLmVuY29kZShjaGVjaygpKQ==
|
||||
Type: starlark
|
||||
sink:
|
||||
Cols:
|
||||
- create_time
|
||||
- detail
|
||||
- domain_id
|
||||
- message_type
|
||||
DB: mie
|
||||
Host: mysql.db-mysql:3306
|
||||
Pwd: L0hMysql.
|
||||
Table: message_content
|
||||
Type: mysql_sink
|
||||
User: root
|
||||
source:
|
||||
DB: mie
|
||||
Host: mysql.db-mysql:3306
|
||||
Pwd: L0hMysql.
|
||||
Table: evt_export_file_over
|
||||
Type: mysql_source
|
||||
User: root
|
||||
Sentry:
|
||||
TracesSampleRate: 1
|
||||
StaticURLPathPrefix:
|
||||
NetworkAppIcon: /user/avatar
|
||||
Storage:
|
||||
PublicFolderFileDir: /data/storage/public_folder_file
|
||||
TmpDir: /data/storage/tmp
|
||||
UploadedFilesDir: /data/storage/uploaded_files
|
||||
TranslationPath: translation.csv
|
||||
UpgradeCheckFilePath: /yizhisec/hs_nginx/resource/release_version_record.csv
|
||||
UserManagement:
|
||||
Host: user-service
|
||||
Port: 9013
|
||||
WatermarkServer:
|
||||
Host: hs-watermark
|
||||
Port: 9014
|
||||
Web:
|
||||
Host: 0.0.0.0
|
||||
Mode: release
|
||||
Port: 9129
|
||||
Web2:
|
||||
Host: 0.0.0.0
|
||||
Mode: release
|
||||
Port: 9024
|
||||
WebMessages:
|
||||
Host: 0.0.0.0
|
||||
Mode: release
|
||||
Port: 9025
|
||||
WorkDir: /yizhisec/client_server
|
||||
YosGuard:
|
||||
Host: 172.17.0.1
|
||||
Port: 7788`
|
||||
_upsert = `#!/bin/bash
|
||||
|
||||
kubectl create configmap config-client --namespace hsv2 --from-file=config.yml=./config.yml --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl rollout restart deployment client-deployment -n hsv2`
|
||||
)
|
||||
var (
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "app", "client")
|
||||
)
|
||||
|
||||
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)
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
logger.Debug("☑️ maker.AppClient: 开始构建 yaml 资源文件")
|
||||
content := []byte(fmt.Sprintf(resource.YAMLAppClient, replica))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), content, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppClient: 写入 deployment.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppClient: 开始 yaml 资源文件成功")
|
||||
|
||||
logger.Debug("☑️ maker.AppClient: 开始构建 config 文件")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "config.yml"), []byte(_config), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppClient: 写入 config.yml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppClient: 构建 config 文件成功")
|
||||
|
||||
logger.Debug("☑️ maker.AppClient: 开始构建 upsert.sh 脚本")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppClient: 写入 upsert.sh 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppClient: 构建 upsert.sh 脚本成功")
|
||||
|
||||
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
|
||||
}
|
||||
71
internal/controller/maker/app.front.go
Normal file
71
internal/controller/maker/app.front.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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, version string, vendor string, replica int) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(m.workdir, "app", "front")
|
||||
bs []byte
|
||||
_vendor = model.GetVendor(vendor)
|
||||
)
|
||||
|
||||
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, "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: 写入 deployment.user.yaml 失败: path = %s, err = %v", path, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Front: write deployment.user.yaml success, 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: 写入 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(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(location, "front.admin.tar")), WithImageForcePull(true)); err != nil {
|
||||
logger.Debug("❌ maker.Front: 拉取 front 管理镜像失败: %s, err = %v", _vendor.AppFrontAdminImageName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
120
internal/controller/maker/app.gateway.go
Normal file
120
internal/controller/maker/app.gateway.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppGateway(ctx context.Context, version string, replica int) error {
|
||||
const (
|
||||
_config = `Version: "3"
|
||||
Database:
|
||||
Elastic:
|
||||
Address: http://es-service.db-es:9200
|
||||
Mysql:
|
||||
Address: mysql.db-mysql:3306
|
||||
DBName: mie
|
||||
Password: L0hMysql.
|
||||
UserName: root
|
||||
Redis:
|
||||
Address: redis-master.db-redis:6379
|
||||
Password: HybridScope0xRed1s.
|
||||
Gateway:
|
||||
Cert:
|
||||
ClientCrt: /yizhisec/ssl/client.crt
|
||||
ClientKey: /yizhisec/ssl/client.key
|
||||
TokenFilePath: /etc/yizhisec/token
|
||||
Key:
|
||||
Token: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
|
||||
Log:
|
||||
Dir: ./log
|
||||
Level: 1
|
||||
Name: gateway_controller
|
||||
Sentry:
|
||||
TracesSampleRate: 1
|
||||
UserManagement:
|
||||
Host: user-service
|
||||
Port: 9013
|
||||
Web:
|
||||
Host: 0.0.0.0
|
||||
Mode: release
|
||||
Port: 9012
|
||||
WorkDir: /yizhisec/gateway_controller/workspace
|
||||
YosGuard:
|
||||
Host: 172.17.0.1
|
||||
Port: 7788`
|
||||
_upsert = `#!/bin/bash
|
||||
|
||||
kubectl create configmap config-gateway --namespace hsv2 --from-file=config.yml=./config.yml --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl rollout restart deployment gateway-deployment -n hsv2`
|
||||
)
|
||||
var (
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "app", "gateway")
|
||||
)
|
||||
|
||||
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)
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
logger.Debug("☑️ maker.AppGateway: 开始构建 yaml 资源文件")
|
||||
content := []byte(fmt.Sprintf(resource.YAMLAppGateway, replica))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), content, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppGateway: 写入 deployment.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppGateway: 开始 yaml 资源文件成功")
|
||||
|
||||
logger.Debug("☑️ maker.AppGateway: 开始构建 config 文件")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "config.yml"), []byte(_config), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppGateway: 写入 config.yml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppGateway: 构建 config 文件成功")
|
||||
|
||||
logger.Debug("☑️ maker.AppGateway: 开始构建 upsert.sh 脚本")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppGateway: 写入 upsert.sh 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppGateway: 构建 upsert.sh 脚本成功")
|
||||
|
||||
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
|
||||
}
|
||||
213
internal/controller/maker/app.mie.go
Normal file
213
internal/controller/maker/app.mie.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppMie(ctx context.Context, version string, replica int) error {
|
||||
const (
|
||||
_config = `Version: "3"
|
||||
BackupSeafile:
|
||||
Host: hs-resource-server
|
||||
Port: 19980
|
||||
account_manager:
|
||||
address: http://user-service:9013
|
||||
client_server:
|
||||
msg: http://client-service:9025
|
||||
api: http://client-service:9024
|
||||
web: http://client-service:9129
|
||||
backend_queue_names:
|
||||
request: request_que
|
||||
web: web_que
|
||||
backup_database_server:
|
||||
host: hs-backup-server
|
||||
port: 9349
|
||||
backup_seafile_server:
|
||||
host: hs-resource-server
|
||||
port: 19980
|
||||
clientPKG:
|
||||
android:
|
||||
client_pkg_dir: /data/storage/client_pkg/android
|
||||
client_pkg_file_path: /data/storage/client_pkg/android/SecureApplication-Client-Android.apk
|
||||
client_pkg_name: SecureApplication-Client-Android.apk
|
||||
client_version_file_path: /data/storage/client_pkg/android/android_version.json
|
||||
dir: /data/storage/client_pkg
|
||||
ios:
|
||||
client_pkg_dir: /data/storage/client_pkg/ios
|
||||
client_pkg_file_path: ''
|
||||
client_pkg_name: ''
|
||||
client_version_file_path: /data/storage/client_pkg/ios/ios_version.json
|
||||
linux:
|
||||
client_pkg_dir: /data/storage/client_pkg/linux
|
||||
client_pkg_file_path: /data/storage/client_pkg/linux/hscore-ubuntu-22.04-amd64.deb
|
||||
client_pkg_name: hscore-ubuntu-22.04-amd64.deb
|
||||
client_version_file_path: /data/storage/client_pkg/linux/linux_version.json
|
||||
mac:
|
||||
client_pkg_beta_file_path: /yizhisec/hs_nginx/resource/hybridscope-client-mac-beta.pkg
|
||||
client_pkg_beta_name: hybridscope-client-mac-beta.pkg
|
||||
client_pkg_dir: /data/storage/client_pkg/mac
|
||||
client_pkg_file_path: /yizhisec/hs_nginx/resource/hybridscope-client-mac.pkg
|
||||
client_pkg_name: hybridscope-client-mac.pkg
|
||||
client_version_file_path: /data/storage/client_pkg/mac/mac_version.json
|
||||
oem_dir: /yizhisec/hs_nginx/data/443/oem
|
||||
oem_file_path: /yizhisec/hs_nginx/data/443/oem/data.json
|
||||
windows:
|
||||
client_main_zip_name: app.7z
|
||||
client_pkg_cfg_file_name: login.conf
|
||||
client_pkg_dir: /data/storage/client_pkg/windows
|
||||
client_pkg_unzip_dir: package
|
||||
client_pkg_zip: /data/storage/client_pkg/windows/dsclient.zip
|
||||
client_zip_version: version
|
||||
databases:
|
||||
elasticsearch:
|
||||
host: es-service.db-es
|
||||
port: 9200
|
||||
ipdb:
|
||||
path: /etc/mie-server/ipdb/ip.ipdb
|
||||
mysql:
|
||||
db: mie
|
||||
host: mysql-cluster-mysql-master.db-mysql
|
||||
password: L0hMysql.
|
||||
port: 3306
|
||||
username: root
|
||||
redis:
|
||||
host: redis-master.db-redis
|
||||
password: HybridScope0xRed1s.
|
||||
port: 6379
|
||||
username: ''
|
||||
exe_root_license:
|
||||
path: /etc/mie-server/root.pem
|
||||
gateway_service:
|
||||
host: gateway-service.hsv2
|
||||
port: 9012
|
||||
host: 0.0.0.0
|
||||
license:
|
||||
version: 3
|
||||
license_init_conf: /etc/mie-server/server_license_init.conf
|
||||
public_key: /etc/mie-server/license/pub_key
|
||||
log_level: 20
|
||||
mqtt_server:
|
||||
host: emqx-service.db-emqx
|
||||
port: 1883
|
||||
port: 9002
|
||||
resource_server:
|
||||
address: http://hs-resource-server:19980
|
||||
secret_key: i345piuh48776lkjsdhfsdfljho
|
||||
sentry_dsn: null
|
||||
static_urlpath_prefix:
|
||||
network_app_icon: /user/avatar
|
||||
storage:
|
||||
avatar_dir: /data/storage/avatar
|
||||
mobile_app_dir: /yizhisec/hs_nginx
|
||||
network_app_icon: network_app
|
||||
patch_dir: /data/storage/patch
|
||||
public_folder_file_dir: /data/storage/public_folder_file
|
||||
share_file_storage: /data/storage/share_file
|
||||
sync_avatar_dir: sync
|
||||
tmp_dir: /data/storage/tmp
|
||||
upload_avatar_dir: local
|
||||
uploaded_files: /data/storage/uploaded_files
|
||||
token_key: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
|
||||
translation_path: /etc/mie-server/translation.csv
|
||||
yosguard_service:
|
||||
host: 172.17.0.1
|
||||
port: 7788
|
||||
ElinkLogin: true
|
||||
export_with_blind_watermark: true`
|
||||
_upsert = `#!/bin/bash
|
||||
|
||||
kubectl create configmap config-api --namespace hsv2 --from-file=conf.yml=./conf.yml --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
kubectl apply -f deployment-api.yaml
|
||||
kubectl apply -f deployment-sweeper.yaml
|
||||
kubectl apply -f deployment-worker.yaml
|
||||
kubectl apply -f deployment-cron.yaml
|
||||
|
||||
kubectl rollout restart deployment api-deployment -n hsv2`
|
||||
)
|
||||
var (
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "app", "mie")
|
||||
)
|
||||
|
||||
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)
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
logger.Debug("☑️ maker.AppMie: 写入 conf.yml 文件..., dest = %s", filepath.Join(workdir, "conf.yml"))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "conf.yml"), []byte(_config), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 写入 conf.yml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: 写入 conf.yml 文件成功, dest = %s", filepath.Join(workdir, "conf.yml"))
|
||||
|
||||
logger.Debug("☑️ maker.AppMie: 写入 deployment-api.yaml 文件..., dest = %s", filepath.Join(workdir, "deployment-api.yaml"))
|
||||
apiYaml := []byte(fmt.Sprintf(resource.YAMLAppMieAPI, replica))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment-api.yaml"), apiYaml, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 写入 deployment-api.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: 写入 deployment-api.yaml 文件成功, dest = %s", filepath.Join(workdir, "deployment-api.yaml"))
|
||||
|
||||
logger.Debug("☑️ maker.AppMie: 写入 deployment-sweeper.yaml 文件..., dest = %s", filepath.Join(workdir, "deployment-sweeper.yaml"))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment-sweeper.yaml"), resource.YAMLAppMieSweeper, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 写入 deployment-sweeper.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: 写入 deployment-sweeper.yaml 文件成功, dest = %s", filepath.Join(workdir, "deployment-sweeper.yaml"))
|
||||
|
||||
logger.Debug("☑️ maker.AppMie: 写入 deployment-worker.yaml 文件..., dest = %s", filepath.Join(workdir, "deployment-worker.yaml"))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment-worker.yaml"), resource.YAMLAppMieWorker, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 写入 deployment-worker.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: 写入 deployment-worker.yaml 文件成功, dest = %s", filepath.Join(workdir, "deployment-worker.yaml"))
|
||||
|
||||
logger.Debug("☑️ maker.AppMie: 写入 deployment-cron.yaml 文件..., dest = %s", filepath.Join(workdir, "deployment-cron.yaml"))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment-cron.yaml"), resource.YAMLAppMieCron, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 写入 deployment-cron.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: 写入 deployment-cron.yaml 文件成功, dest = %s", filepath.Join(workdir, "deployment-cron.yaml"))
|
||||
|
||||
logger.Debug("☑️ maker.AppMie: 写入 upsert.sh 文件..., dest = %s", filepath.Join(workdir, "upsert.sh"))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppMie: 写入 upsert.sh 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppMie: 写入 upsert.sh 文件成功, dest = %s", filepath.Join(workdir, "upsert.sh"))
|
||||
|
||||
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
|
||||
}
|
||||
176
internal/controller/maker/app.nginx.go
Normal file
176
internal/controller/maker/app.nginx.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func (m *maker) AppNginx(ctx context.Context, version string, vendor string, replica int, inlcudeImage bool) error {
|
||||
const (
|
||||
_upsert = `#!/bin/bash
|
||||
|
||||
kubectl create configmap nginx-main --namespace hsv2 --from-file=nginx.conf=./conf/nginx.conf --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap nginx-user --namespace hsv2 --from-file=user.conf=./conf/user.conf --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap nginx-gateway --namespace hsv2 --from-file=gateway.conf=./conf/gateway.conf --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap nginx-web --namespace hsv2 --from-file=web.conf=./conf/web.conf --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap nginx-client --namespace hsv2 --from-file=client.conf=./conf/client.conf --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap nginx-common --namespace hsv2 --from-file=common.conf=./conf/common.conf --dry-run=client -o yaml | kubectl apply -f -
|
||||
%s
|
||||
|
||||
kubectl create configmap ssl-ca-crt --namespace hsv2 --from-file=ca.crt=./ssl/ca.crt --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap ssl-ffdhe2048 --namespace hsv2 --from-file=ffdhe2048.txt=./ssl/ffdhe2048.txt --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
kubectl create configmap ssl-server-crt --namespace hsv2 --from-file=server.crt=./ssl/server.crt --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap ssl-server-key --namespace hsv2 --from-file=server.key=./ssl/server.key --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
kubectl create configmap ssl-mqtt-crt --namespace hsv2 --from-file=mqtt.server.crt=./ssl/mqtt.server.crt --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap ssl-mqtt-key --namespace hsv2 --from-file=mqtt.server.key=./ssl/mqtt.server.key --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
kubectl create configmap ssl-client-server-crt --namespace hsv2 --from-file=client.server.crt=./ssl/client.server.crt --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap ssl-client-server-key --namespace hsv2 --from-file=client.server.key=./ssl/client.server.key --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
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 deployment.yaml
|
||||
kubectl rollout restart deployment nginx-deployment -n hsv2`
|
||||
)
|
||||
var (
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "app", "nginx")
|
||||
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, version = %s, vendor = %s, workdir = %s", version, vendor, 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")
|
||||
|
||||
logger.Debug(" ☑️ maker.AppNginx: 创建 conf 与 ssl 子目录")
|
||||
if err = os.MkdirAll(confDir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 创建 conf 目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
if err = os.MkdirAll(sslDir, 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 创建 ssl 目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: 创建 conf 与 ssl 子目录成功")
|
||||
|
||||
// 写入 nginx 配置文件到 conf 子目录(列表 + for 循环)
|
||||
logger.Debug(" ☑️ maker.AppNginx: 写入 nginx 配置文件到 conf 子目录")
|
||||
confFiles := []struct {
|
||||
name string
|
||||
content []byte
|
||||
}{
|
||||
{"nginx.conf", resource.NGINXMain},
|
||||
{"user.conf", resource.NGINXUser},
|
||||
{"gateway.conf", resource.NGINXGateway},
|
||||
{"web.conf", resource.NGINXWeb},
|
||||
{"client.conf", resource.NGINXClient},
|
||||
{"common.conf", resource.NGINXCommon},
|
||||
}
|
||||
|
||||
// vendor != elink, append seafile conf
|
||||
if vendor != "elink" {
|
||||
confFiles = append(confFiles, struct {
|
||||
name string
|
||||
content []byte
|
||||
}{
|
||||
"seafile.conf", resource.NGINXSeafile,
|
||||
})
|
||||
}
|
||||
|
||||
for _, f := range confFiles {
|
||||
dest := filepath.Join(confDir, f.name)
|
||||
if err = os.WriteFile(dest, f.content, 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 写入 %s 失败: %v", f.name, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: 写入 %s 成功, dest = %s", f.name, dest)
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: 写入 nginx 配置文件成功")
|
||||
|
||||
// 写入 ssl 文件到 ssl 子目录
|
||||
logger.Debug(" ☑️ maker.AppNginx: 写入 SSL 证书与密钥到 ssl 子目录")
|
||||
sslFiles := []struct{ name, content string }{
|
||||
{"ffdhe2048.txt", resource.SSLFFDHE2048},
|
||||
{"ca.crt", resource.SSLCaCrt},
|
||||
{"server.crt", resource.SSLServerCrt},
|
||||
{"server.key", resource.SSLServerKey},
|
||||
{"mqtt.server.crt", resource.SSLMQTTServerCrt},
|
||||
{"mqtt.server.key", resource.SSLMQTTServerKey},
|
||||
{"client.server.crt", resource.SSLClientServerCrt},
|
||||
{"client.server.key", resource.SSLClientServerKey},
|
||||
{"web.server.crt", resource.SSLWebServerCrt},
|
||||
{"web.server.key", resource.SSLWebServerKey},
|
||||
}
|
||||
for _, f := range sslFiles {
|
||||
dest := filepath.Join(sslDir, f.name)
|
||||
if err = os.WriteFile(dest, []byte(f.content), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppNginx: 写入 %s 失败: %v", f.name, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: 写入 %s 成功, dest = %s", f.name, dest)
|
||||
}
|
||||
|
||||
// write nginx deployment yaml
|
||||
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: 写入 deployment.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppNginx: 写入 deployment.yaml 成功, dest = %s", dest)
|
||||
|
||||
// write nginx upsert script
|
||||
dest = filepath.Join(workdir, "upsert.sh")
|
||||
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)
|
||||
|
||||
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
|
||||
}
|
||||
162
internal/controller/maker/app.oem.go
Normal file
162
internal/controller/maker/app.oem.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"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, version string, vendor string, replica int) error {
|
||||
const (
|
||||
_nginx = `user root;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 512;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
client_max_body_size 10M;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
root /data;
|
||||
location / {
|
||||
try_files \$uri \$uri/ =404;
|
||||
}
|
||||
}
|
||||
}`
|
||||
_dockerfile = `FROM hub.yizhisec.com/external/nginx:1.29.1-alpine3.22
|
||||
|
||||
WORKDIR /data
|
||||
COPY oem /data/oem
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]`
|
||||
_image = "hub.yizhisec.com/hybridscope/v2/oem-%s:latest"
|
||||
)
|
||||
var (
|
||||
err error
|
||||
_vendor = model.GetVendor(vendor)
|
||||
workdir = filepath.Join(m.workdir, "app", "oem")
|
||||
output []byte
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.AppOEM: 开始构建 oem[%s], version = %s, ,workdir = %s", vendor, version, workdir)
|
||||
|
||||
if _vendor == nil {
|
||||
supported := model.GetVendorNames()
|
||||
logger.Debug("❌ maker.AppOEM: vendor not supported, 支持的 vendor 有: %v", supported)
|
||||
return fmt.Errorf("请检查 vendor 是否正确, 支持的 vendor 有: %v", supported)
|
||||
}
|
||||
|
||||
// 1. make workdir
|
||||
logger.Debug("☑️ maker.AppOEM: 开始创建 workdir = %s", workdir)
|
||||
_ = os.RemoveAll(workdir)
|
||||
if err = os.MkdirAll(workdir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppOEM: workdir 创建成功 = %s", workdir)
|
||||
|
||||
// 2. download oem.tar.gz
|
||||
logger.Debug("☑️ maker.AppOEM: 开始下载 oem[%s] url = %s", vendor, _vendor)
|
||||
if err = archiver.DownloadAndExtract(ctx, _vendor.OEMUrl, workdir); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: oem[%s] tar 下载失败, url = %s, err = %v", vendor, _vendor.OEMUrl, err)
|
||||
return err
|
||||
}
|
||||
if _vendor.OEMDir != "oem" {
|
||||
if err = os.Rename(
|
||||
filepath.Join(workdir, _vendor.OEMDir),
|
||||
filepath.Join(workdir, "oem"),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: oem[%s] tar 重命名失败, err = %v", vendor, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
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")
|
||||
if err = os.WriteFile(
|
||||
filepath.Join(workdir, "nginx.conf"),
|
||||
[]byte(_nginx),
|
||||
0o644,
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: nginx.conf 写入失败, err = %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppOEM: nginx.conf 写入成功")
|
||||
|
||||
// 4. write Dockerfile
|
||||
logger.Debug("☑️ maker.AppOEM: 开始写入 Dockerfile")
|
||||
if err = os.WriteFile(
|
||||
filepath.Join(workdir, "Dockerfile"),
|
||||
[]byte(_dockerfile),
|
||||
0o644,
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: Dockerfile 写入失败, err = %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppOEM: Dockerfile 写入成功")
|
||||
|
||||
// 5. build docker image
|
||||
imageName := fmt.Sprintf(_image, vendor)
|
||||
logger.Debug("☑️ maker.AppOEM: 开始构建 docker image = %s", imageName)
|
||||
// docker build -t <image_name> -f <workdir/Dockerfile> <workdir>
|
||||
_cmd := exec.CommandContext(ctx, "docker", "build", "-t", imageName, "-f", filepath.Join(workdir, "Dockerfile"), workdir)
|
||||
if output, err = _cmd.CombinedOutput(); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: docker image 构建失败, err = %v, output = %s", err, string(output))
|
||||
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"))
|
||||
|
||||
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
|
||||
}
|
||||
logger.Debug("✅ maker.AppOEM: docker image 保存成功, image = %s", imageName)
|
||||
|
||||
// 7. render oem.yaml
|
||||
logger.Debug("☑️ maker.AppOEM: 开始渲染 deployment.yaml")
|
||||
oemYAML := fmt.Sprintf(resource.YAMLAppOEM, replica, imageName)
|
||||
if err = os.WriteFile(
|
||||
filepath.Join(workdir, "deployment.yaml"),
|
||||
[]byte(oemYAML),
|
||||
0o644,
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.AppOEM: deployment.yaml 写入失败, err = %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppOEM: deployment.yaml 写入成功")
|
||||
|
||||
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
|
||||
}
|
||||
@@ -2,8 +2,144 @@ package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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:
|
||||
Elastic:
|
||||
Address: http://es-service.db-es:9200
|
||||
IPDB:
|
||||
Path: /etc/hs_user_management/ipdb/ip.ipdb
|
||||
Mysql:
|
||||
Address: mysql-cluster-mysql-master.db-mysql:3306
|
||||
DBName: mie
|
||||
Password: L0hMysql.
|
||||
UserName: root
|
||||
Redis:
|
||||
Address: redis-master.db-redis:6379
|
||||
Password: HybridScope0xRed1s.
|
||||
DisabledFeatureFilePath: /etc/yizhisec/disabled_features
|
||||
EnableTenant: false
|
||||
Key:
|
||||
Token: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
|
||||
LicensePubKey: /etc/yizhisec/license/pub_key
|
||||
Log:
|
||||
Dir: ./log
|
||||
Level: 1
|
||||
Name: hs_user_management
|
||||
Sentry:
|
||||
TracesSampleRate: 1
|
||||
Sso:
|
||||
DingTalk:
|
||||
ApiHost: oapi.dingtalk.com
|
||||
LoginUrl: https://oapi.dingtalk.com/connect/qrconnect
|
||||
Feishu:
|
||||
ApiHost: open.feishu.cn
|
||||
LoginUrl: https://open.feishu.cn/open-apis/authen/v1/index
|
||||
Proxy:
|
||||
CallbackHost: hssso.yizhisec.com:33443
|
||||
Cert:
|
||||
ClientCrt: /etc/hs_user_management/proxy/certs/client.crt
|
||||
ClientKey: /etc/hs_user_management/proxy/certs/client.key
|
||||
ServiceHost: hssso.yizhisec.com:33444
|
||||
RedirectPath:
|
||||
BoundFailed: /#/accountSettings/thirdAccount
|
||||
BoundSuccess: /#/accountSettings/thirdAccount
|
||||
LoginFailed: /#/thirdError
|
||||
LoginNeedBoundUser: /#/bind
|
||||
LoginSuccess: /#/
|
||||
WorkWeiXin:
|
||||
ApiHost: qyapi.weixin.qq.com
|
||||
LoginUrl: https://login.work.weixin.qq.com/wwlogin/sso/login
|
||||
Storage:
|
||||
Avatar:
|
||||
ADSyncDir: ad
|
||||
Base: /data/storage/avatar
|
||||
LDAPSyncDir: ldap
|
||||
LocalDir: local
|
||||
SyncDir: sync
|
||||
TranslationPath: translation.csv
|
||||
Web:
|
||||
Host: 0.0.0.0
|
||||
Mode: release
|
||||
Port: 9013
|
||||
WorkDir: /yizhisec/hs_user_management/workspace
|
||||
YosGuard:
|
||||
Host: 172.17.0.1
|
||||
Port: 7788
|
||||
ElinkLogin: false`
|
||||
_upsert = `#!/bin/bash
|
||||
|
||||
kubectl create configmap config-user --namespace hsv2 --from-file=config.yml=./config.yml --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl rollout restart deployment user-deployment -n hsv2`
|
||||
)
|
||||
var (
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "app", "user")
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
logger.Debug("☑️ maker.AppUser: 开始构建 yaml 资源文件")
|
||||
content := []byte(fmt.Sprintf(resource.YAMLAppUser, replica))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), []byte(content), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppUser: 写入 deployment.yaml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppUser: 开始 yaml 资源文件成功")
|
||||
|
||||
// 写入 config.yml
|
||||
logger.Debug("☑️ maker.AppUser: 开始构建 config 文件")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "config.yml"), []byte(_config), 0644); err != nil {
|
||||
logger.Debug("❌ maker.AppUser: 写入 config.yml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppUser: 构建 config 文件成功")
|
||||
|
||||
logger.Debug("☑️ maker.AppUser: 开始构建 upsert.sh 脚本")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
|
||||
logger.Debug("❌ maker.AppUser: 写入 upsert.sh 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.AppUser: 构建 upsert.sh 脚本成功")
|
||||
|
||||
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,25 +4,25 @@ import (
|
||||
"context"
|
||||
"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"
|
||||
)
|
||||
|
||||
func (m *maker) Binary(ctx context.Context) error {
|
||||
func (m *maker) K0s(ctx context.Context) error {
|
||||
var (
|
||||
tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/k8s-bin.tar"
|
||||
binDir = filepath.Join(opt.Cfg.Make.Dir, "dependency")
|
||||
tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/k8s-bin.tar"
|
||||
location = filepath.Join(m.workdir, "dependency", "k0s")
|
||||
)
|
||||
|
||||
logger.Info("☑️ 开始准备 k8s 二进制文件...")
|
||||
logger.Info("☑️ 开始准备 k0s...")
|
||||
logger.Debug("下载地址: %s", tarURL)
|
||||
logger.Debug("目标目录: %s", binDir)
|
||||
logger.Debug("目标目录: %s", location)
|
||||
|
||||
if err := archiver.DownloadAndExtract(
|
||||
ctx,
|
||||
tarURL,
|
||||
binDir,
|
||||
location,
|
||||
archiver.WithInsecureSkipVerify(),
|
||||
archiver.WithGzipCompression(true),
|
||||
); err != nil {
|
||||
@@ -30,7 +30,32 @@ func (m *maker) Binary(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ 准备 k8s 二进制文件成功!!!")
|
||||
logger.Debug("☑️ maker.K0s: 开始准备相关镜像...")
|
||||
var images = []*model.Image{
|
||||
{Name: "quay.io/k0sproject/apiserver-network-proxy-agent:v0.32.0", Fallback: "hub.yizhisec.com/external/apiserver-network-proxy-agent:v0.32.0", Save: "k0s.apiserver-network-proxy-agent.tar"},
|
||||
{Name: "quay.io/k0sproject/cni-node:1.7.1-k0s.0", Fallback: "", Save: "k0s.cni-node.tar"},
|
||||
{Name: "quay.io/k0sproject/coredns:1.12.2", Fallback: "", Save: "k0s.coredns.tar"},
|
||||
{Name: "quay.io/k0sproject/kube-proxy:v1.33.4", Fallback: "", Save: "k0s.kube-proxy.tar"},
|
||||
{Name: "quay.io/k0sproject/kube-router:v2.5.0-iptables1.8.11-0", Fallback: "", Save: "k0s.kube-router.tar"},
|
||||
{Name: "quay.io/k0sproject/metrics-server:v0.7.2-0", Fallback: "", Save: "k0s.metrics-server.tar"},
|
||||
{Name: "quay.io/k0sproject/pause:3.10.1", Fallback: "", Save: "k0s.pause.tar"},
|
||||
}
|
||||
for _, image := range images {
|
||||
opts := []ImageOpt{
|
||||
WithImageFallback(image.Fallback),
|
||||
WithImageSave(filepath.Join(location, image.Save)),
|
||||
WithImageForcePull(image.Force),
|
||||
}
|
||||
|
||||
if err := m.Image(ctx, image.Name, opts...); err != nil {
|
||||
logger.Error("❌ maker.K0s: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
|
||||
logger.Debug("❌ maker.K0s: 获取镜像失败: %s, %v", image.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.Debug("✅ maker.K0s: 准备相关镜像成功!!!")
|
||||
|
||||
logger.Info("✅ 准备 k0s 成功!!!")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ func (m *maker) DependencyCheck(ctx context.Context) error {
|
||||
return fmt.Errorf("docker 命令未找到: %w", err)
|
||||
}
|
||||
|
||||
if err := checkCommand(ctx, "7z"); err != nil {
|
||||
return fmt.Errorf("7z 命令未找到: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
184
internal/controller/maker/client.pkg.go
Normal file
184
internal/controller/maker/client.pkg.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
type clientPKGDownload struct {
|
||||
URL string
|
||||
Name string
|
||||
}
|
||||
|
||||
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 {
|
||||
return func(o *clientPKGOption) {
|
||||
if url == "" || location == "" {
|
||||
return
|
||||
}
|
||||
|
||||
o.Downloads = append(o.Downloads, &clientPKGDownload{
|
||||
URL: url,
|
||||
Name: location,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func WithClientPKGCMDs(cmds ...string) ClientPKGOption {
|
||||
return func(o *clientPKGOption) {
|
||||
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/hsv2/base/nginx:latest
|
||||
|
||||
RUN mkdir -p /data
|
||||
COPY version.json /data/version.json
|
||||
|
||||
%s
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
`
|
||||
)
|
||||
|
||||
if !lo.Contains([]string{"mac", "win", "linux"}, _os) {
|
||||
return fmt.Errorf("invalid os: %s", _os)
|
||||
}
|
||||
|
||||
var (
|
||||
o = &clientPKGOption{
|
||||
Downloads: []*clientPKGDownload{},
|
||||
CMDs: []string{},
|
||||
}
|
||||
err error
|
||||
location = filepath.Join(m.workdir, "client", _os)
|
||||
_file string
|
||||
_content string
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
logger.Info("☑️ maker.ClientPKG: start build client pkg, os = %s, version = %s, location = %s", _os, _version, location)
|
||||
|
||||
if err = os.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)
|
||||
if os.WriteFile(_file, []byte(_content), 0644); err != nil {
|
||||
logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", _file, err.Error())
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.ClientPKG: write file success, file = %s", _file)
|
||||
|
||||
lines := lo.Map(o.Downloads, func(d *clientPKGDownload, index int) string {
|
||||
return fmt.Sprintf("wget -O /data/%s %s", d.Name, d.URL)
|
||||
})
|
||||
|
||||
if len(o.CMDs) > 0 {
|
||||
lines = append(lines, o.CMDs...)
|
||||
}
|
||||
|
||||
_file = filepath.Join(location, "Dockerfile")
|
||||
_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())
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.ClientPKG: write file success, file = %s", _file)
|
||||
|
||||
imgName := fmt.Sprintf("hub.yizhisec.com/hsv2/client/%s:%s", _os, _version)
|
||||
logger.Debug("☑️ maker.ClientPKG: build docker image, os = %s, version = %s, full_name = %s", _os, _version, imgName)
|
||||
_cmd := fmt.Sprintf("docker build --network host -t %s -f Dockerfile .", imgName)
|
||||
if err = m.RunCommand(ctx, location, _cmd); err != nil {
|
||||
logger.Debug("❌ maker.ClientPKG: run command failed, cmd = %s, err = %s", _cmd, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
imgLocation := filepath.Join(location, _os+".tar")
|
||||
logger.Debug("☑️ maker.ClientPKG: save img to %s", imgLocation)
|
||||
_cmd = fmt.Sprintf("docker save hub.yizhisec.com/hsv2/client/%s:%s -o %s", _os, _version, imgLocation)
|
||||
if err = m.RunCommand(ctx, location, _cmd); err != nil {
|
||||
logger.Debug("❌ maker.ClientPKG: run command failed, cmd = %s, err = %s", _cmd, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
imgNameInCluster := fmt.Sprintf("10.96.123.45:80/hsv2/client/%s:%s", _os, _version)
|
||||
deployment := filepath.Join(location, "deployment.yaml")
|
||||
_content = strings.ReplaceAll(
|
||||
fmt.Sprintf(
|
||||
resource.YAMLClientPKG,
|
||||
1, // replica 先默认 1 就够了
|
||||
imgNameInCluster,
|
||||
),
|
||||
"__os__",
|
||||
_os,
|
||||
)
|
||||
if err = os.WriteFile(deployment, []byte(_content), 0644); err != nil {
|
||||
logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", deployment, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
37
internal/controller/maker/command.go
Normal file
37
internal/controller/maker/command.go
Normal file
@@ -0,0 +1,37 @@
|
||||
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 {
|
||||
if len(_cmds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Debug("maker.RunCommand: dir = %s, cmds = %v", dir, _cmds)
|
||||
|
||||
for _, cmdStr := range _cmds {
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", cmdStr)
|
||||
|
||||
if dir != "" {
|
||||
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, output = %s", cmdStr, dir, err, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -7,11 +7,10 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/samber/lo"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -86,6 +85,11 @@ EEuYRYXDouPJ1F//rYraSoJ4mtaipB6z1A==
|
||||
-----END EC PRIVATE KEY-----`
|
||||
upsert = `#!/bin/bash
|
||||
|
||||
# Generate server_license_init.conf
|
||||
uuid=$(cat /proc/sys/kernel/random/uuid)
|
||||
now=$(date +%s)
|
||||
echo "{\"uuid\": \"$uuid\", \"install_time\": $now}" > ./server_license_init.conf
|
||||
|
||||
kubectl create configmap config-token --namespace hsv2 --from-file=token=./token --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap config-license-init --namespace hsv2 --from-file=server_license_init.conf=./server_license_init.conf --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap config-oem-data --namespace hsv2 --from-file=data.json=./oem_data.json --dry-run=client -o yaml | kubectl apply -f -
|
||||
@@ -96,12 +100,22 @@ kubectl create configmap ssl-client-key --namespace hsv2 --from-file=client.key=
|
||||
kubectl create configmap ssl-client-ca-crt --namespace hsv2 --from-file=client.ca.crt=./ssl_client_ca.crt --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap ssl-client-ca-key --namespace hsv2 --from-file=client.ca.key=./ssl_client_ca.key --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap ssl-web-crt --namespace hsv2 --from-file=web.server.crt=./ssl_web.crt --dry-run=client -o yaml | kubectl apply -f -
|
||||
`
|
||||
_version_yaml = `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: config-version
|
||||
namespace: hsv2
|
||||
data:
|
||||
version.txt: |
|
||||
__version__
|
||||
`
|
||||
)
|
||||
|
||||
var (
|
||||
err error
|
||||
dir = filepath.Join(opt.Cfg.Make.Dir, "configmap")
|
||||
dir = filepath.Join(m.workdir, "configmap")
|
||||
vendorUrlMap = map[string]string{
|
||||
"standard": "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem.tar.gz",
|
||||
"elink": "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_csgElink.tar.gz",
|
||||
@@ -204,6 +218,12 @@ kubectl create configmap ssl-web-crt --namespace hsv2 --from-file=web.server.crt
|
||||
}
|
||||
logger.Debug("✅ maker.ConfigMap: 写入 ssl_client_ca.key 文件: %s 成功", filepath.Join(dir, "ssl_client_ca.key"))
|
||||
|
||||
if err = os.WriteFile(filepath.Join(dir, "version.yaml"), []byte(_version_yaml), 0644); err != nil {
|
||||
logger.Debug("❌ maker.ConfigMap: 写入 version.yaml 文件: %s 失败, 错误: %v", filepath.Join(dir, "version.yaml"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.ConfigMap: 写入 version.yaml 文件: %s 成功", filepath.Join(dir, "version.yaml"))
|
||||
|
||||
// upsert configmap
|
||||
logger.Debug("☑️ maker.ConfigMap: 执行 upsert 脚本: %s", filepath.Join(dir, "upsert.sh"))
|
||||
if err = os.WriteFile(filepath.Join(dir, "upsert.sh"), []byte(upsert), 0755); err != nil {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/archiver"
|
||||
)
|
||||
|
||||
func (m *maker) Deb(ctx context.Context) error {
|
||||
var (
|
||||
tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/deb/docker.tar.gz"
|
||||
binDir = filepath.Join(opt.Cfg.Make.Dir, "dependency", "deb")
|
||||
)
|
||||
|
||||
logger.Info("☑️ 开始准备 deb(docker) 文件...")
|
||||
logger.Debug("下载地址: %s", tarURL)
|
||||
logger.Debug("目标目录: %s", binDir)
|
||||
|
||||
if err := archiver.DownloadAndExtract(
|
||||
ctx,
|
||||
tarURL,
|
||||
binDir,
|
||||
archiver.WithInsecureSkipVerify(),
|
||||
archiver.WithGzipCompression(true),
|
||||
); err != nil {
|
||||
logger.Info("❌ 下载并解压 deb(docker) 文件失败")
|
||||
logger.Debug("下载并解压 deb(docker) 文件失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ 准备 deb(docker) 文件成功!!!")
|
||||
return nil
|
||||
|
||||
}
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ func WithElasticMemRate(memRate int) ElasticOpt {
|
||||
|
||||
func WithElasticStorageGi(storage string) ElasticOpt {
|
||||
return func(o *elasticOpt) {
|
||||
if matched, _ := regexp.MatchString(`^\d+(\.\d+)?[EPTGMK]i?$`, storage); matched {
|
||||
if opt.StorageSizeReg.MatchString(storage) {
|
||||
o.Storage = storage
|
||||
}
|
||||
}
|
||||
@@ -66,8 +66,8 @@ RUN chmod +x /data/create_index.sh
|
||||
var (
|
||||
err error
|
||||
output []byte
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "elastic")
|
||||
helperTarLocation = filepath.Join(opt.Cfg.Make.Dir, "dependency", "image", "es-init-helper.alpine-3.22.2.tar")
|
||||
location = filepath.Join(m.workdir, "dependency", "elastic")
|
||||
helperTarLocation = filepath.Join(m.workdir, "dependency", "image", "es-init-helper.alpine-3.22.2.tar")
|
||||
)
|
||||
|
||||
opt := &elasticOpt{
|
||||
@@ -179,6 +179,28 @@ RUN chmod +x /data/create_index.sh
|
||||
}
|
||||
logger.Debug("✅ maker.Elastic: 写入 kibana.yaml 文件成功")
|
||||
|
||||
logger.Debug("☑️ maker.Elastic: 开始获取所需镜像...")
|
||||
|
||||
var images = []*model.Image{
|
||||
{Name: "hub.yizhisec.com/external/elasticsearch:7.17.28", Fallback: "", Save: "elasticsearch.7.17.28.tar"},
|
||||
{Name: "hub.yizhisec.com/external/kibana:7.17.28", Fallback: "", Save: "kibana.7.17.28.tar"},
|
||||
}
|
||||
|
||||
for _, image := range images {
|
||||
opts := []ImageOpt{
|
||||
WithImageFallback(image.Fallback),
|
||||
WithImageSave(filepath.Join(location, image.Save)),
|
||||
WithImageForcePull(image.Force),
|
||||
}
|
||||
|
||||
if err := m.Image(ctx, image.Name, opts...); err != nil {
|
||||
logger.Error("❌ maker.Elastic: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
|
||||
logger.Debug("❌ maker.Elastic: 获取镜像失败: %s, %v", image.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.Debug("✅ maker.Elastic: 获取所需镜像成功!!!")
|
||||
|
||||
logger.Info("✅ maker.Elastic: 构建 elastic(es) 依赖成功!!!")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@ 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"
|
||||
)
|
||||
|
||||
func (m *maker) EMQX(ctx context.Context) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "emqx")
|
||||
location = filepath.Join(m.workdir, "dependency", "emqx")
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.EMQX: 开始构建 emqx(mqtt) 依赖...")
|
||||
@@ -32,6 +32,14 @@ func (m *maker) EMQX(ctx context.Context) error {
|
||||
}
|
||||
logger.Debug("✅ maker.EMQX: 写入 emqx.yaml 文件成功, dest = %s", filepath.Join(location, "emqx.yaml"))
|
||||
|
||||
logger.Debug("☑️ maker.EMQX: 开始获取所需镜像...")
|
||||
var img = &model.Image{Name: "hub.yizhisec.com/external/emqx:5.1", Fallback: "", Save: "emqx.5.1.tar"}
|
||||
if err = m.Image(ctx, img.Name, WithImageFallback(img.Fallback), WithImageSave(filepath.Join(location, img.Save))); err != nil {
|
||||
logger.Debug("❌ maker.EMQX: 获取镜像失败: %s, %v", img.Name, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.EMQX: 获取所需镜像成功!!!")
|
||||
|
||||
logger.Info("✅ maker.EMQX: 构建 emqx(mqtt) 依赖成功!!!")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,15 +6,15 @@ 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"
|
||||
)
|
||||
|
||||
func (m *maker) Flannel(ctx context.Context, mode string) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "flannel")
|
||||
location = filepath.Join(m.workdir, "dependency", "flannel")
|
||||
)
|
||||
|
||||
logger.Info("☑️ 开始构建 flannel 资源...")
|
||||
@@ -34,6 +34,27 @@ func (m *maker) Flannel(ctx context.Context, mode string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var images = []*model.Image{
|
||||
{Name: "ghcr.io/flannel-io/flannel:v0.27.4", Fallback: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/flannel-io/flannel:v0.27.4", Save: "flannel.tar"},
|
||||
{Name: "ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1", Fallback: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1", Save: "flannel-cni-plugin.tar"},
|
||||
}
|
||||
|
||||
logger.Debug("MakeFlannel: 开始获取镜像...")
|
||||
for _, image := range images {
|
||||
opts := []ImageOpt{
|
||||
WithImageFallback(image.Fallback),
|
||||
WithImageSave(filepath.Join(location, image.Save)),
|
||||
WithImageForcePull(image.Force),
|
||||
}
|
||||
|
||||
if err := m.Image(ctx, image.Name, opts...); err != nil {
|
||||
logger.Error("❌ MakeFlannel: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
|
||||
logger.Debug("❌ MakeFlannel: 获取镜像失败: %s, %v", image.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.Debug("MakeFlannel: 获取镜像成功!!!")
|
||||
|
||||
logger.Info("✅ 构建 flannel 成功!!!")
|
||||
|
||||
return nil
|
||||
|
||||
@@ -5,20 +5,22 @@ import (
|
||||
"os"
|
||||
"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/resource"
|
||||
)
|
||||
|
||||
func (m *maker) HSNet(ctx context.Context) error {
|
||||
const (
|
||||
service = `[Unit]
|
||||
_service = `[Unit]
|
||||
Description=hs-net Container Service
|
||||
Documentation=https://docs.containerd.io
|
||||
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
|
||||
|
||||
# 拉取最新镜像(按需启用/注释)
|
||||
@@ -28,9 +30,7 @@ ExecStartPre=-/usr/local/bin/k0s ctr -n hs-net container rm hs-net
|
||||
ExecStart=/usr/local/bin/k0s ctr -n hs-net run \
|
||||
--net-host \
|
||||
--privileged \
|
||||
--cgroup host \
|
||||
--env LD_LIBRARY_PATH=/yizhisec/hs_net \
|
||||
--env RUSTFLAGS="-C target-cpu=nehalem" \
|
||||
--env RUST_BACKTRACE=1 \
|
||||
--mount type=bind,src=/etc/localtime,dst=/etc/localtime,options=rbind:ro \
|
||||
--mount type=bind,src=/etc/hosts,dst=/etc/hosts,options=rbind:ro \
|
||||
@@ -43,11 +43,11 @@ ExecStart=/usr/local/bin/k0s ctr -n hs-net run \
|
||||
--mount type=bind,src=/yizhisec/hs_net/conf,dst=/etc/hs_net,options=rbind:rw \
|
||||
hub.yizhisec.com/hybridscope/hsnet:release_2.1.0-std hs-net
|
||||
|
||||
# --cgroup host \
|
||||
# --env RUSTFLAGS="-C target-cpu=nehalem" \
|
||||
# 重启策略
|
||||
Restart=on-failure
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
StartLimitInterval=60s
|
||||
StartLimitBurst=5
|
||||
|
||||
# 资源限制(按需调整)
|
||||
MemoryLimit=2G
|
||||
@@ -59,24 +59,311 @@ 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]
|
||||
WantedBy=multi-user.target`
|
||||
_conf_out = `log:
|
||||
level: info
|
||||
controller:
|
||||
protocol: https
|
||||
registerHost: hs-gateway-register-controller
|
||||
host: hs-gateway-controller
|
||||
port: 443
|
||||
tokenFilePath: /etc/yizhisec/token
|
||||
registerRetry: 30
|
||||
wg:
|
||||
private_key: qPfOaNKrV11kzaGQiNQNyiu6wMQGUpIM+/xqboVAnng=
|
||||
private_network: 246.0.0.1/8
|
||||
listen_port: 23209
|
||||
mtu: 1300
|
||||
obf_key: 0
|
||||
keep_alive: 61
|
||||
tun_name: wg_tun
|
||||
yosGuard:
|
||||
host: __ip__
|
||||
port: 7788
|
||||
mqtt:
|
||||
protocol: tls
|
||||
host: mqtt.yizhisec.com
|
||||
port: '443'
|
||||
cert: /yizhisec/hs_net/conf/ssl/mqtt.client.crt
|
||||
key: /yizhisec/hs_net/conf/ssl/mqtt.client.key
|
||||
keep_alive: 60
|
||||
paseto_key: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
|
||||
resource_server: hs-gateway-controller
|
||||
dns_cache:
|
||||
Address: 127.0.0.1:9028
|
||||
gatewayVersionFile: /etc/yizhisec/gateway_version.json
|
||||
lastVersion: null
|
||||
workDir: /yizhisec/hs_net/workspace
|
||||
eth_names: []
|
||||
tag: ''
|
||||
cluster_mock: ''
|
||||
openobserve_uri: ''
|
||||
tcp_mode_disable: false
|
||||
`
|
||||
_conf_in = `{
|
||||
"LogLevel": "info",
|
||||
"LogFile": "/yizhisec/hs_net/workspace/log/wireguard",
|
||||
"DnsVirtNetwork": null,
|
||||
"DnsVirtNetworkV6": null,
|
||||
"Foreground": false,
|
||||
"WithPprof": false,
|
||||
"DnsCache": {
|
||||
"Address": "127.0.0.1:9028"
|
||||
},
|
||||
"log": {
|
||||
"level": "info"
|
||||
},
|
||||
"yosGuard": {
|
||||
"host": "__ip__",
|
||||
"port": 7788
|
||||
},
|
||||
"controller": {
|
||||
"protocol": "https",
|
||||
"host": "hs-gateway-controller",
|
||||
"registerHost": "hs-gateway-register-controller",
|
||||
"port": 443,
|
||||
"registerRetry": 30,
|
||||
"tokenFilePath": "/etc/yizhisec/token"
|
||||
},
|
||||
"wg": {
|
||||
"private_key": "qPfOaNKrV11kzaGQiNQNyiu6wMQGUpIM+/xqboVAnng=",
|
||||
"private_network": "246.0.0.1/8",
|
||||
"listen_port": 23209,
|
||||
"mtu": 1380,
|
||||
"obf_key": 0,
|
||||
"keep_alive": 60,
|
||||
"tun_name": "wg_tun"
|
||||
},
|
||||
"mqtt": {
|
||||
"protocol": "tls",
|
||||
"host": "mqtt.yizhisec.com",
|
||||
"port": 443,
|
||||
"cert": "/yizhisec/hs_net/conf/ssl/mqtt.client.crt",
|
||||
"key": "/yizhisec/hs_net/conf/ssl/mqtt.client.key",
|
||||
"keep_alive": 60
|
||||
},
|
||||
"paseto_key": "TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=",
|
||||
"resource_server": "hs-gateway-controller",
|
||||
"gatewayVersionFile": "/etc/yizhisec/gateway_version.json",
|
||||
"lastVersion": null,
|
||||
"workDir": "/yizhisec/hs_net/workspace",
|
||||
"dns_cache": {
|
||||
"Address": "127.0.0.1:9028"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
_url = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_net/release/2.1.0-std/package.tar.gz"
|
||||
)
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "hs-net")
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "dependency", "hs_net")
|
||||
)
|
||||
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Error("MakeHSNet: 创建目录失败s")
|
||||
logger.Debug("MakeHSNet: 创建目录失败: %s", err.Error())
|
||||
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
|
||||
}
|
||||
|
||||
logger.Fatal("MakeHSNet: 构建 hs-net 失败!!!(怎么完善,怎么完善,怎么完善???)")
|
||||
// {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
|
||||
}
|
||||
|
||||
// mv workdir/package/server workdir/
|
||||
// mv workdir/package/server_aes workdir/
|
||||
if err = os.Rename(filepath.Join(workdir, "package", "server"), filepath.Join(workdir, "server")); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 重命名文件失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if err = os.Rename(filepath.Join(workdir, "package", "server_aes"), filepath.Join(workdir, "server_aes")); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 重命名文件失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// write down conf_out to server.conf
|
||||
if err = os.WriteFile(filepath.Join(workdir, "server.conf"), []byte(_conf_out), 0644); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 写入配置文件失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// write down conf_in to conf/server.conf
|
||||
if err = os.MkdirAll(filepath.Join(workdir, "conf", "ssl"), 0755); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 创建目录失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if err = os.WriteFile(filepath.Join(workdir, "conf", "server.conf"), []byte(_conf_in), 0644); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 写入配置文件失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// write resource.SSLMQTTClientCrt
|
||||
if err = os.WriteFile(filepath.Join(workdir, "conf", "ssl", "mqtt.client.crt"), resource.SSLMQTTClientCrt, 0644); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 写入配置文件失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if err = os.WriteFile(filepath.Join(workdir, "conf", "ssl", "mqtt.client.key"), resource.SSLMQTTClientKey, 0644); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 写入配置文件失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// mkdir workspace
|
||||
if err = os.MkdirAll(filepath.Join(workdir, "workspace"), 0755); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 创建目录失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// new empty file lastVersion.txt
|
||||
if err = os.WriteFile(filepath.Join(workdir, "lastVersion.txt"), []byte{}, 0644); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 创建空文件失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// 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())
|
||||
return err
|
||||
}
|
||||
|
||||
// upsert.sh script
|
||||
const _upsert = `#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "Starting hs-net deployment..."
|
||||
|
||||
# 1. Copy token file
|
||||
echo "Copying token file..."
|
||||
mkdir -p /etc/yizhisec
|
||||
cp ../../configmap/token /etc/yizhisec/token
|
||||
echo "Token file copied successfully"
|
||||
|
||||
# 2. Get local IP address
|
||||
echo "Detecting local IP address..."
|
||||
LocalIP=$(ip route get 1.1.1.1 | grep -oP 'src \K\S+')
|
||||
if [ -z "$LocalIP" ]; then
|
||||
echo "Error: Failed to detect local IP address"
|
||||
exit 1
|
||||
fi
|
||||
echo "Local IP detected: $LocalIP"
|
||||
|
||||
# 3. Update /etc/hosts with required entries
|
||||
echo "Updating /etc/hosts..."
|
||||
for host in "hs-gateway-register-controller" "hs-gateway-controller" "mqtt.yizhisec.com"; do
|
||||
if grep -q "$host" /etc/hosts; then
|
||||
sed -i "/$host/d" /etc/hosts
|
||||
fi
|
||||
echo "$LocalIP $host" >> /etc/hosts
|
||||
echo "Added: $LocalIP $host"
|
||||
done
|
||||
echo "/etc/hosts updated successfully"
|
||||
|
||||
# 4. Replace __ip__ in server.conf
|
||||
echo "Updating server.conf..."
|
||||
sed -i "s/__ip__/$LocalIP/g" server.conf
|
||||
echo "server.conf updated successfully"
|
||||
|
||||
# 5. Replace __ip__ in conf/server.conf
|
||||
echo "Updating conf/server.conf..."
|
||||
sed -i "s/__ip__/$LocalIP/g" conf/server.conf
|
||||
echo "conf/server.conf updated successfully"
|
||||
|
||||
# 6. Create /mnt/huge directory
|
||||
echo "Creating /mnt/huge directory..."
|
||||
mkdir -p /mnt/huge
|
||||
echo "/mnt/huge directory created successfully"
|
||||
|
||||
# 7. Create workspace directories
|
||||
echo "Creating workspace directories..."
|
||||
mkdir -p /yizhisec/hs_net/workspace/log
|
||||
mkdir -p /yizhisec/hs_net/conf
|
||||
echo "Workspace directories created successfully"
|
||||
|
||||
# 8. Copy configuration files
|
||||
echo "Copying configuration files..."
|
||||
cp -r conf/* /yizhisec/hs_net/conf/
|
||||
echo "Configuration files copied successfully"
|
||||
|
||||
# 9. Copy binaries based on CPU AVX support
|
||||
echo "Detecting CPU AVX support..."
|
||||
if grep -q avx /proc/cpuinfo; then
|
||||
echo "AVX support detected, using server_aes"
|
||||
cp server_aes /yizhisec/hs_net/server
|
||||
chmod +x /yizhisec/hs_net/server
|
||||
echo "server_aes copied to /yizhisec/hs_net/server with execute permission"
|
||||
else
|
||||
echo "AVX not supported, using server"
|
||||
cp server /yizhisec/hs_net/server
|
||||
chmod +x /yizhisec/hs_net/server
|
||||
echo "server copied to /yizhisec/hs_net/server with execute permission"
|
||||
fi
|
||||
echo "Binary copied successfully"
|
||||
|
||||
# 10. Copy lastVersion.txt
|
||||
echo "Copying lastVersion.txt..."
|
||||
cp lastVersion.txt /yizhisec/hs_net/
|
||||
echo "lastVersion.txt copied successfully"
|
||||
|
||||
# 11. Load container image
|
||||
echo "Loading hs-net container image..."
|
||||
k0s ctr -n hs-net images import hs-net.tar
|
||||
echo "Container image loaded successfully"
|
||||
|
||||
# 12. Install and enable systemd service
|
||||
echo "Installing hs-net systemd service..."
|
||||
cp hs-net.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable hs-net.service
|
||||
echo "hs-net service installed and enabled"
|
||||
|
||||
# 13. Start the service
|
||||
echo "Starting hs-net service..."
|
||||
systemctl restart hs-net.service
|
||||
echo "hs-net service started successfully"
|
||||
|
||||
echo "hs-net deployment completed successfully!"
|
||||
echo "You can check the service status with: systemctl status hs-net.service"
|
||||
`
|
||||
|
||||
// Write upsert.sh
|
||||
logger.Debug("☑️ MakeHSNet: 写入 upsert.sh 脚本...")
|
||||
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
|
||||
logger.Debug("❌ MakeHSNet: 写入 upsert.sh 失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ MakeHSNet: 写入 upsert.sh 成功")
|
||||
|
||||
logger.Info("✅ MakeHSNet: 构建 hs-net 成功, workdir = %s", workdir)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"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/model"
|
||||
)
|
||||
|
||||
type imageOpt struct {
|
||||
@@ -32,9 +32,11 @@ func WithImageSave(filename string) ImageOpt {
|
||||
}
|
||||
}
|
||||
|
||||
func WithImageForcePull() ImageOpt {
|
||||
func WithImageForcePull(force bool) ImageOpt {
|
||||
return func(o *imageOpt) {
|
||||
o.ForcePull = true
|
||||
if force {
|
||||
o.ForcePull = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,63 +109,13 @@ SAVE:
|
||||
}
|
||||
|
||||
func (m *maker) Images(ctx context.Context) error {
|
||||
type Images struct {
|
||||
Name string
|
||||
Fallback string
|
||||
Save string
|
||||
Force bool
|
||||
}
|
||||
|
||||
var images = []*Images{
|
||||
{Name: "quay.io/k0sproject/apiserver-network-proxy-agent:v0.32.0", Fallback: "hub.yizhisec.com/external/apiserver-network-proxy-agent:v0.32.0", Save: "k0s.apiserver-network-proxy-agent.tar"},
|
||||
{Name: "quay.io/k0sproject/cni-node:1.7.1-k0s.0", Fallback: "", Save: "k0s.cni-node.tar"},
|
||||
{Name: "quay.io/k0sproject/coredns:1.12.2", Fallback: "", Save: "k0s.coredns.tar"},
|
||||
{Name: "quay.io/k0sproject/kube-proxy:v1.33.4", Fallback: "", Save: "k0s.kube-proxy.tar"},
|
||||
{Name: "quay.io/k0sproject/kube-router:v2.5.0-iptables1.8.11-0", Fallback: "", Save: "k0s.kube-router.tar"},
|
||||
{Name: "quay.io/k0sproject/metrics-server:v0.7.2-0", Fallback: "", Save: "k0s.metrics-server.tar"},
|
||||
{Name: "quay.io/k0sproject/pause:3.10.1", Fallback: "", Save: "k0s.pause.tar"},
|
||||
|
||||
{Name: "ghcr.io/flannel-io/flannel:v0.27.4", Fallback: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/flannel-io/flannel:v0.27.4", Save: "flannel.tar"},
|
||||
{Name: "ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1", Fallback: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1", Save: "flannel-cni-plugin.tar"},
|
||||
|
||||
{Name: "docker.io/longhornio/longhorn-engine:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-engine:v1.10.0", Save: "longhorn.longhorn-engine.tar"},
|
||||
{Name: "docker.io/longhornio/longhorn-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-manager:v1.10.0", Save: "longhorn.longhorn-manager.tar"},
|
||||
{Name: "docker.io/longhornio/longhorn-instance-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-instance-manager:v1.10.0", Save: "longhorn.longhorn-instance-manager.tar"},
|
||||
{Name: "docker.io/longhornio/longhorn-share-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-share-manager:v1.10.0", Save: "longhorn.longhorn-share-manager.tar"},
|
||||
{Name: "docker.io/longhornio/longhorn-ui:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-ui:v1.10.0", Save: "longhorn.longhorn-ui.tar"},
|
||||
{Name: "docker.io/longhornio/csi-snapshotter:v8.3.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-snapshotter:v8.3.0-20250826", Save: "longhorn.csi-snapshotter.tar"},
|
||||
{Name: "docker.io/longhornio/csi-resizer:v1.14.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-resizer:v1.14.0-20250826", Save: "longhorn.csi-resizer.tar"},
|
||||
{Name: "docker.io/longhornio/csi-provisioner:v5.3.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-provisioner:v5.3.0-20250826", Save: "longhorn.csi-provisioner.tar"},
|
||||
{Name: "docker.io/longhornio/livenessprobe:v2.16.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/livenessprobe:v2.16.0-20250826", Save: "longhorn.livenessprobe.tar"},
|
||||
{Name: "docker.io/longhornio/csi-node-driver-registrar:v2.14.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-node-driver-registrar:v2.14.0-20250826", Save: "longhorn.csi-node-driver-registrar.tar"},
|
||||
{Name: "docker.io/longhornio/csi-attacher:v4.9.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-attacher:v4.9.0-20250826", Save: "longhorn.csi-attacher.tar"},
|
||||
|
||||
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: "docker.io/bitpoke/mysql-operator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator:v0.6.3", Save: "bitpoke.mysql-operator.v0.6.3.tar"},
|
||||
{Name: "docker.io/bitpoke/mysql-operator-orchestrator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-orchestrator:v0.6.3", Save: "bitpoke.mysql-operator-orchestrator.v0.6.3.tar"},
|
||||
{Name: "docker.io/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Save: "bitpoke.mysql-operator-sidecar-8.0.v0.6.3.tar"},
|
||||
{Name: "docker.io/library/percona:8.0.28-20", Fallback: "docker-mirror.yizhisec.com/library/percona:8.0.28-20", Save: "percona.8.0.28-20.tar"},
|
||||
{Name: "docker.io/prom/mysqld-exporter:v0.13.0", Fallback: "docker-mirror.yizhisec.com/prom/mysqld-exporter:v0.13.0", Save: "prom.mysqld-exporter.v0.13.0.tar"},
|
||||
|
||||
{Name: "docker.io/bitnami/redis:8.2.2", Fallback: "hub.yizhisec.com/external/bitnami/redis:8.2.2", Save: "bitnami.redis.8.2.2.tar"},
|
||||
{Name: "hub.yizhisec.com/external/elasticsearch:7.17.28", Fallback: "", Save: "elasticsearch.7.17.28.tar"},
|
||||
{Name: "hub.yizhisec.com/external/kibana:7.17.28", Fallback: "", Save: "kibana.7.17.28.tar"},
|
||||
{Name: "hub.yizhisec.com/external/emqx:5.1", Fallback: "", Save: "emqx.5.1.tar"},
|
||||
|
||||
{Name: "hub.yizhisec.com/build/hybirdscope/front/admin:latest", Fallback: "", Save: "app.front.admin.tar", Force: true},
|
||||
{Name: "hub.yizhisec.com/hybridscope/v2/front-user:latest", Fallback: "", Save: "app.front.user.tar", Force: true},
|
||||
{Name: "hub.yizhisec.com/hybridscope/user_management:latest", Fallback: "", Save: "app.user.tar", Force: true},
|
||||
{Name: "hub.yizhisec.com/hybridscope/gateway_controller:latest", Fallback: "", Save: "app.gateway.tar", Force: true},
|
||||
{Name: "hub.yizhisec.com/hybridscope/client_server:latest", Fallback: "", Save: "app.client.tar", Force: true},
|
||||
{Name: "hub.yizhisec.com/hybridscope/mie-server:latest", Fallback: "", Save: "app.mie.tar", Force: true},
|
||||
{Name: "hub.yizhisec.com/hybridscope/less_dns_service:latest", Fallback: "", Save: "app.less_dns.tar", Force: true},
|
||||
{Name: "hub.yizhisec.com/hybridscope/hsnet:release_2.1.0-std", Fallback: "", Save: "app.hsnet.tar", Force: true},
|
||||
}
|
||||
|
||||
for _, image := range images {
|
||||
image.Save = filepath.Join(opt.Cfg.Make.Dir, "dependency", "image", image.Save)
|
||||
image.Save = filepath.Join(m.workdir, "dependency", "image", image.Save)
|
||||
}
|
||||
|
||||
logger.Info("☑️ 开始获取镜像(s)...")
|
||||
@@ -173,10 +125,7 @@ func (m *maker) Images(ctx context.Context) error {
|
||||
opts := []ImageOpt{
|
||||
WithImageFallback(image.Fallback),
|
||||
WithImageSave(image.Save),
|
||||
}
|
||||
|
||||
if image.Force {
|
||||
opts = append(opts, WithImageForcePull())
|
||||
WithImageForcePull(image.Force),
|
||||
}
|
||||
|
||||
logger.Info("☑️ 获取镜像: %s", image.Name)
|
||||
|
||||
@@ -3,12 +3,12 @@ package maker
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
func TestImage(t *testing.T) {
|
||||
logger.SetLogLevel(logger.LogLevelDebug)
|
||||
m := NewMaker()
|
||||
m := NewMaker("./x-data")
|
||||
m.Image(t.Context(), "docker.io/nginx:1.29.3-alpine3.22",
|
||||
WithImageFallback("swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.29.3-alpine3.22"),
|
||||
WithImageSave("/root/nginx.tar"),
|
||||
|
||||
@@ -5,15 +5,14 @@ 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/resource"
|
||||
)
|
||||
|
||||
func (m *maker) LessDNS(ctx context.Context) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "less-dns")
|
||||
location = filepath.Join(m.workdir, "dependency", "less-dns")
|
||||
)
|
||||
|
||||
logger.Info("☑️ maker.LessDNS: 开始构建 less-dns...")
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
func (m *maker) Longhorn(ctx context.Context, replica int) error {
|
||||
@@ -24,18 +24,18 @@ persistence:
|
||||
)
|
||||
|
||||
var (
|
||||
err error
|
||||
chartURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/charts/longhorn-1.10.0.tgz"
|
||||
longhornDir = filepath.Join(opt.Cfg.Make.Dir, "dependency", "longhorn")
|
||||
chartFile = filepath.Join(longhornDir, "longhorn-1.10.0.tgz")
|
||||
valuesFile = filepath.Join(longhornDir, "values.yaml")
|
||||
err error
|
||||
chartURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/charts/longhorn-1.10.0.tgz"
|
||||
location = filepath.Join(m.workdir, "dependency", "longhorn")
|
||||
chartFile = filepath.Join(location, "longhorn-1.10.0.tgz")
|
||||
valuesFile = filepath.Join(location, "values.yaml")
|
||||
)
|
||||
|
||||
logger.Info("☑️ 开始准备 Longhorn 资源...")
|
||||
logger.Debug("下载地址: %s", chartURL)
|
||||
logger.Debug("目标目录: %s", longhornDir)
|
||||
logger.Debug("目标目录: %s", location)
|
||||
|
||||
if err = os.MkdirAll(filepath.Join(opt.Cfg.Make.Dir, "dependency", "longhorn"), 0755); err != nil {
|
||||
if err = os.MkdirAll(filepath.Join(m.workdir, "dependency", "longhorn"), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -58,6 +58,36 @@ persistence:
|
||||
return err
|
||||
}
|
||||
|
||||
var images = []*model.Image{
|
||||
{Name: "docker.io/longhornio/longhorn-engine:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-engine:v1.10.0", Save: "longhorn.longhorn-engine.tar"},
|
||||
{Name: "docker.io/longhornio/longhorn-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-manager:v1.10.0", Save: "longhorn.longhorn-manager.tar"},
|
||||
{Name: "docker.io/longhornio/longhorn-instance-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-instance-manager:v1.10.0", Save: "longhorn.longhorn-instance-manager.tar"},
|
||||
{Name: "docker.io/longhornio/longhorn-share-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-share-manager:v1.10.0", Save: "longhorn.longhorn-share-manager.tar"},
|
||||
{Name: "docker.io/longhornio/longhorn-ui:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-ui:v1.10.0", Save: "longhorn.longhorn-ui.tar"},
|
||||
{Name: "docker.io/longhornio/csi-snapshotter:v8.3.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-snapshotter:v8.3.0-20250826", Save: "longhorn.csi-snapshotter.tar"},
|
||||
{Name: "docker.io/longhornio/csi-resizer:v1.14.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-resizer:v1.14.0-20250826", Save: "longhorn.csi-resizer.tar"},
|
||||
{Name: "docker.io/longhornio/csi-provisioner:v5.3.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-provisioner:v5.3.0-20250826", Save: "longhorn.csi-provisioner.tar"},
|
||||
{Name: "docker.io/longhornio/livenessprobe:v2.16.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/livenessprobe:v2.16.0-20250826", Save: "longhorn.livenessprobe.tar"},
|
||||
{Name: "docker.io/longhornio/csi-node-driver-registrar:v2.14.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-node-driver-registrar:v2.14.0-20250826", Save: "longhorn.csi-node-driver-registrar.tar"},
|
||||
{Name: "docker.io/longhornio/csi-attacher:v4.9.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-attacher:v4.9.0-20250826", Save: "longhorn.csi-attacher.tar"},
|
||||
}
|
||||
|
||||
logger.Debug("MakeLonghorn: 开始获取镜像...")
|
||||
for _, image := range images {
|
||||
opts := []ImageOpt{
|
||||
WithImageFallback(image.Fallback),
|
||||
WithImageSave(filepath.Join(location, image.Save)),
|
||||
WithImageForcePull(image.Force),
|
||||
}
|
||||
|
||||
if err := m.Image(ctx, image.Name, opts...); err != nil {
|
||||
logger.Error("❌ MakeLonghorn: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
|
||||
logger.Debug("❌ MakeLonghorn: 获取镜像失败: %s, %v", image.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.Debug("MakeLonghorn: 获取镜像成功!!!")
|
||||
|
||||
logger.Info("✅ 成功创建 longhorn 资源!!!")
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package maker
|
||||
|
||||
type maker struct{}
|
||||
|
||||
func NewMaker() *maker {
|
||||
return &maker{}
|
||||
type maker struct {
|
||||
workdir string
|
||||
}
|
||||
|
||||
func NewMaker(workdir string) *maker {
|
||||
return &maker{workdir: workdir}
|
||||
}
|
||||
|
||||
63
internal/controller/maker/minio.go
Normal file
63
internal/controller/maker/minio.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
// todo, remake minio-init image
|
||||
func (m *maker) Minio(ctx context.Context, storage string) error {
|
||||
var (
|
||||
err error
|
||||
workdir = filepath.Join(m.workdir, "dependency", "minio")
|
||||
)
|
||||
|
||||
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", workdir)
|
||||
|
||||
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 {
|
||||
logger.Debug("❌ maker.Minio: 写入资源文件失败, filename = %s, err = %v", filename, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Minio: 准备资源文件成功, filename = %s", filename)
|
||||
|
||||
logger.Debug("☑️ maker.Minio: 开始获取所需镜像...")
|
||||
var images = []*model.Image{
|
||||
{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(workdir, image.Save)),
|
||||
WithImageForcePull(image.Force),
|
||||
}
|
||||
|
||||
if err := m.Image(ctx, image.Name, opts...); err != nil {
|
||||
logger.Error("❌ maker.Minio: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
|
||||
logger.Debug("❌ maker.Minio: 获取镜像失败: %s, %v", image.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.Debug("✅ maker.Minio: 获取所需镜像成功!!!")
|
||||
|
||||
logger.Info("✅ maker.Minio: 构建 minio 依赖成功, workdir = %s", workdir)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type MysqlOpt func(*mysqlOpt)
|
||||
@@ -30,7 +30,7 @@ func WithMySQLReplica(replica int) MysqlOpt {
|
||||
func WithMySQLStorage(storage string) MysqlOpt {
|
||||
return func(o *mysqlOpt) {
|
||||
// validate Kubernetes storage size string (e.g., "50Gi", "100Mi")
|
||||
if matched, _ := regexp.MatchString(`^\d+(\.\d+)?[EPTGMK]i?$`, storage); matched {
|
||||
if opt.StorageSizeReg.MatchString(storage) {
|
||||
o.Storage = storage
|
||||
}
|
||||
}
|
||||
@@ -74,14 +74,14 @@ spec:
|
||||
resources:
|
||||
requests:
|
||||
storage: %s
|
||||
podSpec:
|
||||
resources:
|
||||
requests:
|
||||
cpu: "250m"
|
||||
memory: "250Mi"
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: "4Gi"
|
||||
podSpec:
|
||||
resources:
|
||||
requests:
|
||||
cpu: "250m"
|
||||
memory: "250Mi"
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: "4Gi"
|
||||
# affinity:
|
||||
# podAntiAffinity:
|
||||
# requiredDuringSchedulingIgnoredDuringExecution: # 强制规则
|
||||
@@ -95,7 +95,6 @@ spec:
|
||||
# values: ["mysql-cluster"]
|
||||
# topologyKey: "kubernetes.io/hostname" # 确保不同节点
|
||||
---
|
||||
|
||||
apiVersion: mysql.presslabs.org/v1alpha1
|
||||
kind: MysqlDatabase
|
||||
metadata:
|
||||
@@ -118,9 +117,9 @@ spec:
|
||||
|
||||
err error
|
||||
chartURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/charts/mysql-operator-0.6.3.tgz"
|
||||
mysqlDir = filepath.Join(opt.Cfg.Make.Dir, "dependency", "mysql")
|
||||
chartFile = filepath.Join(mysqlDir, "mysql-operator-0.6.3.tgz")
|
||||
clusterFile = filepath.Join(mysqlDir, "cluster.yaml")
|
||||
location = filepath.Join(m.workdir, "dependency", "mysql")
|
||||
chartFile = filepath.Join(location, "mysql-operator-0.6.3.tgz")
|
||||
clusterFile = filepath.Join(location, "cluster.yaml")
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
@@ -129,13 +128,13 @@ spec:
|
||||
|
||||
logger.Info("☑️ 开始构建 MySQL 资源...")
|
||||
|
||||
if err = os.MkdirAll(mysqlDir, 0755); err != nil {
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Password = base64.StdEncoding.EncodeToString([]byte(o.Password))
|
||||
|
||||
logger.Debug("正在下载 MySQL operator chart..., url = %s, dir = %s", chartURL, mysqlDir)
|
||||
logger.Debug("正在下载 MySQL operator chart..., url = %s, dir = %s", chartURL, location)
|
||||
if err = downloader.Download(
|
||||
ctx,
|
||||
chartURL,
|
||||
@@ -158,6 +157,31 @@ spec:
|
||||
|
||||
logger.Debug("✅ 成功创建 database.yaml")
|
||||
|
||||
logger.Debug("☑️ MakeMysql: 开始获取所需镜像...")
|
||||
|
||||
var images = []*model.Image{
|
||||
{Name: "docker.io/bitpoke/mysql-operator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator:v0.6.3", Save: "bitpoke.mysql-operator.v0.6.3.tar"},
|
||||
{Name: "docker.io/bitpoke/mysql-operator-orchestrator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-orchestrator:v0.6.3", Save: "bitpoke.mysql-operator-orchestrator.v0.6.3.tar"},
|
||||
{Name: "docker.io/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Save: "bitpoke.mysql-operator-sidecar-8.0.v0.6.3.tar"},
|
||||
{Name: "docker.io/library/percona:8.0.28-20", Fallback: "docker-mirror.yizhisec.com/library/percona:8.0.28-20", Save: "percona.8.0.28-20.tar"},
|
||||
{Name: "docker.io/prom/mysqld-exporter:v0.13.0", Fallback: "docker-mirror.yizhisec.com/prom/mysqld-exporter:v0.13.0", Save: "prom.mysqld-exporter.v0.13.0.tar"},
|
||||
}
|
||||
|
||||
for _, image := range images {
|
||||
opts := []ImageOpt{
|
||||
WithImageFallback(image.Fallback),
|
||||
WithImageSave(filepath.Join(location, image.Save)),
|
||||
WithImageForcePull(image.Force),
|
||||
}
|
||||
|
||||
if err := m.Image(ctx, image.Name, opts...); err != nil {
|
||||
logger.Error("❌ MakeMysql: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
|
||||
logger.Debug("❌ MakeMysql: 获取镜像失败: %s, %v", image.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.Debug("✅ MakeMysql: 获取镜像成功!!!")
|
||||
|
||||
logger.Info("✅ MySQL 资源构建成功!!!")
|
||||
|
||||
return nil
|
||||
|
||||
147
internal/controller/maker/proxy.go
Normal file
147
internal/controller/maker/proxy.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
)
|
||||
|
||||
func (m *maker) Proxy(ctx context.Context) error {
|
||||
const (
|
||||
binURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/caddy"
|
||||
systemdSvc = `[Unit]
|
||||
Description=YiZhiSec Caddy Reverse Proxy
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/caddy run --config /etc/caddy/caddy.json
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
Nice=-20
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
)
|
||||
|
||||
location := filepath.Join(m.workdir, "dependency", "proxy")
|
||||
|
||||
logger.Info("☑️ maker.Proxy: 开始构建 caddy 反向代理...")
|
||||
logger.Debug("☑️ maker.Proxy: 创建目录 %s", location)
|
||||
if err := os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Debug("❌ maker.Proxy: 创建目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Proxy: 创建目录 %s 成功", location)
|
||||
|
||||
logger.Debug("☑️ maker.Proxy: 下载 caddy 二进制..., url = %s, dest = %s", binURL, filepath.Join(location, "caddy"))
|
||||
if err := downloader.Download(
|
||||
ctx,
|
||||
binURL,
|
||||
filepath.Join(location, "caddy"),
|
||||
downloader.WithInsecureSkipVerify(),
|
||||
downloader.WithFileMode(0755),
|
||||
); err != nil {
|
||||
logger.Debug("❌ maker.Proxy: 下载 caddy 失败, url = %s, err = %v", binURL, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Proxy: 下载 caddy 成功, url = %s", binURL)
|
||||
|
||||
logger.Debug("☑️ maker.Proxy: 写入 caddy.json 文件..., dest = %s", filepath.Join(location, "caddy.json"))
|
||||
caddyConfig := model.CaddyConfig{
|
||||
"apps": &model.CaddyApp{
|
||||
Layer4: &model.CaddyLayer4{
|
||||
Servers: map[string]*model.CaddyServer{
|
||||
"proxy_8443": {
|
||||
Listen: []string{":8443"},
|
||||
Routes: []*model.CaddyRoute{
|
||||
{
|
||||
Handle: []*model.CaddyHandle{
|
||||
{
|
||||
Handler: "proxy",
|
||||
Upstreams: []*model.CaddyUpstream{
|
||||
{Dial: []string{"__ip_1__:32443"}},
|
||||
{Dial: []string{"__ip_2__:32443"}},
|
||||
},
|
||||
HealthChecks: &model.CaddyHealthCheck{
|
||||
Active: &model.CaddyActive{
|
||||
Interval: "10s",
|
||||
Timeout: "2s",
|
||||
Port: 32443,
|
||||
},
|
||||
Passive: &model.CaddyPassive{
|
||||
FailDuration: "30s",
|
||||
MaxFails: 2,
|
||||
},
|
||||
},
|
||||
LoadBalancing: &model.CaddyLoadBalancing{
|
||||
Selection: &model.CaddySelection{
|
||||
Policy: "round_robin",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"proxy_443": {
|
||||
Listen: []string{":443"},
|
||||
Routes: []*model.CaddyRoute{
|
||||
{
|
||||
Handle: []*model.CaddyHandle{
|
||||
{
|
||||
Handler: "proxy",
|
||||
Upstreams: []*model.CaddyUpstream{
|
||||
{Dial: []string{"__ip_1__:31443"}},
|
||||
{Dial: []string{"__ip_2__:31443"}},
|
||||
},
|
||||
HealthChecks: &model.CaddyHealthCheck{
|
||||
Active: &model.CaddyActive{
|
||||
Interval: "10s",
|
||||
Timeout: "2s",
|
||||
Port: 31443,
|
||||
},
|
||||
Passive: &model.CaddyPassive{
|
||||
FailDuration: "30s",
|
||||
MaxFails: 2,
|
||||
},
|
||||
},
|
||||
LoadBalancing: &model.CaddyLoadBalancing{
|
||||
Selection: &model.CaddySelection{
|
||||
Policy: "round_robin",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
bs, _ := json.MarshalIndent(caddyConfig, "", " ")
|
||||
if err := os.WriteFile(filepath.Join(location, "caddy.json"), []byte(bs), 0644); err != nil {
|
||||
logger.Debug("❌ maker.Proxy: 写入 Caddyfile 失败, dest = %s, err = %v", filepath.Join(location, "caddy.json"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Proxy: 写入 Caddyfile 文件成功, dest = %s", filepath.Join(location, "caddy.json"))
|
||||
|
||||
logger.Debug("☑️ maker.Proxy: 写入 caddy.service 文件..., dest = %s", filepath.Join(location, "caddy.service"))
|
||||
if err := os.WriteFile(filepath.Join(location, "caddy.service"), []byte(systemdSvc), 0644); err != nil {
|
||||
logger.Debug("❌ maker.Proxy: 写入 caddy.service 失败, dest = %s, err = %v", filepath.Join(location, "caddy.service"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Proxy: 写入 caddy.service 文件成功, dest = %s", filepath.Join(location, "caddy.service"))
|
||||
|
||||
logger.Info("✅ maker.Proxy: 构建 caddy 反向代理成功!!!")
|
||||
return nil
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type RedisOpt func(*redisOpt)
|
||||
@@ -38,7 +38,7 @@ func WithRedisPassword(password string) RedisOpt {
|
||||
|
||||
func WithRedisStorage(storage string) RedisOpt {
|
||||
return func(o *redisOpt) {
|
||||
if matched, _ := regexp.MatchString(`^\d+(\.\d+)?[EPTGMK]i?$`, storage); matched {
|
||||
if opt.StorageSizeReg.MatchString(storage) {
|
||||
o.Storage = storage
|
||||
}
|
||||
}
|
||||
@@ -91,18 +91,18 @@ metrics:
|
||||
fn(o)
|
||||
}
|
||||
|
||||
redisDir := filepath.Join(opt.Cfg.Make.Dir, "dependency", "redis")
|
||||
chartFile := filepath.Join(redisDir, chartFilename)
|
||||
valuesFile := filepath.Join(redisDir, "values.yaml")
|
||||
location := filepath.Join(m.workdir, "dependency", "redis")
|
||||
chartFile := filepath.Join(location, chartFilename)
|
||||
valuesFile := filepath.Join(location, "values.yaml")
|
||||
|
||||
logger.Info("☑️ maker.Redis: 开始构建 Redis 资源...")
|
||||
logger.Debug("☑️ maker.Redis: 开始构建 Redis 资源..., opt = %#v", o)
|
||||
logger.Debug("☑️ maker.Redis: 创建目录 %s", redisDir)
|
||||
if err := os.MkdirAll(redisDir, 0755); err != nil {
|
||||
logger.Debug("☑️ maker.Redis: 创建目录 %s", location)
|
||||
if err := os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Debug("❌ maker.Redis: 创建目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Redis: 创建目录 %s 成功", redisDir)
|
||||
logger.Debug("✅ maker.Redis: 创建目录 %s 成功", location)
|
||||
|
||||
logger.Debug("☑️ maker.Redis: 下载 Redis chart..., url = %s, dest = %s", chartURL, chartFile)
|
||||
if err := downloader.Download(
|
||||
@@ -124,6 +124,14 @@ metrics:
|
||||
}
|
||||
logger.Debug("✅ maker.Redis: 写入 values.yaml 文件成功, dest = %s", valuesFile)
|
||||
|
||||
logger.Debug("☑️ maker.Redis: 开始获取镜像...")
|
||||
img := &model.Image{Name: "docker.io/bitnami/redis:8.2.2", Fallback: "hub.yizhisec.com/external/bitnami/redis:8.2.2", Save: "bitnami.redis.8.2.2.tar"}
|
||||
if err := m.Image(ctx, img.Name, WithImageFallback(img.Fallback), WithImageSave(filepath.Join(location, img.Save))); err != nil {
|
||||
logger.Debug("❌ maker.Redis: 获取镜像失败: %s, %v", img.Name, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Redis: 获取镜像成功!!!")
|
||||
|
||||
logger.Info("✅ maker.Redis: 构建 Redis 资源成功!!!")
|
||||
|
||||
return nil
|
||||
|
||||
73
internal/controller/maker/registry.go
Normal file
73
internal/controller/maker/registry.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"
|
||||
)
|
||||
|
||||
func (m *maker) Registry(ctx context.Context, storage string) error {
|
||||
const (
|
||||
_registryToml = `
|
||||
[plugins."io.containerd.grpc.v1.cri".registry]
|
||||
config_path = ""
|
||||
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
|
||||
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."10.96.123.45:80"]
|
||||
endpoint = ["http://10.96.123.45:80"]
|
||||
[plugins."io.containerd.grpc.v1.cri".registry.configs]
|
||||
[plugins."io.containerd.grpc.v1.cri".registry.configs."10.96.123.45:80".tls]
|
||||
insecure_skip_verify = true
|
||||
[plugins."io.containerd.grpc.v1.cri".registry.configs."10.96.123.45:80".auth]`
|
||||
)
|
||||
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(m.workdir, "dependency", "registry")
|
||||
imgName = "docker.io/library/registry:2.8.3"
|
||||
uiImgName = "docker.io/quiq/registry-ui:0.11.0"
|
||||
imgFallback = "docker-mirror.yizhisec.com/library/registry:2.8.3"
|
||||
uiImgFallback = "docker-mirror.yizhisec.com/quiq/registry-ui:0.11.0"
|
||||
)
|
||||
|
||||
logger.Info("☑️ 开始创建依赖: registry...")
|
||||
|
||||
logger.Debug("☑️ 创建 dir: %s ...", location)
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Debug("❌ 创建 dir %s 失败: %v", location, err)
|
||||
return err
|
||||
}
|
||||
|
||||
yamlFile := filepath.Join(location, "registry.yaml")
|
||||
content := fmt.Sprintf(resource.YAMLRegistry, storage)
|
||||
logger.Debug("写入 yaml 文件: %s ...", yamlFile)
|
||||
if os.WriteFile(yamlFile, []byte(content), 0644); err != nil {
|
||||
logger.Debug("❌ 写入 yaml 文件 %s 失败: %v", yamlFile, err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Debug("开始准备镜像...")
|
||||
imgFile := filepath.Join(location, "registry.tar")
|
||||
if err = m.Image(ctx, imgName, WithImageFallback(imgFallback), WithImageSave(imgFile)); err != nil {
|
||||
logger.Debug("❌ 准备镜像 %s 失败: %v", imgName, err)
|
||||
return err
|
||||
}
|
||||
imgFile = filepath.Join(location, "registry-ui.tar")
|
||||
if err = m.Image(ctx, uiImgName, WithImageFallback(uiImgFallback), WithImageSave(imgFile)); err != nil {
|
||||
logger.Debug("❌ 准备镜像 %s 失败: %v", uiImgName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Debug("写入 registry.toml 文件: %s ...", filepath.Join(location, "registry.toml"))
|
||||
if os.WriteFile(filepath.Join(location, "registry.toml"), []byte(_registryToml), 0644); err != nil {
|
||||
logger.Debug("❌ 写入 registry.toml 文件 %s 失败: %v", filepath.Join(location, "registry.toml"), err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ 创建依赖成功: registry!!!")
|
||||
|
||||
return nil
|
||||
}
|
||||
212
internal/controller/maker/seafile.go
Normal file
212
internal/controller/maker/seafile.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
"yizhisec.com/hsv2/forge/pkg/model"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
type SeafileOpt func(*seafileOpt)
|
||||
type seafileOpt struct {
|
||||
DBHost string
|
||||
DBPassword string
|
||||
AdminEmail string
|
||||
AdminPassword string
|
||||
ServerHostname string
|
||||
Storage string
|
||||
}
|
||||
|
||||
func WithSeafileStorage(storage string) SeafileOpt {
|
||||
return func(o *seafileOpt) {
|
||||
if opt.StorageSizeReg.MatchString(storage) {
|
||||
o.Storage = storage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithSeafileDBHost(host string) SeafileOpt {
|
||||
return func(o *seafileOpt) {
|
||||
if o.DBHost == "" {
|
||||
o.DBHost = host
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithSeafileDBPassword(password string) SeafileOpt {
|
||||
return func(o *seafileOpt) {
|
||||
if o.DBPassword == "" {
|
||||
o.DBPassword = password
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithSeafileAdminEmail(email string) SeafileOpt {
|
||||
return func(o *seafileOpt) {
|
||||
if opt.EmailReg.MatchString(email) {
|
||||
o.AdminEmail = email
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithSeafileAdminPassword(password string) SeafileOpt {
|
||||
return func(o *seafileOpt) {
|
||||
if o.AdminPassword == "" {
|
||||
o.AdminPassword = password
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithSeafileHostname(hostname string) SeafileOpt {
|
||||
return func(o *seafileOpt) {
|
||||
if o.ServerHostname == "" {
|
||||
o.ServerHostname = hostname
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *maker) Seafile(ctx context.Context, opts ...SeafileOpt) error {
|
||||
const (
|
||||
_config = `
|
||||
ControllerServer:
|
||||
UserManagement: user-service.hsv2:9013
|
||||
Database:
|
||||
Mysql:
|
||||
Address: %s:3306
|
||||
DBName: backup_server
|
||||
Password: %s
|
||||
SeafileDBName: seafile_db
|
||||
UserName: root
|
||||
Log:
|
||||
Dir: ./log
|
||||
Level: 1
|
||||
Name: hs_backup_seafile
|
||||
SeafileServer:
|
||||
Admin: %s
|
||||
AdminPassword: %s
|
||||
BackupDir: /seafile/backup_data
|
||||
Host: seafile-service
|
||||
Port: 80
|
||||
StorageDir: /seafile/storage
|
||||
Sentry:
|
||||
DSN: https://fd7149f063c211eda2b50242ac15001c@sentry.yizhisec.com:13443/7
|
||||
TracesSampleRate: 1
|
||||
Web:
|
||||
Host: 0.0.0.0
|
||||
Mode: release
|
||||
Port: 9027
|
||||
WorkDir: /yizhisec/hs_backup_seafile/workspace
|
||||
YosGuard:
|
||||
Host: 172.17.0.1
|
||||
Port: 7788`
|
||||
_upsert = `#!/bin/bash
|
||||
|
||||
kubectl create configmap config-backup-seafile --namespace seafile --from-file=config.yml=./config.yml --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl create configmap nginx-seafile --namespace hsv2 --from-file=seafile.conf=./nginx.conf --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl rollout restart deployment backup-seafile-deployment -n seafile`
|
||||
)
|
||||
|
||||
var (
|
||||
err error
|
||||
o = &seafileOpt{
|
||||
DBHost: "mysql-cluster-mysql-master.db-mysql",
|
||||
DBPassword: "L0hMysql.",
|
||||
AdminEmail: "admin@yizhisec.com",
|
||||
AdminPassword: "asecret",
|
||||
ServerHostname: "cloud.hybridscope.com",
|
||||
Storage: "50Gi",
|
||||
}
|
||||
workdir = filepath.Join(m.workdir, "dependency", "seafile")
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
logger.Info("☑️ maker.Seafile: 开始构建 seafile 依赖, dir = %s", workdir)
|
||||
|
||||
// 1. 准备工作目录
|
||||
logger.Debug("☑️ make.Seafile: 准备工作目录: %s", workdir)
|
||||
if err = os.MkdirAll(workdir, 0755); err != nil {
|
||||
logger.Error("❌ make.Seafile: 准备工作目录: %s 失败, err = %v", workdir, err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ make.Seafile: 准备工作目录: %s 成功", workdir)
|
||||
|
||||
// 2. seafile yaml
|
||||
logger.Debug("☑️ make.Seafile: 准备 seafile yaml")
|
||||
bs := []byte(fmt.Sprintf(resource.YAMLSeafile, o.DBHost, o.DBPassword, o.AdminEmail, o.AdminPassword, o.ServerHostname, o.Storage))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "seafile.yaml"), bs, 0644); err != nil {
|
||||
logger.Error("❌ make.Seafile: 准备 seafile yaml: %s 失败, err = %v", filepath.Join(workdir, "seafile.yaml"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ make.Seafile: 准备 seafile yaml 成功")
|
||||
|
||||
// 3. backup-seafile deployment
|
||||
logger.Debug("☑️ make.Seafile: 准备 backup-seafile deployment")
|
||||
bs = []byte(resource.YAMLBackupSeafile)
|
||||
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), bs, 0644); err != nil {
|
||||
logger.Error("❌ make.Seafile: 准备 backup-seafile deployment: %s 失败, err = %v", filepath.Join(workdir, "deployment.yaml"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ make.Seafile: 准备 backup-seafile deployment 成功")
|
||||
|
||||
// 4. config.yml
|
||||
logger.Debug("☑️ make.Seafile: 准备 config.yml")
|
||||
bs = []byte(fmt.Sprintf(_config, o.DBHost, o.DBPassword, o.AdminEmail, o.AdminPassword))
|
||||
if err = os.WriteFile(filepath.Join(workdir, "config.yml"), bs, 0644); err != nil {
|
||||
logger.Error("❌ make.Seafile: 准备 config.yml: %s 失败, err = %v", filepath.Join(workdir, "config.yml"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ make.Seafile: 准备 config.yml 成功")
|
||||
|
||||
// 5. seafile.conf
|
||||
logger.Debug("☑️ make.Seafile: 准备 seafile.conf")
|
||||
bs = resource.NGINXSeafile
|
||||
if err = os.WriteFile(filepath.Join(workdir, "seafile.conf"), bs, 0644); err != nil {
|
||||
logger.Error("❌ make.Seafile: 准备 seafile.conf: %s 失败, err = %v", filepath.Join(workdir, "seafile.conf"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ make.Seafile: 准备 seafile.conf 成功")
|
||||
|
||||
// 6. upsert.sh
|
||||
logger.Debug("☑️ make.Seafile: 准备 upsert.sh")
|
||||
bs = []byte(_upsert)
|
||||
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), bs, 0755); err != nil {
|
||||
logger.Error("❌ make.Seafile: 准备 upsert.sh: %s 失败, err = %v", filepath.Join(workdir, "upsert.sh"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ make.Seafile: 准备 upsert.sh 成功")
|
||||
|
||||
// 7. prepare images
|
||||
logger.Debug("☑️ make.Seafile: 准备 images")
|
||||
imgDir := filepath.Join(m.workdir, "dependency", "image")
|
||||
if err = os.MkdirAll(imgDir, 0755); err != nil {
|
||||
logger.Error("❌ make.Seafile: 准备 images 目录: %s 失败, err = %v", imgDir, err)
|
||||
return err
|
||||
}
|
||||
var images = []*model.Image{
|
||||
{Name: "hub.yizhisec.com/hybridscope/hs_backup_seafile:latest", Fallback: "", Save: "seafile.backup_seafile.tar", Force: true},
|
||||
{Name: "hub.yizhisec.com/product/hybridscope/memcached:latest", Fallback: "", Save: "seafile.memcached.tar"},
|
||||
{Name: "hub.yizhisec.com/product/hybridscope/seafile-mc:latest", Fallback: "", Save: "seafile.seafile_mc.tar"},
|
||||
}
|
||||
for _, img := range images {
|
||||
img.Save = filepath.Join(imgDir, img.Save)
|
||||
if err = m.Image(ctx, img.Name, WithImageSave(img.Save), WithImageForcePull(img.Force)); err != nil {
|
||||
logger.Error("❌ make.Seafile: 准备 image: %s 失败, err = %v", img.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.Debug("✅ make.Seafile: 准备 images 成功")
|
||||
|
||||
logger.Info("✅ maker.Seafile: 构建 seafile 依赖成功!!!")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -2,47 +2,39 @@ package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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/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 (
|
||||
configTemplate = `
|
||||
_config = `
|
||||
Web:
|
||||
# default listen in docker0
|
||||
Host: 172.17.0.1
|
||||
Host: __ip__
|
||||
Port: 7788
|
||||
|
||||
UUIDFilePath: /etc/yosguard/uuid
|
||||
|
||||
# 心跳间隔: 单位秒,默认为5
|
||||
HeartbeatDuration: 5
|
||||
|
||||
# 控制器 yosguard 地址
|
||||
ControllerServer:
|
||||
Host: dasheng.zhsftech.debug
|
||||
Port: 443
|
||||
|
||||
# True: 作为控制器运行; False: 不作为控制器运行
|
||||
AsController: true
|
||||
|
||||
# True: 作为网关运行; False: 不作为网关运行
|
||||
AsGateway: false
|
||||
|
||||
Database:
|
||||
SQLite:
|
||||
DBPath: "/etc/yosguard/db/yosguard.db"
|
||||
SQLPath: "/etc/yosguard/db/create.sql"`
|
||||
DBPath: /etc/yosguard/db/yosguard.db
|
||||
HeartbeatDuration: 5
|
||||
UUIDFilePath: /etc/yosguard/uuid
|
||||
`
|
||||
|
||||
systemdService = `
|
||||
[Unit]
|
||||
@@ -64,10 +56,23 @@ WantedBy=multi-user.target`
|
||||
binURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/yosguard"
|
||||
)
|
||||
|
||||
location := filepath.Join(opt.Cfg.Make.Dir, "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
|
||||
@@ -87,12 +92,12 @@ WantedBy=multi-user.target`
|
||||
}
|
||||
logger.Debug("✅ maker.Yosguard: 下载 yosguard 成功, url = %s", binURL)
|
||||
|
||||
logger.Debug("☑️ maker.Yosguard: 写入 config_template.yml 文件..., dest = %s", filepath.Join(location, "config_template.yml"))
|
||||
if err := os.WriteFile(filepath.Join(location, "config_template.yml"), []byte(configTemplate), 0644); err != nil {
|
||||
logger.Debug("❌ maker.Yosguard: 写入 config_template.yml 失败, dest = %s, err = %v", filepath.Join(location, "config_template.yml"), err)
|
||||
logger.Debug("☑️ maker.Yosguard: 写入 config.yml 文件..., dest = %s", filepath.Join(location, "config.yml"))
|
||||
if err := os.WriteFile(filepath.Join(location, "config.yml"), []byte(_config), 0644); err != nil {
|
||||
logger.Debug("❌ maker.Yosguard: 写入 config.yml 失败, dest = %s, err = %v", filepath.Join(location, "config.yml"), err)
|
||||
return err
|
||||
}
|
||||
logger.Debug("✅ maker.Yosguard: 写入 config_template.yml 文件成功, dest = %s", filepath.Join(location, "config_template.yml"))
|
||||
logger.Debug("✅ maker.Yosguard: 写入 config.yml 文件成功, dest = %s", filepath.Join(location, "config.yml"))
|
||||
|
||||
logger.Debug("☑️ maker.Yosguard: 写入 create.sql 文件..., dest = %s", filepath.Join(location, "create.sql"))
|
||||
if err := os.WriteFile(filepath.Join(location, "create.sql"), resource.SQLYosguard, 0644); err != nil {
|
||||
@@ -108,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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package opt
|
||||
|
||||
import "regexp"
|
||||
|
||||
type config struct {
|
||||
Debug bool
|
||||
Make struct {
|
||||
@@ -11,3 +13,12 @@ type config struct {
|
||||
var (
|
||||
Cfg = &config{}
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultWorkdir = "/root/hsv2-installation"
|
||||
)
|
||||
|
||||
var (
|
||||
StorageSizeReg = regexp.MustCompile(`^\d+(\.\d+)?[EPTGMK]i?$`)
|
||||
EmailReg = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||||
)
|
||||
|
||||
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"
|
||||
)
|
||||
5
main.go
5
main.go
@@ -5,10 +5,15 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"yizhisec.com/hsv2/forge/internal/cmd"
|
||||
)
|
||||
|
||||
func init() {
|
||||
time.Local = time.FixedZone("CST", 8*60*60)
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
648
pkg/imager/pull.go
Normal file
648
pkg/imager/pull.go
Normal file
@@ -0,0 +1,648 @@
|
||||
package imager
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
// PullOption is a functional option for configuring image pull
|
||||
type PullOption func(*pullOption)
|
||||
|
||||
type pullOption struct {
|
||||
Proxy string // http or socks5 proxy
|
||||
Rename string // pull image and rename it
|
||||
PlainHTTP bool // use http instead of https
|
||||
SkipTLSVerify bool // skip TLS certificate verification
|
||||
Username string // registry username for authentication
|
||||
Password string // registry password for authentication
|
||||
RetryTimes int // retry times for sync/proxy registries, default is 3
|
||||
RetryDelay time.Duration // delay between retries, default is 2 seconds
|
||||
}
|
||||
|
||||
// WithProxy sets the proxy for pulling images
|
||||
func WithProxy(proxy string) PullOption {
|
||||
return func(o *pullOption) {
|
||||
o.Proxy = proxy
|
||||
}
|
||||
}
|
||||
|
||||
// WithRename sets a new name for the pulled image
|
||||
func WithRename(name string) PullOption {
|
||||
return func(o *pullOption) {
|
||||
o.Rename = name
|
||||
}
|
||||
}
|
||||
|
||||
// WithPlainHTTP enables plain HTTP (no TLS)
|
||||
func WithPlainHTTP() PullOption {
|
||||
return func(o *pullOption) {
|
||||
o.PlainHTTP = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithSkipTLSVerify skips TLS certificate verification
|
||||
func WithSkipTLSVerify() PullOption {
|
||||
return func(o *pullOption) {
|
||||
o.SkipTLSVerify = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithAuth sets authentication credentials
|
||||
func WithAuth(username, password string) PullOption {
|
||||
return func(o *pullOption) {
|
||||
o.Username = username
|
||||
o.Password = password
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetry sets the retry times for pulling images
|
||||
func WithRetry(times int) PullOption {
|
||||
return func(o *pullOption) {
|
||||
if times < 0 {
|
||||
times = 0
|
||||
}
|
||||
o.RetryTimes = times
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetryDelay sets the delay between retries
|
||||
func WithRetryDelay(delay time.Duration) PullOption {
|
||||
return func(o *pullOption) {
|
||||
o.RetryDelay = delay
|
||||
}
|
||||
}
|
||||
|
||||
// manifestV2 represents Docker manifest v2 schema
|
||||
type manifestV2 struct {
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
MediaType string `json:"mediaType"`
|
||||
Config struct {
|
||||
MediaType string `json:"mediaType"`
|
||||
Size int64 `json:"size"`
|
||||
Digest string `json:"digest"`
|
||||
} `json:"config"`
|
||||
Layers []struct {
|
||||
MediaType string `json:"mediaType"`
|
||||
Size int64 `json:"size"`
|
||||
Digest string `json:"digest"`
|
||||
} `json:"layers"`
|
||||
}
|
||||
|
||||
// imageReference parses image reference into registry, repository, and tag
|
||||
type imageReference struct {
|
||||
Registry string
|
||||
Repository string
|
||||
Tag string
|
||||
}
|
||||
|
||||
// parseImageReference parses an image name into its components
|
||||
func parseImageReference(name string) (*imageReference, error) {
|
||||
ref := &imageReference{
|
||||
Tag: "latest",
|
||||
}
|
||||
|
||||
// First, split by / to separate registry from repository
|
||||
// This handles cases like localhost:5000/myimage:tag
|
||||
slashParts := strings.SplitN(name, "/", 2)
|
||||
|
||||
var registryPart, repoPart string
|
||||
|
||||
if len(slashParts) == 1 {
|
||||
// No slash, it's just image:tag (e.g., alpine:3.19)
|
||||
repoPart = slashParts[0]
|
||||
ref.Registry = "registry-1.docker.io"
|
||||
} else {
|
||||
// Has slash, check if first part is a registry
|
||||
// Registry contains . or : (for host:port)
|
||||
if strings.Contains(slashParts[0], ".") || strings.Contains(slashParts[0], ":") {
|
||||
// First part is a registry
|
||||
registryPart = slashParts[0]
|
||||
repoPart = slashParts[1]
|
||||
ref.Registry = registryPart
|
||||
} else {
|
||||
// First part is namespace (e.g., library/alpine)
|
||||
repoPart = name
|
||||
ref.Registry = "registry-1.docker.io"
|
||||
}
|
||||
}
|
||||
|
||||
// Now parse the repository and tag from repoPart
|
||||
// Split on last : to get tag (in case repository name contains /)
|
||||
lastColon := strings.LastIndex(repoPart, ":")
|
||||
if lastColon > 0 {
|
||||
ref.Repository = repoPart[:lastColon]
|
||||
ref.Tag = repoPart[lastColon+1:]
|
||||
} else {
|
||||
ref.Repository = repoPart
|
||||
}
|
||||
|
||||
// For docker.io, add library/ prefix if no namespace
|
||||
if ref.Registry == "registry-1.docker.io" && !strings.Contains(ref.Repository, "/") {
|
||||
ref.Repository = "library/" + ref.Repository
|
||||
}
|
||||
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
// PullImage pulls an OCI image using HTTP requests and saves it as a tar archive
|
||||
func PullImage(ctx context.Context, name string, store io.Writer, opts ...PullOption) error {
|
||||
options := &pullOption{
|
||||
RetryTimes: 3, // default retry times
|
||||
RetryDelay: 2 * time.Second, // default retry delay
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
||||
logger.Debug("Pulling image: %s with options: %+v", name, options)
|
||||
|
||||
// Parse image reference
|
||||
ref, err := parseImageReference(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse image reference: %w", err)
|
||||
}
|
||||
|
||||
logger.Debug("Parsed image reference: registry=%s, repository=%s, tag=%s", ref.Registry, ref.Repository, ref.Tag)
|
||||
|
||||
// Create HTTP client
|
||||
client := createHTTPClient(options)
|
||||
|
||||
// Retry logic for sync/proxy registries
|
||||
var lastErr error
|
||||
for attempt := 0; attempt <= options.RetryTimes; attempt++ {
|
||||
if attempt > 0 {
|
||||
logger.Debug("Retry attempt %d/%d for image: %s", attempt, options.RetryTimes, name)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(options.RetryDelay):
|
||||
// Continue after delay
|
||||
}
|
||||
}
|
||||
|
||||
// Get auth token if needed
|
||||
token, err := getAuthToken(ctx, client, ref, options)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("failed to get auth token: %w", err)
|
||||
logger.Debug("Attempt %d failed: %v", attempt+1, lastErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// Get manifest
|
||||
manifest, err := getManifest(ctx, client, ref, token, options)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("failed to get manifest: %w", err)
|
||||
logger.Debug("Attempt %d failed: %v", attempt+1, lastErr)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Debug("Got manifest with %d layers", len(manifest.Layers))
|
||||
|
||||
// Download and create tar archive
|
||||
if err := createImageTar(ctx, client, ref, manifest, token, store, name, options); err != nil {
|
||||
lastErr = fmt.Errorf("failed to create image tar: %w", err)
|
||||
logger.Debug("Attempt %d failed: %v", attempt+1, lastErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// Success!
|
||||
logger.Info("Successfully pulled image: %s", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// All retries exhausted
|
||||
if options.RetryTimes > 0 {
|
||||
return fmt.Errorf("failed to pull image after %d retries: %w", options.RetryTimes+1, lastErr)
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// createHTTPClient creates an HTTP client with the given options
|
||||
func createHTTPClient(options *pullOption) *http.Client {
|
||||
client := &http.Client{}
|
||||
|
||||
// Configure TLS
|
||||
if options.SkipTLSVerify {
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client.Transport = transport
|
||||
}
|
||||
|
||||
// Configure proxy
|
||||
if options.Proxy != "" {
|
||||
proxyURL, err := url.Parse(options.Proxy)
|
||||
if err == nil {
|
||||
if client.Transport == nil {
|
||||
client.Transport = &http.Transport{}
|
||||
}
|
||||
if transport, ok := client.Transport.(*http.Transport); ok {
|
||||
transport.Proxy = http.ProxyURL(proxyURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// getAuthToken gets authentication token from registry
|
||||
func getAuthToken(ctx context.Context, client *http.Client, ref *imageReference, options *pullOption) (string, error) {
|
||||
// Try to access registry API v2 to get auth challenge
|
||||
scheme := "https"
|
||||
if options.PlainHTTP {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
apiURL := fmt.Sprintf("%s://%s/v2/", scheme, ref.Registry)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Add basic auth if provided
|
||||
if options.Username != "" && options.Password != "" {
|
||||
req.SetBasicAuth(options.Username, options.Password)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// If 200 OK, no auth needed
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Check for WWW-Authenticate header
|
||||
authHeader := resp.Header.Get("WWW-Authenticate")
|
||||
if authHeader == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Parse auth challenge
|
||||
token, err := fetchToken(ctx, client, authHeader, ref, options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// fetchToken fetches authentication token from auth server
|
||||
func fetchToken(ctx context.Context, client *http.Client, authHeader string, ref *imageReference, options *pullOption) (string, error) {
|
||||
// Parse WWW-Authenticate header
|
||||
// Format: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/alpine:pull"
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
params := make(map[string]string)
|
||||
parts := strings.Split(authHeader[7:], ",")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
idx := strings.Index(part, "=")
|
||||
if idx > 0 {
|
||||
key := part[:idx]
|
||||
value := strings.Trim(part[idx+1:], "\"")
|
||||
params[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
realm, ok := params["realm"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no realm in auth header")
|
||||
}
|
||||
|
||||
// Build token URL
|
||||
tokenURL := realm
|
||||
if service, ok := params["service"]; ok {
|
||||
tokenURL += "?service=" + url.QueryEscape(service)
|
||||
}
|
||||
scope := fmt.Sprintf("repository:%s:pull", ref.Repository)
|
||||
if params["scope"] != "" {
|
||||
scope = params["scope"]
|
||||
}
|
||||
tokenURL += "&scope=" + url.QueryEscape(scope)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", tokenURL, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Add basic auth if provided
|
||||
if options.Username != "" && options.Password != "" {
|
||||
req.SetBasicAuth(options.Username, options.Password)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("failed to get token: %s", resp.Status)
|
||||
}
|
||||
|
||||
var tokenResp struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokenResp.Token, nil
|
||||
}
|
||||
|
||||
// getManifest fetches the image manifest
|
||||
func getManifest(ctx context.Context, client *http.Client, ref *imageReference, token string, options *pullOption) (*manifestV2, error) {
|
||||
scheme := "https"
|
||||
if options.PlainHTTP {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
manifestURL := fmt.Sprintf("%s://%s/v2/%s/manifests/%s", scheme, ref.Registry, ref.Repository, ref.Tag)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", manifestURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set accept headers for manifest v2 (add both types)
|
||||
req.Header.Add("Accept", "application/vnd.docker.distribution.manifest.v2+json")
|
||||
req.Header.Add("Accept", "application/vnd.oci.image.manifest.v1+json")
|
||||
req.Header.Add("Accept", "application/vnd.docker.distribution.manifest.list.v2+json")
|
||||
|
||||
if token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
}
|
||||
|
||||
logger.Debug("Fetching manifest from: %s", manifestURL)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
logger.Debug("Manifest request failed: %s, body: %s", resp.Status, string(body))
|
||||
return nil, fmt.Errorf("failed to get manifest: %s", resp.Status)
|
||||
}
|
||||
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
logger.Debug("Manifest Content-Type: %s", contentType)
|
||||
|
||||
// Read response body
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if it's a manifest list (multi-arch)
|
||||
if strings.Contains(contentType, "manifest.list") || strings.Contains(contentType, "image.index") {
|
||||
// Parse manifest list and select amd64/linux
|
||||
var manifestList struct {
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
Manifests []struct {
|
||||
Digest string `json:"digest"`
|
||||
Platform struct {
|
||||
Architecture string `json:"architecture"`
|
||||
OS string `json:"os"`
|
||||
} `json:"platform"`
|
||||
} `json:"manifests"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &manifestList); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse manifest list: %w", err)
|
||||
}
|
||||
|
||||
logger.Debug("Found manifest list with %d manifests", len(manifestList.Manifests))
|
||||
|
||||
// Find amd64/linux manifest
|
||||
var targetDigest string
|
||||
for _, m := range manifestList.Manifests {
|
||||
logger.Debug("Checking manifest: arch=%s, os=%s, digest=%s",
|
||||
m.Platform.Architecture, m.Platform.OS, m.Digest)
|
||||
if m.Platform.Architecture == "amd64" && m.Platform.OS == "linux" {
|
||||
targetDigest = m.Digest
|
||||
break
|
||||
}
|
||||
}
|
||||
if targetDigest == "" && len(manifestList.Manifests) > 0 {
|
||||
// Fallback to first manifest with known platform
|
||||
for _, m := range manifestList.Manifests {
|
||||
if m.Platform.Architecture != "unknown" && m.Platform.OS != "unknown" {
|
||||
targetDigest = m.Digest
|
||||
logger.Debug("Using fallback manifest: arch=%s, os=%s",
|
||||
m.Platform.Architecture, m.Platform.OS)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if targetDigest == "" {
|
||||
return nil, fmt.Errorf("no suitable manifest found in manifest list")
|
||||
}
|
||||
|
||||
logger.Debug("Selected manifest digest: %s", targetDigest)
|
||||
// Fetch the actual manifest by digest
|
||||
return getManifestByDigest(ctx, client, ref, targetDigest, token, options)
|
||||
}
|
||||
|
||||
var manifest manifestV2
|
||||
if err := json.Unmarshal(body, &manifest); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse manifest: %w", err)
|
||||
}
|
||||
|
||||
logger.Debug("Parsed manifest: schemaVersion=%d, config.digest=%s, layers=%d",
|
||||
manifest.SchemaVersion, manifest.Config.Digest, len(manifest.Layers))
|
||||
|
||||
return &manifest, nil
|
||||
}
|
||||
|
||||
// getManifestByDigest fetches a manifest by its digest
|
||||
func getManifestByDigest(ctx context.Context, client *http.Client, ref *imageReference, digest, token string, options *pullOption) (*manifestV2, error) {
|
||||
scheme := "https"
|
||||
if options.PlainHTTP {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
manifestURL := fmt.Sprintf("%s://%s/v2/%s/manifests/%s", scheme, ref.Registry, ref.Repository, digest)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", manifestURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "application/vnd.docker.distribution.manifest.v2+json")
|
||||
req.Header.Add("Accept", "application/vnd.oci.image.manifest.v1+json")
|
||||
|
||||
if token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to get manifest by digest: %s", resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var manifest manifestV2
|
||||
if err := json.Unmarshal(body, &manifest); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse manifest: %w", err)
|
||||
}
|
||||
|
||||
return &manifest, nil
|
||||
}
|
||||
|
||||
// createImageTar downloads layers and creates a Docker-compatible tar archive
|
||||
func createImageTar(ctx context.Context, client *http.Client, ref *imageReference, manifest *manifestV2, token string, store io.Writer, imageName string, options *pullOption) error {
|
||||
// Create tar writer
|
||||
tw := tar.NewWriter(store)
|
||||
defer tw.Close()
|
||||
|
||||
scheme := "https"
|
||||
if options.PlainHTTP {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
// Download config blob
|
||||
logger.Debug("Downloading config blob: %s", manifest.Config.Digest)
|
||||
configBlob, err := downloadBlob(ctx, client, scheme, ref.Registry, ref.Repository, manifest.Config.Digest, token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download config: %w", err)
|
||||
}
|
||||
|
||||
// Write config as json file
|
||||
configFileName := strings.TrimPrefix(manifest.Config.Digest, "sha256:") + ".json"
|
||||
if err := writeTarEntry(tw, configFileName, configBlob); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Download and write each layer
|
||||
var layerFiles []string
|
||||
for i, layer := range manifest.Layers {
|
||||
logger.Debug("Downloading layer %d/%d: %s", i+1, len(manifest.Layers), layer.Digest)
|
||||
layerBlob, err := downloadBlob(ctx, client, scheme, ref.Registry, ref.Repository, layer.Digest, token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download layer %s: %w", layer.Digest, err)
|
||||
}
|
||||
|
||||
layerFileName := strings.TrimPrefix(layer.Digest, "sha256:") + "/layer.tar"
|
||||
layerFiles = append(layerFiles, layerFileName)
|
||||
|
||||
if err := writeTarEntry(tw, layerFileName, layerBlob); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create manifest.json for Docker compatibility
|
||||
if options.Rename != "" {
|
||||
imageName = options.Rename
|
||||
}
|
||||
|
||||
manifestJSON := []map[string]interface{}{
|
||||
{
|
||||
"Config": configFileName,
|
||||
"RepoTags": []string{imageName},
|
||||
"Layers": layerFiles,
|
||||
},
|
||||
}
|
||||
|
||||
manifestData, err := json.Marshal(manifestJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeTarEntry(tw, "manifest.json", manifestData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create repositories file (legacy)
|
||||
repositories := make(map[string]map[string]string)
|
||||
repositories[imageName] = map[string]string{ref.Tag: strings.TrimPrefix(manifest.Config.Digest, "sha256:")[:12]}
|
||||
reposData, err := json.Marshal(repositories)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeTarEntry(tw, "repositories", reposData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// downloadBlob downloads a blob from registry
|
||||
func downloadBlob(ctx context.Context, client *http.Client, scheme, registry, repository, digest, token string) ([]byte, error) {
|
||||
blobURL := fmt.Sprintf("%s://%s/v2/%s/blobs/%s", scheme, registry, repository, digest)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", blobURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to download blob: %s", resp.Status)
|
||||
}
|
||||
|
||||
// Check if content is gzipped
|
||||
var reader io.Reader = resp.Body
|
||||
if resp.Header.Get("Content-Type") == "application/vnd.docker.image.rootfs.diff.tar.gzip" ||
|
||||
strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {
|
||||
gzReader, err := gzip.NewReader(resp.Body)
|
||||
if err == nil {
|
||||
defer gzReader.Close()
|
||||
reader = gzReader
|
||||
}
|
||||
}
|
||||
|
||||
return io.ReadAll(reader)
|
||||
}
|
||||
|
||||
// writeTarEntry writes a file entry to tar archive
|
||||
func writeTarEntry(tw *tar.Writer, name string, data []byte) error {
|
||||
hdr := &tar.Header{
|
||||
Name: name,
|
||||
Mode: 0644,
|
||||
Size: int64(len(data)),
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tw.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
429
pkg/imager/pull_test.go
Normal file
429
pkg/imager/pull_test.go
Normal file
@@ -0,0 +1,429 @@
|
||||
package imager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"yizhisec.com/hsv2/forge/pkg/logger"
|
||||
)
|
||||
|
||||
// TestPullImage_PublicImage tests pulling a public image from Docker Hub
|
||||
func TestPullImage_PublicImage(t *testing.T) {
|
||||
logger.SetLogLevel(logger.LogLevelDebug)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Create temp directory for test output
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "alpine.tar")
|
||||
outputFile = "./redis.alpine.tar"
|
||||
|
||||
// Create output file
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Pull alpine image (small and commonly available)
|
||||
imageName := "docker-mirror.yizhisec.com/library/redis:alpine"
|
||||
err = PullImage(ctx, imageName, f)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
// Verify file was created and has content
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithRegistry tests pulling from a specific registry
|
||||
func TestPullImage_WithRegistry(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "nginx.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Pull from docker.io with explicit registry
|
||||
imageName := "docker.io/library/nginx:alpine"
|
||||
err = PullImage(ctx, imageName, f)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithRename tests pulling and renaming an image
|
||||
func TestPullImage_WithRename(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "renamed.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
imageName := "alpine:3.19"
|
||||
newName := "my-custom-alpine:latest"
|
||||
|
||||
err = PullImage(ctx, imageName, f, WithRename(newName))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s and renamed to %s, size: %d bytes", imageName, newName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithAuth tests pulling with authentication (skip if no credentials)
|
||||
func TestPullImage_WithAuth(t *testing.T) {
|
||||
// This test requires actual credentials, so it's skipped by default
|
||||
t.Skip("Skipping authentication test - requires valid credentials")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "private.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Replace with your private registry details
|
||||
imageName := "your-registry.com/your-repo/your-image:tag"
|
||||
username := "your-username"
|
||||
password := "your-password"
|
||||
|
||||
err = PullImage(ctx, imageName, f, WithAuth(username, password))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull private image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled private image %s, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithRetry tests retry functionality
|
||||
func TestPullImage_WithRetry(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "retry.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Pull with custom retry settings
|
||||
imageName := "alpine:3.19"
|
||||
err = PullImage(ctx, imageName, f,
|
||||
WithRetry(5), // 5 retries
|
||||
WithRetryDelay(1*time.Second), // 1 second delay
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s with retry, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithZeroRetry tests with retry disabled
|
||||
func TestPullImage_WithZeroRetry(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "no-retry.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Pull with retry disabled
|
||||
imageName := "alpine:3.19"
|
||||
err = PullImage(ctx, imageName, f, WithRetry(0))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s without retry, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithSkipTLSVerify tests pulling with TLS verification disabled
|
||||
func TestPullImage_WithSkipTLSVerify(t *testing.T) {
|
||||
// This test is for registries with self-signed certificates
|
||||
t.Skip("Skipping TLS skip test - requires a registry with self-signed cert")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "insecure.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
imageName := "your-insecure-registry.local/image:tag"
|
||||
err = PullImage(ctx, imageName, f, WithSkipTLSVerify())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s with TLS verification skipped, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestParseImageReference tests the image reference parsing logic
|
||||
func TestParseImageReference(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
imageName string
|
||||
wantRegistry string
|
||||
wantRepository string
|
||||
wantTag string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "simple image with tag",
|
||||
imageName: "alpine:3.19",
|
||||
wantRegistry: "registry-1.docker.io",
|
||||
wantRepository: "library/alpine",
|
||||
wantTag: "3.19",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "simple image without tag",
|
||||
imageName: "alpine",
|
||||
wantRegistry: "registry-1.docker.io",
|
||||
wantRepository: "library/alpine",
|
||||
wantTag: "latest",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "image with namespace",
|
||||
imageName: "library/nginx:alpine",
|
||||
wantRegistry: "registry-1.docker.io",
|
||||
wantRepository: "library/nginx",
|
||||
wantTag: "alpine",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom registry",
|
||||
imageName: "hub.yizhisec.com/external/alpine:3.22.2",
|
||||
wantRegistry: "hub.yizhisec.com",
|
||||
wantRepository: "external/alpine",
|
||||
wantTag: "3.22.2",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "docker.io explicit",
|
||||
imageName: "docker.io/library/redis:8.2.2",
|
||||
wantRegistry: "docker.io",
|
||||
wantRepository: "library/redis",
|
||||
wantTag: "8.2.2",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "registry with port",
|
||||
imageName: "localhost:5000/myimage:v1.0",
|
||||
wantRegistry: "localhost:5000",
|
||||
wantRepository: "myimage",
|
||||
wantTag: "v1.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "quay.io registry",
|
||||
imageName: "quay.io/k0sproject/pause:3.10.1",
|
||||
wantRegistry: "quay.io",
|
||||
wantRepository: "k0sproject/pause",
|
||||
wantTag: "3.10.1",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ref, err := parseImageReference(tt.imageName)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseImageReference() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if ref.Registry != tt.wantRegistry {
|
||||
t.Errorf("parseImageReference() registry = %v, want %v", ref.Registry, tt.wantRegistry)
|
||||
}
|
||||
if ref.Repository != tt.wantRepository {
|
||||
t.Errorf("parseImageReference() repository = %v, want %v", ref.Repository, tt.wantRepository)
|
||||
}
|
||||
if ref.Tag != tt.wantTag {
|
||||
t.Errorf("parseImageReference() tag = %v, want %v", ref.Tag, tt.wantTag)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestPullImage_ContextCancellation tests that context cancellation works
|
||||
func TestPullImage_ContextCancellation(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "cancelled.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Cancel context immediately
|
||||
cancel()
|
||||
|
||||
imageName := "alpine:3.19"
|
||||
err = PullImage(ctx, imageName, f)
|
||||
if err == nil {
|
||||
t.Error("expected error due to cancelled context, got nil")
|
||||
}
|
||||
|
||||
t.Logf("Context cancellation correctly handled: %v", err)
|
||||
}
|
||||
|
||||
// BenchmarkPullImage benchmarks image pulling performance
|
||||
func BenchmarkPullImage(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
tmpDir := b.TempDir()
|
||||
|
||||
imageName := "alpine:3.19"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
outputFile := filepath.Join(tmpDir, "alpine_bench.tar")
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
|
||||
err = PullImage(ctx, imageName, f)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to pull image: %v", err)
|
||||
}
|
||||
|
||||
f.Close()
|
||||
os.Remove(outputFile)
|
||||
}
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
55
pkg/model/caddy.go
Normal file
55
pkg/model/caddy.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package model
|
||||
|
||||
type CaddyUpstream struct {
|
||||
Dial []string `json:"dial"`
|
||||
}
|
||||
|
||||
type CaddyActive struct {
|
||||
Interval string `json:"interval"`
|
||||
Timeout string `json:"timeout"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
type CaddyPassive struct {
|
||||
MaxFails int `json:"max_fails"`
|
||||
FailDuration string `json:"fail_duration"`
|
||||
}
|
||||
|
||||
type CaddyHealthCheck struct {
|
||||
Active *CaddyActive `json:"active"`
|
||||
Passive *CaddyPassive `json:"passive"`
|
||||
}
|
||||
|
||||
type CaddySelection struct {
|
||||
Policy string `json:"policy"`
|
||||
}
|
||||
|
||||
type CaddyLoadBalancing struct {
|
||||
Selection *CaddySelection `json:"selection"`
|
||||
}
|
||||
|
||||
type CaddyHandle struct {
|
||||
Handler string `json:"handler"`
|
||||
Upstreams []*CaddyUpstream `json:"upstreams"`
|
||||
HealthChecks *CaddyHealthCheck `json:"health_checks"`
|
||||
LoadBalancing *CaddyLoadBalancing `json:"load_balancing"`
|
||||
}
|
||||
|
||||
type CaddyRoute struct {
|
||||
Handle []*CaddyHandle `json:"handle"`
|
||||
}
|
||||
|
||||
type CaddyServer struct {
|
||||
Listen []string `json:"listen"`
|
||||
Routes []*CaddyRoute `json:"routes"`
|
||||
}
|
||||
|
||||
type CaddyLayer4 struct {
|
||||
Servers map[string]*CaddyServer `json:"servers"`
|
||||
}
|
||||
|
||||
type CaddyApp struct {
|
||||
Layer4 *CaddyLayer4 `json:"layer4"`
|
||||
}
|
||||
|
||||
type CaddyConfig map[string]*CaddyApp
|
||||
8
pkg/model/image.go
Normal file
8
pkg/model/image.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package model
|
||||
|
||||
type Image struct {
|
||||
Name string
|
||||
Fallback string
|
||||
Save string
|
||||
Force bool
|
||||
}
|
||||
58
pkg/model/vendor.go
Normal file
58
pkg/model/vendor.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package model
|
||||
|
||||
type Vendor struct {
|
||||
Name string
|
||||
OEMUrl string
|
||||
OEMDir string
|
||||
AppFrontUserImageName string
|
||||
AppFrontAdminImageName string
|
||||
}
|
||||
|
||||
var (
|
||||
vendorMap = map[string]*Vendor{
|
||||
"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": {
|
||||
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": {
|
||||
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": {
|
||||
Name: "heishuimeng",
|
||||
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_heishuimeng.tar.gz",
|
||||
OEMDir: "oem_heishuimeng",
|
||||
AppFrontUserImageName: "hub.yizhisec.com/hybridscope/v2/front-user:latest",
|
||||
AppFrontAdminImageName: "hub.yizhisec.com/build/hybirdscope/front/admin:latest",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func GetVendor(name string) *Vendor {
|
||||
if vendor, ok := vendorMap[name]; ok {
|
||||
return vendor
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetVendorNames() []string {
|
||||
names := make([]string, 0, len(vendorMap))
|
||||
for name := range vendorMap {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
73
pkg/resource/nginx/caddy.json
Normal file
73
pkg/resource/nginx/caddy.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"apps": {
|
||||
"layer4": {
|
||||
"servers": {
|
||||
"proxy_8443_tcp_backends": {
|
||||
"listen": [":8443"],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"handler": "proxy",
|
||||
"upstreams": [
|
||||
{"dial": ["10.118.2.11:32443"]},
|
||||
{"dial": ["10.118.2.12:32443"]}
|
||||
],
|
||||
"health_checks": {
|
||||
"active": {
|
||||
"interval": "5s",
|
||||
"timeout": "2s",
|
||||
"port": 32443
|
||||
},
|
||||
"passive": {
|
||||
"max_fails": 1,
|
||||
"fail_duration": "30s"
|
||||
}
|
||||
},
|
||||
"load_balancing": {
|
||||
"selection": {
|
||||
"policy": "round_robin"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"proxy_443_tcp_backends": {
|
||||
"listen": [":443"],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"handler": "proxy",
|
||||
"upstreams": [
|
||||
{"dial": ["10.118.2.11:31443"]},
|
||||
{"dial": ["10.118.2.12:31443"]}
|
||||
],
|
||||
"health_checks": {
|
||||
"active": {
|
||||
"interval": "5s",
|
||||
"timeout": "2s",
|
||||
"port": 31443
|
||||
},
|
||||
"passive": {
|
||||
"max_fails": 1,
|
||||
"fail_duration": "30s"
|
||||
}
|
||||
},
|
||||
"load_balancing": {
|
||||
"selection": {
|
||||
"policy": "round_robin"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
255
pkg/resource/nginx/client.conf
Normal file
255
pkg/resource/nginx/client.conf
Normal file
@@ -0,0 +1,255 @@
|
||||
upstream hs-client-server {
|
||||
least_conn;
|
||||
server client-service:9129 max_fails=3 fail_timeout=10s;
|
||||
}
|
||||
|
||||
upstream hs-client-without-auth-server {
|
||||
least_conn;
|
||||
server client-service:9024 max_fails=3 fail_timeout=10s;
|
||||
}
|
||||
|
||||
upstream hs-client-message-server {
|
||||
least_conn;
|
||||
server client-service:9025 max_fails=3 fail_timeout=10s;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl
|
||||
proxy_protocol;
|
||||
server_name hs-client-api-server hs.client.api.server;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/client.server.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/client.server.key;
|
||||
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /api/v1/pkg/archive {
|
||||
proxy_pass http://client-win-service.hsv2/api/v2_2/_client/win/check.json;
|
||||
}
|
||||
|
||||
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/ {
|
||||
proxy_pass http://hs-client-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 /api/v1/ {
|
||||
proxy_pass http://hs-client-without-auth-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 /api/v1/dl/ {
|
||||
proxy_pass http://hs-client-without-auth-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 /api/v2/admin/ {
|
||||
proxy_pass http://hs-client-message-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 /app-store {
|
||||
# auth_request /app-store-auth;
|
||||
# rewrite ^/app-store(.*)$ $1 break;
|
||||
# proxy_pass http://hs-resource-server:19980;
|
||||
# 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 /app-store-auth {
|
||||
internal;
|
||||
proxy_pass http://hs-client-server/auth$request_uri;
|
||||
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_set_header Content-Length "";
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Original-Method $request_method;
|
||||
proxy_set_header X-Original-IP $remote_addr;
|
||||
proxy_set_header Query-Data $http_query_data;
|
||||
}
|
||||
|
||||
# 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 /static/config/ {
|
||||
alias /static/config/;
|
||||
}
|
||||
|
||||
location /static/resource/ {
|
||||
alias /static/resource/;
|
||||
}
|
||||
|
||||
location /user/avatar/ {
|
||||
alias /static/avatar/;
|
||||
add_header Cache-Control public;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
server {
|
||||
listen 443 ssl proxy_protocol;
|
||||
server_name hs-client-update-server hs.client.update.server;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/client.server.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/client.server.key;
|
||||
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
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://app-helper-service.hsv2/api/v2_2/_client/win/old/version.json;
|
||||
}
|
||||
|
||||
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://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 {
|
||||
# proxy_pass http://hs-client-without-auth-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 /api/v1/pkg {
|
||||
proxy_pass http://hs-client-without-auth-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 /api/v1/dl/ {
|
||||
proxy_pass http://hs-client-without-auth-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 /user/avatar/ {
|
||||
alias /static/avatar/;
|
||||
expires 7d;
|
||||
add_header Cache-Control public;
|
||||
}
|
||||
|
||||
location /static/config/ {
|
||||
alias /static/config/;
|
||||
}
|
||||
|
||||
location /static/resource/ {
|
||||
alias /static/resource/;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9118 ssl
|
||||
proxy_protocol;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/client.server.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/client.server.key;
|
||||
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
client_max_body_size 50M;
|
||||
location = /api/v1/version {
|
||||
proxy_pass http://hs-client-without-auth-server;
|
||||
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 /api/v1/pkg {
|
||||
proxy_pass http://hs-client-without-auth-server;
|
||||
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 /api/v1/dl/ {
|
||||
proxy_pass http://hs-client-without-auth-server;
|
||||
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;
|
||||
}
|
||||
}
|
||||
28
pkg/resource/nginx/client_pkg.conf
Normal file
28
pkg/resource/nginx/client_pkg.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
user root;
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
log_format custom '$time_iso8601 - $remote_addr - $http_host - $status - $request_time - $request_method - $request_uri';
|
||||
access_log /var/log/nginx/access.log custom;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
|
||||
gzip on;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location %s {
|
||||
alias /data;
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
}
|
||||
204
pkg/resource/nginx/common.conf
Normal file
204
pkg/resource/nginx/common.conf
Normal file
@@ -0,0 +1,204 @@
|
||||
ssl_certificate /etc/nginx/ssl/web.server.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/web.server.key;
|
||||
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
|
||||
ssl_session_tickets off;
|
||||
|
||||
ssl_dhparam /etc/nginx/ssl/ffdhe2048.txt;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
client_header_buffer_size 1k;
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
location = /token_auth {
|
||||
internal;
|
||||
proxy_pass http://hs-api/api/tokenauth;
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
}
|
||||
|
||||
location /client/dl/android {
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
default_type application/octet-stream;
|
||||
alias /static/client/android;
|
||||
if ($arg_attname ~ "^(.+)") {
|
||||
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
|
||||
}
|
||||
}
|
||||
|
||||
location /client/dl/ {
|
||||
# remove download client auth verify
|
||||
# auth_request /token_auth;
|
||||
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
default_type application/octet-stream;
|
||||
alias /static/client/;
|
||||
if ($arg_attname ~ "^(.+)") {
|
||||
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
|
||||
}
|
||||
}
|
||||
|
||||
location /file/share/ {
|
||||
auth_request /token_auth;
|
||||
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
alias /static/share_file/;
|
||||
if ($arg_attname ~ "^(.+)") {
|
||||
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
|
||||
}
|
||||
}
|
||||
|
||||
location /file/public/ {
|
||||
auth_request /token_auth;
|
||||
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
alias /static/public_folder/;
|
||||
if ($arg_attname ~ "^(.+)") {
|
||||
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
|
||||
}
|
||||
}
|
||||
|
||||
location /file/clipboard/ {
|
||||
auth_request /token_auth;
|
||||
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
alias /static/clipboard_file/;
|
||||
}
|
||||
|
||||
location /file/uploaded/ {
|
||||
auth_request /token_auth;
|
||||
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
alias /static/uploaded_files/;
|
||||
if ($arg_attname ~ "^(.+)") {
|
||||
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
|
||||
}
|
||||
}
|
||||
|
||||
location /resource/update_log.csv {
|
||||
auth_request /token_auth;
|
||||
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
default_type application/octet-stream;
|
||||
alias /static/resource/update_log.csv;
|
||||
}
|
||||
|
||||
location /resource/update_timestamp.txt {
|
||||
auth_request /token_auth;
|
||||
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
default_type application/octet-stream;
|
||||
alias /static/resource/update_timestamp.txt;
|
||||
}
|
||||
|
||||
location /resource/ {
|
||||
default_type application/octet-stream;
|
||||
alias /static/resource/;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://hs-api;
|
||||
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 /network-disk {
|
||||
# set $arg_token ''; # 声明 $arg_token 变量
|
||||
# if ($args ~* "token=(.*?)(&|$)") {
|
||||
# set $arg_token $1;
|
||||
# }
|
||||
# auth_request /token_auth;
|
||||
# set $auth $http_authorization;
|
||||
# if ($http_authorization = "") {
|
||||
# set $auth "token $arg_authorization";
|
||||
# }
|
||||
# rewrite ^/network-disk(.*)$ $1 break;
|
||||
# proxy_pass http://hs-resource-server:19980;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Authorization $auth;
|
||||
# }
|
||||
|
||||
# location /app-store {
|
||||
# set $arg_token ''; # 声明 $arg_token 变量
|
||||
# if ($args ~* "token=(.*?)(&|$)") {
|
||||
# set $arg_token $1;
|
||||
# }
|
||||
# auth_request /app-store-auth;
|
||||
# set $auth $http_authorization;
|
||||
# if ($http_authorization = "") {
|
||||
# set $auth "token $arg_authorization";
|
||||
# }
|
||||
# rewrite ^/app-store(.*)$ $1 break;
|
||||
# proxy_pass http://hs-resource-server:19980;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Authorization $auth;
|
||||
# }
|
||||
|
||||
location /app-store-auth {
|
||||
internal;
|
||||
set $hs_token $http_hs_token;
|
||||
if ($hs_token = "") {
|
||||
set $hs_token $arg_token;
|
||||
}
|
||||
proxy_set_header Hs-Token $hs_token;
|
||||
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_set_header Content-Length "";
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Original-Method $request_method;
|
||||
proxy_set_header X-Original-IP $remote_addr;
|
||||
proxy_set_header Query-Data $http_query_data;
|
||||
|
||||
proxy_pass http://hs-api/api$request_uri;
|
||||
}
|
||||
|
||||
error_page 502 /502.json;
|
||||
error_page 503 /503.json;
|
||||
location /503.json {
|
||||
return 503 '{"code": -2, "msg": "服务器未响应", "err": "hs-nginx err"}';
|
||||
}
|
||||
|
||||
error_page 504 /504.json;
|
||||
location /504.json {
|
||||
return 504 '{"code": -3, "msg": "服务器未响应", "err": "hs-nginx err"}';
|
||||
}
|
||||
error_page 497 301 =307 https://$http_host$request_uri;
|
||||
error_page 401 @my_401;
|
||||
error_page 403 @my_403;
|
||||
error_page 404 @my_404;
|
||||
error_page 502 @my_502;
|
||||
|
||||
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>';
|
||||
}
|
||||
|
||||
location @my_403 {
|
||||
default_type text/html;
|
||||
return 403 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>403</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>403 Forbidden</h1>';
|
||||
}
|
||||
|
||||
location @my_404 {
|
||||
default_type text/html;
|
||||
return 404 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>404</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>404 Not_Found</h1>';
|
||||
}
|
||||
|
||||
location @my_502 {
|
||||
default_type text/html;
|
||||
return 502 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>502</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>502 Bad_Gateway</h1>';
|
||||
}
|
||||
75
pkg/resource/nginx/gateway.conf
Normal file
75
pkg/resource/nginx/gateway.conf
Normal file
@@ -0,0 +1,75 @@
|
||||
upstream hs-gateway-controller {
|
||||
least_conn;
|
||||
server gateway-service:9012 max_fails=3 fail_timeout=10s;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl proxy_protocol;
|
||||
server_name hs-gateway-controller;
|
||||
|
||||
ssl_certificate /yizhisec/ssl/server.crt;
|
||||
ssl_certificate_key /yizhisec/ssl/server.key;
|
||||
ssl_client_certificate /yizhisec/ssl/ca.crt;
|
||||
ssl_verify_client on;
|
||||
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
|
||||
ssl_session_tickets off;
|
||||
|
||||
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
|
||||
ssl_dhparam /etc/nginx/ssl/ffdhe2048.txt;
|
||||
|
||||
# intermediate configuration
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://hs-gateway-controller;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl proxy_protocol;
|
||||
server_name hs-gateway-register-controller;
|
||||
|
||||
ssl_certificate /yizhisec/ssl/server.crt;
|
||||
ssl_certificate_key /yizhisec/ssl/server.key;
|
||||
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
ssl_dhparam /etc/nginx/ssl/ffdhe2048.txt;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
location = /api/v1/gateway/setting {
|
||||
if ($request_method != POST ) {
|
||||
return 502 '{"code": -1, "msg": "invalid request"}';
|
||||
}
|
||||
|
||||
proxy_pass http://hs-gateway-controller;
|
||||
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;
|
||||
}
|
||||
}
|
||||
63
pkg/resource/nginx/nginx.conf
Normal file
63
pkg/resource/nginx/nginx.conf
Normal file
@@ -0,0 +1,63 @@
|
||||
user root;
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
stream {
|
||||
error_log /var/log/nginx/error.log error;
|
||||
|
||||
map $ssl_preread_server_name $backend {
|
||||
mqtt.yizhisec.com 127.0.0.1:27443;
|
||||
mqtt-yizhisec-com 127.0.0.1:27443;
|
||||
default web;
|
||||
}
|
||||
|
||||
upstream web {
|
||||
server 127.0.0.1:443;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 27443 ssl proxy_protocol;
|
||||
|
||||
# ssl_session_timeout 10m;
|
||||
ssl_certificate /etc/nginx/ssl/mqtt.server.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/mqtt.server.key;
|
||||
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
|
||||
proxy_pass emqx-service.db-emqx:1883;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 23443;
|
||||
proxy_pass $backend;
|
||||
ssl_preread on;
|
||||
proxy_protocol on;
|
||||
}
|
||||
}
|
||||
|
||||
http {
|
||||
log_format custom '$time_iso8601 - $remote_addr - $http_host - $status - $request_time - $request_method - $request_uri';
|
||||
access_log /var/log/nginx/access.log custom;
|
||||
|
||||
include /etc/nginx/sites-enabled/*.conf;
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
sendfile_max_chunk 512k;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_http_version 1.0;
|
||||
gzip_min_length 1000;
|
||||
gzip_comp_level 6;
|
||||
gzip_disable msie6;
|
||||
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/javascript text/xml application/xml application/rss+xml application/atom+xml;
|
||||
|
||||
keepalive_timeout 65;
|
||||
}
|
||||
81
pkg/resource/nginx/seafile.conf
Normal file
81
pkg/resource/nginx/seafile.conf
Normal file
@@ -0,0 +1,81 @@
|
||||
server {
|
||||
listen 443 ssl proxy_protocol;
|
||||
server_name seafile.yizhisec.com cloud.hybridscope.com seafile-yizhise-com cloud-hybridscope-com;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/client.server.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/client.server.key;
|
||||
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
|
||||
ssl_session_tickets off;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
ssl_trusted_certificate /etc/nginx/ssl/client.ca.crt;
|
||||
ssl_client_certificate /etc/nginx/ssl/client.ca.crt;
|
||||
ssl_verify_client on;
|
||||
|
||||
client_max_body_size 500M;
|
||||
|
||||
# location = /auth-sharing {
|
||||
# internal;
|
||||
# proxy_pass http://client-service:9129/api/auth-sharing;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_pass_request_body off;
|
||||
# proxy_set_header Content-Length "";
|
||||
# proxy_set_header X-Original-URI $request_uri;
|
||||
# }
|
||||
|
||||
location /f/ {
|
||||
rewrite ^(.+[^/])$ $1/ last; # 补上末尾的 /,避免重定向两次
|
||||
# auth_request /auth-sharing;
|
||||
# proxy_pass http://hs-openresty:13381;
|
||||
proxy_pass http://seafile-service.seafile;
|
||||
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 /api/v1/ {
|
||||
proxy_pass http://backup-seafile-service.seafile:9027;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
location /api2/ {
|
||||
# proxy_pass http://hs-resource-server:19980;
|
||||
proxy_pass http://seafile-service.seafile;
|
||||
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 /api/v2.1/ {
|
||||
# proxy_pass http://hs-resource-server:19980;
|
||||
proxy_pass http://seafile-service.seafile;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $proxy_protocol_addr;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
location /seafhttp/ {
|
||||
# proxy_pass http://hs-resource-server:19980;
|
||||
proxy_pass http://seafile-service.seafile;
|
||||
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;
|
||||
}
|
||||
}
|
||||
17
pkg/resource/nginx/user.conf
Normal file
17
pkg/resource/nginx/user.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name hs-user-management-controller;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://user-service:9013;
|
||||
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;
|
||||
}
|
||||
}
|
||||
173
pkg/resource/nginx/web.conf
Normal file
173
pkg/resource/nginx/web.conf
Normal file
@@ -0,0 +1,173 @@
|
||||
server {
|
||||
listen 80;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# upstream hs-backup-server {
|
||||
# least_conn;
|
||||
# server hs-backup-server:9349 max_fails=3 fail_timeout=10s;
|
||||
# }
|
||||
|
||||
upstream hs-api {
|
||||
server api-service:9002;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9002;
|
||||
|
||||
location / {
|
||||
proxy_pass http://hs-api;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl default_server;
|
||||
|
||||
location /api/admin/ {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location /oem {
|
||||
proxy_pass http://oem-service;
|
||||
}
|
||||
|
||||
location /api/my/sys/client/installer {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/client/download/list;
|
||||
}
|
||||
|
||||
location /api/system/version {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/system/version;
|
||||
}
|
||||
|
||||
location /api/v2_2/system/elink {
|
||||
proxy_pass http://app-helper-service.hsv2;
|
||||
}
|
||||
|
||||
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 / {
|
||||
proxy_pass http://front-user-service;
|
||||
}
|
||||
|
||||
include /etc/nginx/common/common.conf;
|
||||
|
||||
error_page 497 301 =307 https://$http_host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8443 ssl;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN"; # 或 "DENY"
|
||||
add_header Content-Security-Policy "img-src * data:; frame-ancestors 'none';" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||
add_header X-Permitted-Cross-Domain-Policies "none";
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Download-Options "noopen" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
|
||||
server_tokens off;
|
||||
|
||||
location / {
|
||||
proxy_pass http://front-admin-service;
|
||||
}
|
||||
|
||||
location /api/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 /user/avatar/ {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/_obj/;
|
||||
}
|
||||
|
||||
location /api/account/profile/avatar {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/user/profile/avatar/update;
|
||||
}
|
||||
|
||||
location /api/admin/business-center/network-app/tunnel {
|
||||
proxy_pass http://app-helper-service.hsv2/api/v2_2/interceptor/mie/resource/icon$request_uri;
|
||||
}
|
||||
|
||||
location /api/v2_1/user {
|
||||
proxy_pass http://user-service:9013;
|
||||
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/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 {
|
||||
proxy_pass http://hs-api/ws;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
location /api/local/user/import/template {
|
||||
auth_request /token_auth;
|
||||
alias /static/resource/local_user_import_template.xlsx;
|
||||
}
|
||||
|
||||
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>';
|
||||
}
|
||||
|
||||
location @my_403 {
|
||||
default_type text/html;
|
||||
return 403 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>403</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>403 Forbidden</h1>';
|
||||
}
|
||||
|
||||
location @my_404 {
|
||||
default_type text/html;
|
||||
return 404 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>404</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>404 Not_Found</h1>';
|
||||
}
|
||||
|
||||
location @my_502 {
|
||||
default_type text/html;
|
||||
return 502 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>502</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>502 Bad_Gateway</h1>';
|
||||
}
|
||||
|
||||
error_page 497 301 =307 https://$http_host$request_uri;
|
||||
error_page 401 @my_401;
|
||||
error_page 403 @my_403;
|
||||
error_page 404 @my_404;
|
||||
error_page 502 @my_502;
|
||||
}
|
||||
@@ -5,24 +5,135 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed flannel.yaml
|
||||
//go:embed yaml/registry.yaml
|
||||
YAMLRegistry string
|
||||
|
||||
//go:embed yaml/flannel.yaml
|
||||
YAMLFlannel string
|
||||
|
||||
//go:embed es.yaml
|
||||
//go:embed yaml/es.yaml
|
||||
YAMLES string
|
||||
|
||||
//go:embed kibana.yaml
|
||||
//go:embed yaml/kibana.yaml
|
||||
YAMLKibana []byte
|
||||
|
||||
//go:embed es.init.sh
|
||||
//go:embed sh/es.init.sh
|
||||
BashESInit []byte
|
||||
|
||||
//go:embed emqx.yaml
|
||||
//go:embed yaml/emqx.yaml
|
||||
YAMLEMQX []byte
|
||||
|
||||
//go:embed yosguard.create.sql
|
||||
//go:embed yaml/minio.yaml
|
||||
YAMLMinIO string
|
||||
|
||||
//go:embed sql/yosguard.create.sql
|
||||
SQLYosguard []byte
|
||||
|
||||
//go:embed less-dns.yaml
|
||||
//go:embed yaml/less-dns.yaml
|
||||
YAMLLessDNS []byte
|
||||
|
||||
//go:embed yaml/seafile.yaml
|
||||
YAMLSeafile string
|
||||
|
||||
//go:embed yaml/backup-seafile.yaml
|
||||
YAMLBackupSeafile string
|
||||
|
||||
//go:embed yaml/app.user.yaml
|
||||
YAMLAppUser string
|
||||
|
||||
//go:embed yaml/app.gateway.yaml
|
||||
YAMLAppGateway string
|
||||
|
||||
//go:embed yaml/app.client.yaml
|
||||
YAMLAppClient string
|
||||
|
||||
//go:embed yaml/app.mie.api.yaml
|
||||
YAMLAppMieAPI string
|
||||
|
||||
//go:embed yaml/app.mie.worker.yaml
|
||||
YAMLAppMieWorker []byte
|
||||
|
||||
//go:embed yaml/app.mie.cron.yaml
|
||||
YAMLAppMieCron []byte
|
||||
|
||||
//go:embed yaml/app.mie.sweeper.yaml
|
||||
YAMLAppMieSweeper []byte
|
||||
|
||||
//go:embed yaml/app.oem.yaml
|
||||
YAMLAppOEM string
|
||||
|
||||
//go:embed yaml/app.front.user.yaml
|
||||
YAMLAppFrontUser string
|
||||
|
||||
//go:embed yaml/app.front.admin.yaml
|
||||
YAMLAppFrontAdmin string
|
||||
|
||||
//go:embed yaml/app.nginx.yaml
|
||||
YAMLAppNGINX string
|
||||
|
||||
//go:embed yaml/app.helper.yaml
|
||||
YAMLAppHelper string
|
||||
|
||||
//go:embed yaml/client.pkg.yaml
|
||||
YAMLClientPKG string
|
||||
|
||||
//go:embed ssl/ca.crt
|
||||
SSLCaCrt string
|
||||
|
||||
//go:embed ssl/client.server.crt
|
||||
SSLClientServerCrt string
|
||||
|
||||
//go:embed ssl/client.server.key
|
||||
SSLClientServerKey string
|
||||
|
||||
//go:embed ssl/ffdhe2048.txt
|
||||
SSLFFDHE2048 string
|
||||
|
||||
//go:embed ssl/mqtt.server.crt
|
||||
SSLMQTTServerCrt string
|
||||
|
||||
//go:embed ssl/mqtt.server.key
|
||||
SSLMQTTServerKey string
|
||||
|
||||
//go:embed ssl/mqtt.client.crt
|
||||
SSLMQTTClientCrt []byte
|
||||
|
||||
//go:embed ssl/mqtt.client.key
|
||||
SSLMQTTClientKey []byte
|
||||
|
||||
//go:embed ssl/server.crt
|
||||
SSLServerCrt string
|
||||
|
||||
//go:embed ssl/server.key
|
||||
SSLServerKey string
|
||||
|
||||
//go:embed ssl/web.server.crt
|
||||
SSLWebServerCrt string
|
||||
|
||||
//go:embed ssl/web.server.key
|
||||
SSLWebServerKey string
|
||||
|
||||
//go:embed nginx/seafile.conf
|
||||
NGINXSeafile []byte
|
||||
|
||||
//go:embed nginx/common.conf
|
||||
NGINXCommon []byte
|
||||
|
||||
//go:embed nginx/gateway.conf
|
||||
NGINXGateway []byte
|
||||
|
||||
//go:embed nginx/web.conf
|
||||
NGINXWeb []byte
|
||||
|
||||
//go:embed nginx/client.conf
|
||||
NGINXClient []byte
|
||||
|
||||
//go:embed nginx/nginx.conf
|
||||
NGINXMain []byte
|
||||
|
||||
//go:embed nginx/user.conf
|
||||
NGINXUser []byte
|
||||
|
||||
//go:embed nginx/client_pkg.conf
|
||||
NGINXClientPKG string
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user