🎨 大部分的 make 指令
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.qoder
|
||||
x-*
|
||||
dist
|
||||
.trae
|
||||
.vscode
|
||||
.idea
|
||||
47
AGENTS.md
Normal file
47
AGENTS.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 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
|
||||
62
README.md
Normal file
62
README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# hsv2/forge
|
||||
|
||||
一个用于构建与安装 HybridScope v2 相关依赖与资源的命令行工具。基于 Go 与 Cobra 构建,提供统一的 `make` 流程与简单的 `install` 占位命令。
|
||||
|
||||
## 环境要求
|
||||
- Go `1.25.2`
|
||||
- Linux 环境,能够访问外部下载源(如 `artifactory.yizhisec.com`、`docker.io` 等)
|
||||
|
||||
## 快速开始
|
||||
```bash
|
||||
# 构建
|
||||
go build ./...
|
||||
|
||||
# 查看帮助
|
||||
./forge --help
|
||||
|
||||
# 进入 make 子命令的帮助
|
||||
./forge make --help
|
||||
|
||||
# 示例:准备 Redis 资源
|
||||
./forge make redis --replica-count 2 --password ******
|
||||
```
|
||||
|
||||
默认构建目录为 `/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` 快速跳过依赖检查。
|
||||
20
go.mod
Normal file
20
go.mod
Normal file
@@ -0,0 +1,20 @@
|
||||
module yizhisec.com/hsv2/forge
|
||||
|
||||
go 1.25.2
|
||||
|
||||
require (
|
||||
gitea.loveuer.com/yizhisec/pkg3 v0.0.1
|
||||
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
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
)
|
||||
29
go.sum
Normal file
29
go.sum
Normal file
@@ -0,0 +1,29 @@
|
||||
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=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
25
internal/cmd/install.go
Normal file
25
internal/cmd/install.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func installCmd() *cobra.Command {
|
||||
_cmd := &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)
|
||||
},
|
||||
}
|
||||
|
||||
return _cmd
|
||||
}
|
||||
|
||||
func runInstall(args []string) error {
|
||||
fmt.Println("Running install command...")
|
||||
return nil
|
||||
}
|
||||
51
internal/cmd/make.binaries.go
Normal file
51
internal/cmd/make.binaries.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/archiver"
|
||||
)
|
||||
|
||||
func makeBinaries() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "binaries",
|
||||
Aliases: []string{"bin", "B"},
|
||||
Short: "Build binary files",
|
||||
Long: `Build all required binary files for the project.`,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return os.MkdirAll(filepath.Join(opt.Cfg.Make.Dir, "dependency", "bin"), 0755)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/k8s-bin.tar"
|
||||
binDir = filepath.Join(opt.Cfg.Make.Dir, "dependency")
|
||||
)
|
||||
|
||||
logger.Info("开始准备 k8s 二进制文件...")
|
||||
logger.Debug("下载地址: %s", tarURL)
|
||||
logger.Debug("目标目录: %s", binDir)
|
||||
|
||||
logger.Info("正在下载二进制文件...")
|
||||
|
||||
if err := archiver.DownloadAndExtract(
|
||||
cmd.Context(),
|
||||
tarURL,
|
||||
binDir,
|
||||
archiver.WithInsecureSkipVerify(),
|
||||
archiver.WithGzipCompression(true),
|
||||
); err != nil {
|
||||
logger.Info("❌ 下载并解压二进制文件失败")
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ 成功下载并解压二进制文件")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
50
internal/cmd/make.debs.go
Normal file
50
internal/cmd/make.debs.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/archiver"
|
||||
)
|
||||
|
||||
func makeDebs() *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 {
|
||||
fmt.Println("Building Debian packages...")
|
||||
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)
|
||||
|
||||
logger.Info("正在下载二进制文件...")
|
||||
|
||||
if err := archiver.DownloadAndExtract(
|
||||
cmd.Context(),
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
44
internal/cmd/make.emqx.go
Normal file
44
internal/cmd/make.emqx.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func makeEMQX() *cobra.Command {
|
||||
_cmd := &cobra.Command{
|
||||
Use: "emqx",
|
||||
Short: "Make EMQX",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "emqx")
|
||||
)
|
||||
|
||||
logger.Info("MakeEMQX: 开始构建 emqx(mqtt) 依赖...")
|
||||
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Error("MakeEMQX: 创建 emqx 目录失败")
|
||||
logger.Debug("MakeEMQX: 创建 emqx 目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(location, "emqx.yaml"), resource.YAMLEMQX, 0644); err != nil {
|
||||
logger.Error("MakeEMQX: 写入 emqx.yaml 失败")
|
||||
logger.Debug("MakeEMQX: 写入 emqx.yaml 失败, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("MakeEMQX: 构建 emqx(mqtt) 依赖成功!!!")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return _cmd
|
||||
}
|
||||
154
internal/cmd/make.es.go
Normal file
154
internal/cmd/make.es.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/archiver"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func makeES() *cobra.Command {
|
||||
const (
|
||||
dockerFile = `
|
||||
FROM docker-mirror.yizhisec.com/library/alpine:3.22.2
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && apk add curl
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
COPY plugins /data/plugins
|
||||
COPY create_index.sh /data/create_index.sh
|
||||
RUN chmod +x /data/create_index.sh
|
||||
`
|
||||
)
|
||||
|
||||
var (
|
||||
makeHelper bool
|
||||
memRate int
|
||||
storage int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "es",
|
||||
Aliases: []string{"elastic", "elasticsearch"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
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")
|
||||
)
|
||||
|
||||
logger.Info("MakeES: 开始构建 elasticsearch(es) 依赖...")
|
||||
|
||||
if err := os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Error("MakeES: 创建目录失败")
|
||||
logger.Debug("MakeES: 创建目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if makeHelper {
|
||||
tmp := filepath.Join(os.TempDir(), "make-es")
|
||||
logger.Info("MakeES: 重新构建 helper 镜像...(%s)", tmp)
|
||||
|
||||
if err = os.RemoveAll(tmp); err != nil {
|
||||
logger.Error("MakeES: 删除临时目录失败")
|
||||
logger.Debug("MakeES: 删除临时目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(tmp, 0755); err != nil {
|
||||
logger.Error("MakeES: 创建临时目录失败")
|
||||
logger.Debug("MakeES: 创建临时目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(tmp, "Dockerfile"), []byte(dockerFile), 0644); err != nil {
|
||||
logger.Error("MakeES: 写入 Dockerfile 失败")
|
||||
logger.Debug("MakeES: 写入 Dockerfile 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = archiver.DownloadAndExtract(
|
||||
cmd.Context(),
|
||||
"https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/dependency/elasticsearch/v2-es-plugins.tar.gz",
|
||||
tmp,
|
||||
archiver.WithInsecureSkipVerify(),
|
||||
); err != nil {
|
||||
logger.Error("MakeES: 下载 es 插件失败")
|
||||
logger.Debug("MakeES: 下载 es 插件失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(tmp, "create_index.sh"), resource.BashESInit, 0644); err != nil {
|
||||
logger.Error("MakeES: 写入 create_index.sh 文件失败")
|
||||
logger.Debug("MakeES: 写入 create_index.sh 文件失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
helpCmd := exec.CommandContext(cmd.Context(), "docker", "build", "-t",
|
||||
"hub.yizhisec.com/hybridscope/v2/es-init-helper:alpine-3.22.2",
|
||||
"-f", filepath.Join(tmp, "Dockerfile"),
|
||||
tmp,
|
||||
)
|
||||
|
||||
if output, err = helpCmd.CombinedOutput(); err != nil {
|
||||
logger.Error("MakeES: 重新构建 helper 镜像失败")
|
||||
logger.Debug("MakeES: 重新构建 helper 镜像失败: %v", err)
|
||||
logger.Debug("MakeES: 重新构建 helper 镜像失败: %s", string(output))
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("MakeES: 重新构建 helper 镜像成功!!!")
|
||||
}
|
||||
|
||||
mk := maker.NewMaker()
|
||||
if err = mk.Image(
|
||||
cmd.Context(),
|
||||
"hub.yizhisec.com/hybridscope/v2/es-init-helper:alpine-3.22.2",
|
||||
maker.WithImageSave(helperTarLocation),
|
||||
); err != nil {
|
||||
logger.Error("MakeES: 准备 es init helper 镜像失败")
|
||||
logger.Debug("MakeES: 准备 es init helper 镜像失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
bs := []byte(fmt.Sprintf(resource.YAMLES,
|
||||
memRate, memRate*2,
|
||||
memRate, memRate,
|
||||
memRate*2, memRate*2,
|
||||
storage,
|
||||
))
|
||||
|
||||
if err = os.WriteFile(filepath.Join(opt.Cfg.Make.Dir, "dependency", "elastic", "es.yaml"), bs, 0644); err != nil {
|
||||
logger.Error("MakeES: 写入 es.yaml 文件失败")
|
||||
logger.Debug("MakeES: 写入 es.yaml 文件失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(opt.Cfg.Make.Dir, "dependency", "elastic", "kibana.yaml"), resource.YAMLKibana, 0644); err != nil {
|
||||
logger.Error("MakeES: 写入 es.yaml 文件失败")
|
||||
logger.Debug("MakeES: 写入 es.yaml 文件失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("MakeES: 构建 elastic(es) 依赖成功!!!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().BoolVar(&makeHelper, "make-helper", false, "重新构建 helper 镜像")
|
||||
_cmd.Flags().IntVar(&memRate, "mem-rate", 1, "内存倍率(从 1G 开始)")
|
||||
_cmd.Flags().IntVar(&storage, "storage", 100, "存储空间(单位: G)")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
46
internal/cmd/make.flannel.go
Normal file
46
internal/cmd/make.flannel.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func makeFlannel() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "flannel",
|
||||
Short: "Build Flannel resources",
|
||||
Long: `Build and prepare Flannel network resources.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "flannel")
|
||||
)
|
||||
|
||||
logger.Info("MakeFlannel: 开始构建 flannel 资源...")
|
||||
|
||||
logger.Debug("MakeFlannel location: %s", location)
|
||||
if err = os.Mkdir(location, 0755); err != nil {
|
||||
logger.Error("MakeFlannel: 创建目录 %s 失败", location)
|
||||
logger.Debug("MakeFlannel: 创建目录 %s 失败: %v", location, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(location, "flannel.yaml"), resource.YAMLFlannel, 0644); err != nil {
|
||||
logger.Error("MakeFlannel: 创建文件 %s 失败", filepath.Join(location, "flannel.yaml"))
|
||||
logger.Debug("MakeFlannel: 创建文件 %s 失败: %v", filepath.Join(location, "flannel.yaml"), err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("MakeFlannel: 构建 flannel 成功!!!")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
67
internal/cmd/make.go
Normal file
67
internal/cmd/make.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func makeCmd() *cobra.Command {
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "make",
|
||||
Short: "Build the project",
|
||||
Long: `Build the project using the specified configuration.`,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
||||
if opt.Cfg.Debug {
|
||||
logger.SetLogLevel(logger.LogLevelDebug)
|
||||
logger.Warn("Running in debug mode")
|
||||
}
|
||||
|
||||
if opt.Cfg.Make.DisableDependencyCheck {
|
||||
logger.Info("Dependency check disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
mk := maker.NewMaker()
|
||||
if err = mk.DependencyCheck(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(opt.Cfg.Make.Dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Debug("Running make prerun success")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.PersistentFlags().BoolVar(&opt.Cfg.Make.DisableDependencyCheck, "disable-dependency-check", false, "Disable dependency check")
|
||||
_cmd.PersistentFlags().StringVar(&opt.Cfg.Make.Dir, "dir", "/root/hsv2-installation", "make base directory")
|
||||
|
||||
_cmd.AddCommand(
|
||||
makeImages(),
|
||||
makeBinaries(),
|
||||
makeDebs(),
|
||||
makeFlannel(),
|
||||
makeLonghorn(),
|
||||
makeMysql(),
|
||||
makeRedis(),
|
||||
makeES(),
|
||||
makeEMQX(),
|
||||
makeYosguard(),
|
||||
makeLessDNS(), // hs-net dependency
|
||||
makeHSNet(),
|
||||
)
|
||||
|
||||
return _cmd
|
||||
}
|
||||
36
internal/cmd/make.hsnet.go
Normal file
36
internal/cmd/make.hsnet.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func makeHSNet() *cobra.Command {
|
||||
_cmd := &cobra.Command{
|
||||
Use: "hs-net",
|
||||
Short: "Build hs-net",
|
||||
Long: `Build hs-net`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "hs-net")
|
||||
)
|
||||
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Error("MakeHSNet: 创建目录失败s")
|
||||
logger.Debug("MakeHSNet: 创建目录失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Fatal("MakeHSNet: 构建 hs-net 失败!!!(怎么完善,怎么完善,怎么完善???)")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return _cmd
|
||||
}
|
||||
103
internal/cmd/make.images.go
Normal file
103
internal/cmd/make.images.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/controller/maker"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
func makeImages() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "images",
|
||||
Aliases: []string{"image"},
|
||||
Short: "Build and pull Docker images",
|
||||
Long: `Build and pull all required Docker images for the project.`,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return os.MkdirAll(filepath.Join(opt.Cfg.Make.Dir, "dependency", "image"), 0755)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) 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"},
|
||||
|
||||
{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)
|
||||
}
|
||||
|
||||
mk := maker.NewMaker()
|
||||
for _, image := range images {
|
||||
|
||||
opts := []maker.ImageOpt{
|
||||
maker.WithImageFallback(image.Fallback),
|
||||
maker.WithImageSave(image.Save),
|
||||
}
|
||||
|
||||
if image.Force {
|
||||
opts = append(opts, maker.WithImageForcePull())
|
||||
}
|
||||
|
||||
if err := mk.Image(cmd.Context(), image.Name, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
44
internal/cmd/make.lessdns.go
Normal file
44
internal/cmd/make.lessdns.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func makeLessDNS() *cobra.Command {
|
||||
_cmd := &cobra.Command{
|
||||
Use: "lessdns",
|
||||
Short: "Build lessdns",
|
||||
Long: `Build lessdns`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "less-dns")
|
||||
)
|
||||
|
||||
logger.Info("MakeLessDNS: 开始构建 less-dns...")
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Error("MakeLessDNS: 创建目录失败s")
|
||||
logger.Debug("MakeLessDNS: 创建目录失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(location, "deployment.yaml"), resource.YAMLLessDNS, 0644); err != nil {
|
||||
logger.Error("MakeLessDNS: 写入文件失败")
|
||||
logger.Debug("MakeLessDNS: 写入文件失败: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("MakeLessDNS: 构建 less-dns 成功!!!")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return _cmd
|
||||
}
|
||||
82
internal/cmd/make.longhorn.go
Normal file
82
internal/cmd/make.longhorn.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
)
|
||||
|
||||
func makeLonghorn() *cobra.Command {
|
||||
const (
|
||||
valuesContent = `# 设置 Longhorn 数据存储路径
|
||||
defaultSettings:
|
||||
defaultDataPath: "/data/longhorn"
|
||||
# 设置默认副本数
|
||||
persistence:
|
||||
reclaimPolicy: Retain
|
||||
defaultClassReplicaCount: %d
|
||||
`
|
||||
)
|
||||
var (
|
||||
replicaCount int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "longhorn",
|
||||
Short: "Build Longhorn resources",
|
||||
Long: `Build and prepare Longhorn storage resources.`,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return os.MkdirAll(filepath.Join(opt.Cfg.Make.Dir, "dependency", "longhorn"), 0755)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
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")
|
||||
)
|
||||
|
||||
logger.Info("开始准备 Longhorn 资源...")
|
||||
logger.Debug("下载地址: %s", chartURL)
|
||||
logger.Debug("目标目录: %s", longhornDir)
|
||||
|
||||
logger.Info("正在下载 Longhorn chart...")
|
||||
|
||||
// Download the chart file
|
||||
if err := downloader.Download(
|
||||
cmd.Context(),
|
||||
chartURL,
|
||||
chartFile,
|
||||
downloader.WithInsecureSkipVerify(),
|
||||
); err != nil {
|
||||
logger.Info("❌ 下载 Longhorn chart 失败")
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ 成功下载 Longhorn chart")
|
||||
|
||||
// Create values.yaml file
|
||||
bs := []byte(fmt.Sprintf(valuesContent, replicaCount))
|
||||
logger.Debug("创建 values.yaml 文件: %s", valuesFile)
|
||||
if err := os.WriteFile(valuesFile, bs, 0644); err != nil {
|
||||
logger.Debug("创建 values.yaml 失败: %v", err)
|
||||
logger.Info("❌ 创建 values.yaml 失败")
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ 成功创建 values.yaml")
|
||||
logger.Debug("Longhorn 资源准备完成")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replicaCount, "replica-count", 2, "存储副本数")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
139
internal/cmd/make.mysql.go
Normal file
139
internal/cmd/make.mysql.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
)
|
||||
|
||||
func makeMysql() *cobra.Command {
|
||||
const (
|
||||
_yaml = `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mysql-secret
|
||||
namespace: db-mysql
|
||||
type: Opaque
|
||||
data:
|
||||
ROOT_PASSWORD: TDBoTXlzcWwu
|
||||
---
|
||||
|
||||
apiVersion: mysql.presslabs.org/v1alpha1
|
||||
kind: MysqlCluster
|
||||
metadata:
|
||||
name: mysql-cluster
|
||||
namespace: db-mysql
|
||||
spec:
|
||||
mysqlVersion: "8.0"
|
||||
replicas: %d
|
||||
image: docker.io/library/percona:8.0.28-20
|
||||
secretName: mysql-secret
|
||||
volumeSpec:
|
||||
persistentVolumeClaim:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
storageClassName: longhorn
|
||||
resources:
|
||||
requests:
|
||||
storage: %dGi
|
||||
# podSpec:
|
||||
# affinity:
|
||||
# podAntiAffinity:
|
||||
# requiredDuringSchedulingIgnoredDuringExecution: # 强制规则
|
||||
# - labelSelector:
|
||||
# matchExpressions:
|
||||
# - key: app.kubernetes.io/name
|
||||
# operator: In
|
||||
# values: ["mysql"]
|
||||
# - key: app.kubernetes.io/instance
|
||||
# operator: In
|
||||
# values: ["mysql-cluster"]
|
||||
# topologyKey: "kubernetes.io/hostname" # 确保不同节点
|
||||
# resources:
|
||||
# requests:
|
||||
# cpu: "2"
|
||||
# memory: "2Gi"
|
||||
# limits:
|
||||
# cpu: "8"
|
||||
# memory: "8Gi"
|
||||
---
|
||||
|
||||
apiVersion: mysql.presslabs.org/v1alpha1
|
||||
kind: MysqlDatabase
|
||||
metadata:
|
||||
name: my-database-mie
|
||||
namespace: db-mysql
|
||||
spec:
|
||||
database: mie
|
||||
clusterRef:
|
||||
name: mysql-cluster
|
||||
namespace: db-mysql
|
||||
`
|
||||
)
|
||||
|
||||
var (
|
||||
replicas int
|
||||
storage int
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "mysql",
|
||||
Short: "Build MySQL resources",
|
||||
Long: `Build and prepare MySQL database resources.`,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return os.MkdirAll(filepath.Join(opt.Cfg.Make.Dir, "dependency", "mysql"), 0755)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
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")
|
||||
)
|
||||
|
||||
logger.Info("开始构建 MySQL 资源...")
|
||||
logger.Debug("下载地址: %s", chartURL)
|
||||
logger.Debug("目标目录: %s", mysqlDir)
|
||||
|
||||
// Download MySQL operator chart
|
||||
logger.Info("正在下载 MySQL operator chart...")
|
||||
if err := downloader.Download(
|
||||
cmd.Context(),
|
||||
chartURL,
|
||||
chartFile,
|
||||
downloader.WithInsecureSkipVerify(),
|
||||
); err != nil {
|
||||
logger.Info("❌ 下载 MySQL operator chart 失败")
|
||||
return err
|
||||
}
|
||||
logger.Info("✅ 成功下载 MySQL operator chart")
|
||||
|
||||
bs := []byte(fmt.Sprintf(_yaml, replicas, storage))
|
||||
|
||||
// Generate secret.yaml
|
||||
if err = os.WriteFile(clusterFile, bs, 0644); err != nil {
|
||||
logger.Debug("创建 database.yaml 失败: %v", err)
|
||||
logger.Info("❌ 创建 database.yaml 失败")
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("✅ 成功创建 database.yaml")
|
||||
|
||||
logger.Info("✅ MySQL 资源构建成功")
|
||||
logger.Debug("MySQL 资源准备完成")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replicas, "replica-count", 2, "mysql 的副本数")
|
||||
_cmd.Flags().IntVar(&storage, "storage-size", 50, "mysql 的存储空间")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
127
internal/cmd/make.redis.go
Normal file
127
internal/cmd/make.redis.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
)
|
||||
|
||||
func makeRedis() *cobra.Command {
|
||||
const (
|
||||
valuesTemplate = `# Redis configuration
|
||||
architecture: replication
|
||||
|
||||
image:
|
||||
registry: docker.io
|
||||
repository: bitnami/redis
|
||||
tag: 8.2.2
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
auth:
|
||||
enabled: true
|
||||
password: "%s"
|
||||
|
||||
master:
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "longhorn"
|
||||
size: 5Gi
|
||||
# resources:
|
||||
# requests:
|
||||
# memory: "512Mi"
|
||||
# cpu: "250m"
|
||||
# limits:
|
||||
# memory: "1Gi"
|
||||
# cpu: "500m"
|
||||
|
||||
replica:
|
||||
replicaCount: %d
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "longhorn"
|
||||
size: 5Gi
|
||||
# resources:
|
||||
# requests:
|
||||
# memory: "512Mi"
|
||||
# cpu: "250m"
|
||||
# limits:
|
||||
# memory: "1Gi"
|
||||
# cpu: "500m"
|
||||
|
||||
metrics:
|
||||
enabled: false
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
`
|
||||
defaultPassword = "HybridScope0xRed1s."
|
||||
)
|
||||
|
||||
var (
|
||||
replicas int
|
||||
password string
|
||||
)
|
||||
|
||||
_cmd := &cobra.Command{
|
||||
Use: "redis",
|
||||
Short: "Build Redis resources",
|
||||
Long: `Build and prepare Redis cache resources.`,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return os.MkdirAll(filepath.Join(opt.Cfg.Make.Dir, "dependency", "redis"), 0755)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
chartURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/charts/redis-23.2.2.tgz"
|
||||
redisDir = filepath.Join(opt.Cfg.Make.Dir, "dependency", "redis")
|
||||
chartFile = filepath.Join(redisDir, "redis-23.2.2.tgz")
|
||||
valuesFile = filepath.Join(redisDir, "values.yaml")
|
||||
)
|
||||
|
||||
if password == "******" {
|
||||
password = defaultPassword
|
||||
}
|
||||
|
||||
logger.Info("开始构建 Redis 资源...")
|
||||
logger.Debug("下载地址: %s", chartURL)
|
||||
logger.Debug("目标目录: %s", redisDir)
|
||||
logger.Debug("Redis 副本数: %d", replicas)
|
||||
|
||||
// Download Redis chart
|
||||
logger.Info("正在下载 Redis chart...")
|
||||
if err := downloader.Download(
|
||||
cmd.Context(),
|
||||
chartURL,
|
||||
chartFile,
|
||||
downloader.WithInsecureSkipVerify(),
|
||||
); err != nil {
|
||||
logger.Info("❌ 下载 Redis chart 失败")
|
||||
return err
|
||||
}
|
||||
logger.Info("✅ 成功下载 Redis chart")
|
||||
|
||||
// Generate values.yaml
|
||||
logger.Debug("创建 values.yaml: %s", valuesFile)
|
||||
valuesContent := fmt.Sprintf(valuesTemplate, password, replicas)
|
||||
if err := os.WriteFile(valuesFile, []byte(valuesContent), 0644); err != nil {
|
||||
logger.Debug("创建 values.yaml 失败: %v", err)
|
||||
logger.Info("❌ 创建 values.yaml 失败")
|
||||
return err
|
||||
}
|
||||
logger.Info("✅ 成功创建 values.yaml")
|
||||
|
||||
logger.Info("✅ Redis 资源构建成功")
|
||||
logger.Debug("Redis 资源准备完成")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_cmd.Flags().IntVar(&replicas, "replica-count", 2, "Redis 副本数")
|
||||
_cmd.Flags().StringVar(&password, "password", "******", "Redis 密码")
|
||||
|
||||
return _cmd
|
||||
}
|
||||
120
internal/cmd/make.yosguard.go
Normal file
120
internal/cmd/make.yosguard.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
"yizhisec.com/hsv2/forge/pkg/downloader"
|
||||
"yizhisec.com/hsv2/forge/pkg/resource"
|
||||
)
|
||||
|
||||
func makeYosguard() *cobra.Command {
|
||||
const (
|
||||
_configTemplate = `
|
||||
Web:
|
||||
# default listen in docker0
|
||||
Host: 172.17.0.1
|
||||
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"`
|
||||
_service = `
|
||||
[Unit]
|
||||
Description=YiZhiSec YOSGuard
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/yosguard web --host __host__
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
Nice=-20
|
||||
Restart=always
|
||||
RestartSec=15
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target`
|
||||
)
|
||||
_cmd := &cobra.Command{
|
||||
Use: "yosguard",
|
||||
Aliases: []string{"YOS"},
|
||||
Short: "Make Yosguard",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "yosguard")
|
||||
)
|
||||
|
||||
logger.Info("MakeYosguard: 开始构建 yosguard...")
|
||||
|
||||
if err = os.MkdirAll(location, 0755); err != nil {
|
||||
logger.Error("MakeYosguard: 创建 yosguard 目录失败")
|
||||
logger.Debug("MakeYosguard: 创建 yosguard 目录失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 1. download yosguard bin: https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/yosguard
|
||||
_url := "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/yosguard"
|
||||
logger.Debug("MakeYosguard: start download bin from %s", _url)
|
||||
if err = downloader.Download(
|
||||
cmd.Context(),
|
||||
_url,
|
||||
filepath.Join(location, "yosguard"),
|
||||
downloader.WithInsecureSkipVerify(),
|
||||
downloader.WithFileMode(0755),
|
||||
); err != nil {
|
||||
logger.Error("MakeYosguard: 下载 yosguard 失败")
|
||||
logger.Debug("MakeYosguard: 下载 yosguard 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. generate config_template.yml
|
||||
if err = os.WriteFile(filepath.Join(location, "config_template.yml"), []byte(_configTemplate), 0644); err != nil {
|
||||
logger.Error("MakeYosguard: 生成 config_template.yml 失败")
|
||||
logger.Debug("MakeYosguard: 生成 config_template.yml 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. generate create.sql
|
||||
if err = os.WriteFile(filepath.Join(location, "create.sql"), resource.SQLYosguard, 0644); err != nil {
|
||||
logger.Error("MakeYosguard: 生成 create.sql 失败")
|
||||
logger.Debug("MakeYosguard: 生成 create.sql 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. generate systemd file
|
||||
if err = os.WriteFile(filepath.Join(location, "yosguard.service"), []byte(_service), 0644); err != nil {
|
||||
logger.Error("MakeYosguard: 生成 yosguard.service 失败")
|
||||
logger.Debug("MakeYosguard: 生成 yosguard.service 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("MakeYosguard: 构建 yosguard成功!!!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return _cmd
|
||||
}
|
||||
37
internal/cmd/root.go
Normal file
37
internal/cmd/root.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"yizhisec.com/hsv2/forge/internal/opt"
|
||||
)
|
||||
|
||||
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 {
|
||||
return rootCmd.ExecuteContext(ctx)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
rootCmd.PersistentFlags().BoolVar(&opt.Cfg.Debug, "debug", false, "Enable debug mode")
|
||||
|
||||
rootCmd.AddCommand(
|
||||
makeCmd(),
|
||||
installCmd(),
|
||||
)
|
||||
}
|
||||
35
internal/controller/maker/check.go
Normal file
35
internal/controller/maker/check.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func (m *maker) DependencyCheck(ctx context.Context) error {
|
||||
// 1. test wget command
|
||||
if err := checkCommand(ctx, "wget", "--version"); err != nil {
|
||||
return fmt.Errorf("wget 命令未找到: %w", err)
|
||||
}
|
||||
|
||||
// 2. test tar command
|
||||
if err := checkCommand(ctx, "tar", "--version"); err != nil {
|
||||
return fmt.Errorf("tar 命令未找到: %w", err)
|
||||
}
|
||||
|
||||
// 3. test docker command
|
||||
if err := checkCommand(ctx, "docker", "info"); err != nil {
|
||||
return fmt.Errorf("docker 命令未找到: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkCommand checks if a command is available by running it with the specified args
|
||||
func checkCommand(ctx context.Context, name string, args ...string) error {
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
102
internal/controller/maker/image.go
Normal file
102
internal/controller/maker/image.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type imageOpt struct {
|
||||
Fallbacks []string
|
||||
IgnoreFailure bool
|
||||
ForcePull bool
|
||||
Save string
|
||||
}
|
||||
|
||||
type ImageOpt func(*imageOpt)
|
||||
|
||||
func WithImageFallback(fallbacks ...string) ImageOpt {
|
||||
return func(o *imageOpt) {
|
||||
o.Fallbacks = lo.Filter(fallbacks, func(item string, _ int) bool { return item != "" })
|
||||
}
|
||||
}
|
||||
|
||||
func WithImageSave(filename string) ImageOpt {
|
||||
return func(o *imageOpt) {
|
||||
o.Save = filename
|
||||
}
|
||||
}
|
||||
|
||||
func WithImageForcePull() ImageOpt {
|
||||
return func(o *imageOpt) {
|
||||
o.ForcePull = true
|
||||
}
|
||||
}
|
||||
|
||||
func (m *maker) Image(ctx context.Context, name string, opts ...ImageOpt) error {
|
||||
logger.Info("开始获取镜像: %s", name)
|
||||
var (
|
||||
err error
|
||||
o = &imageOpt{}
|
||||
_cmd *exec.Cmd
|
||||
)
|
||||
|
||||
for _, fn := range opts {
|
||||
fn(o)
|
||||
}
|
||||
|
||||
logger.Debug("maker.Image: name = %s, opt = %#v", name, o)
|
||||
|
||||
if !o.ForcePull {
|
||||
_cmd = exec.CommandContext(ctx, "docker", "image", "inspect", name)
|
||||
if err = _cmd.Run(); err == nil {
|
||||
logger.Info("💾 镜像 %s 已存在", name)
|
||||
goto SAVE
|
||||
}
|
||||
}
|
||||
|
||||
_cmd = exec.CommandContext(ctx, "docker", "pull", name)
|
||||
if err = _cmd.Run(); err != nil {
|
||||
logger.Debug("获取原始镜像 %s 失败: %v", name, err)
|
||||
} else {
|
||||
logger.Info("✅ 成功获取镜像: %s", name)
|
||||
goto SAVE
|
||||
}
|
||||
|
||||
for _, fallback := range o.Fallbacks {
|
||||
logger.Info("开始获取镜像: %s (%s)", name, fallback)
|
||||
_cmd := exec.CommandContext(ctx, "docker", "pull", fallback)
|
||||
if err = _cmd.Run(); err != nil {
|
||||
logger.Debug("获取镜像 %s (%s) 失败: %v", name, fallback, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// pull success, retag image
|
||||
_cmd = exec.CommandContext(ctx, "docker", "tag", fallback, name)
|
||||
if err = _cmd.Run(); err != nil {
|
||||
logger.Debug("重命名镜像 %s => %s 失败: %s", fallback, name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Info("✅ 成功获取镜像: %s (%s)", name, fallback)
|
||||
break
|
||||
}
|
||||
|
||||
SAVE:
|
||||
if o.Save != "" {
|
||||
logger.Debug("保存镜像 %s 到 %s", name, o.Save)
|
||||
if err = exec.CommandContext(ctx, "docker", "save", "-o", o.Save, name).Run(); err != nil {
|
||||
logger.Debug("保存镜像 %s 到 %s 失败: %v", name, o.Save, err)
|
||||
} else {
|
||||
logger.Info("✅ 镜像 %s 保存到 %s 成功", name, o.Save)
|
||||
}
|
||||
}
|
||||
|
||||
if o.IgnoreFailure {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
16
internal/controller/maker/image_test.go
Normal file
16
internal/controller/maker/image_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package maker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
)
|
||||
|
||||
func TestImage(t *testing.T) {
|
||||
logger.SetLogLevel(logger.LogLevelDebug)
|
||||
m := NewMaker()
|
||||
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"),
|
||||
)
|
||||
}
|
||||
7
internal/controller/maker/maker.go
Normal file
7
internal/controller/maker/maker.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package maker
|
||||
|
||||
type maker struct{}
|
||||
|
||||
func NewMaker() *maker {
|
||||
return &maker{}
|
||||
}
|
||||
13
internal/opt/opt.go
Normal file
13
internal/opt/opt.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package opt
|
||||
|
||||
type config struct {
|
||||
Debug bool
|
||||
Make struct {
|
||||
DisableDependencyCheck bool
|
||||
Dir string
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
Cfg = &config{}
|
||||
)
|
||||
19
main.go
Normal file
19
main.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"yizhisec.com/hsv2/forge/internal/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
if err := cmd.Execute(ctx); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
225
pkg/archiver/archiver.go
Normal file
225
pkg/archiver/archiver.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package archiver
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
)
|
||||
|
||||
// Options defines options for downloading and extracting archives
|
||||
type Options struct {
|
||||
// InsecureSkipVerify skips TLS certificate verification (equivalent to wget --no-check-certificate)
|
||||
InsecureSkipVerify bool
|
||||
// HTTPClient allows providing a custom HTTP client
|
||||
HTTPClient *http.Client
|
||||
// OnProgress is called during extraction with the current file being extracted
|
||||
OnProgress func(filename string, index int, total int)
|
||||
// IsGzipped explicitly specifies whether the archive is gzip compressed
|
||||
// If nil, auto-detect based on file extension (.tar.gz, .tgz)
|
||||
IsGzipped *bool
|
||||
}
|
||||
|
||||
// Option is a functional option for configuring the archiver
|
||||
type Option func(*Options)
|
||||
|
||||
// WithInsecureSkipVerify skips TLS certificate verification
|
||||
func WithInsecureSkipVerify() Option {
|
||||
return func(o *Options) {
|
||||
o.InsecureSkipVerify = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithHTTPClient sets a custom HTTP client
|
||||
func WithHTTPClient(client *http.Client) Option {
|
||||
return func(o *Options) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
}
|
||||
|
||||
// WithProgress sets a progress callback
|
||||
func WithProgress(callback func(filename string, index int, total int)) Option {
|
||||
return func(o *Options) {
|
||||
o.OnProgress = callback
|
||||
}
|
||||
}
|
||||
|
||||
// WithGzipCompression explicitly sets whether the archive is gzip compressed
|
||||
func WithGzipCompression(isGzipped bool) Option {
|
||||
return func(o *Options) {
|
||||
o.IsGzipped = &isGzipped
|
||||
}
|
||||
}
|
||||
|
||||
// DownloadAndExtract downloads a tar or tar.gz file from URL and extracts it to destDir
|
||||
// Supports both .tar and .tar.gz formats
|
||||
func DownloadAndExtract(ctx context.Context, url, destDir string, opts ...Option) error {
|
||||
options := &Options{
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
||||
logger.Debug("开始下载和解压: %s -> %s", url, destDir)
|
||||
|
||||
// Create HTTP client
|
||||
client := options.HTTPClient
|
||||
if client == nil {
|
||||
client = &http.Client{}
|
||||
if options.InsecureSkipVerify {
|
||||
logger.Debug("TLS 证书验证已禁用")
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Download the file
|
||||
logger.Debug("发起 HTTP 请求: %s", url)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
logger.Debug("创建请求失败: %v", err)
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logger.Debug("下载失败: %v", err)
|
||||
return fmt.Errorf("failed to download: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logger.Debug("HTTP 状态码异常: %d", resp.StatusCode)
|
||||
return fmt.Errorf("bad status: %s", resp.Status)
|
||||
}
|
||||
|
||||
logger.Debug("下载成功,准备解压")
|
||||
|
||||
// Determine if the file is gzipped
|
||||
var reader io.Reader = resp.Body
|
||||
var isGzipped bool
|
||||
|
||||
if options.IsGzipped != nil {
|
||||
// Explicitly specified by option
|
||||
isGzipped = *options.IsGzipped
|
||||
logger.Debug("压缩格式由选项指定: isGzipped=%v", isGzipped)
|
||||
} else {
|
||||
// Auto-detect based on file extension
|
||||
isGzipped = strings.HasSuffix(url, ".tar.gz") || strings.HasSuffix(url, ".tgz")
|
||||
logger.Debug("根据文件扩展名自动检测压缩格式: isGzipped=%v", isGzipped)
|
||||
}
|
||||
|
||||
if isGzipped {
|
||||
logger.Debug("使用 gzip 解压")
|
||||
gzReader, err := gzip.NewReader(resp.Body)
|
||||
if err != nil {
|
||||
logger.Debug("创建 gzip reader 失败: %v", err)
|
||||
return fmt.Errorf("failed to create gzip reader: %w", err)
|
||||
}
|
||||
defer gzReader.Close()
|
||||
reader = gzReader
|
||||
} else {
|
||||
logger.Debug("tar 格式(未压缩)")
|
||||
}
|
||||
|
||||
// Extract tar archive
|
||||
if err := extractTar(reader, destDir, options); err != nil {
|
||||
return fmt.Errorf("failed to extract tar: %w", err)
|
||||
}
|
||||
|
||||
logger.Debug("解压完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractTar extracts a tar archive to the destination directory
|
||||
func extractTar(r io.Reader, destDir string, options *Options) error {
|
||||
tarReader := tar.NewReader(r)
|
||||
fileCount := 0
|
||||
totalFiles := 0
|
||||
|
||||
// First pass: count total files (optional, for progress reporting)
|
||||
// For now, we'll just extract directly
|
||||
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
logger.Debug("读取 tar 条目失败: %v", err)
|
||||
return fmt.Errorf("failed to read tar header: %w", err)
|
||||
}
|
||||
|
||||
target := filepath.Join(destDir, header.Name)
|
||||
logger.Debug("解压文件: %s", header.Name)
|
||||
|
||||
// Call progress callback if provided
|
||||
if options.OnProgress != nil {
|
||||
options.OnProgress(header.Name, fileCount, totalFiles)
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
// Create directory
|
||||
if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil {
|
||||
logger.Debug("创建目录失败 %s: %v", target, err)
|
||||
return fmt.Errorf("failed to create directory %s: %w", target, err)
|
||||
}
|
||||
|
||||
case tar.TypeReg:
|
||||
// Create regular file
|
||||
// Ensure parent directory exists
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
||||
logger.Debug("创建父目录失败 %s: %v", filepath.Dir(target), err)
|
||||
return fmt.Errorf("failed to create parent directory: %w", err)
|
||||
}
|
||||
|
||||
outFile, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
logger.Debug("创建文件失败 %s: %v", target, err)
|
||||
return fmt.Errorf("failed to create file %s: %w", target, err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(outFile, tarReader); err != nil {
|
||||
outFile.Close()
|
||||
logger.Debug("写入文件失败 %s: %v", target, err)
|
||||
return fmt.Errorf("failed to write file %s: %w", target, err)
|
||||
}
|
||||
outFile.Close()
|
||||
fileCount++
|
||||
|
||||
case tar.TypeSymlink:
|
||||
// Create symbolic link
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
||||
logger.Debug("创建符号链接父目录失败 %s: %v", filepath.Dir(target), err)
|
||||
return fmt.Errorf("failed to create parent directory for symlink: %w", err)
|
||||
}
|
||||
|
||||
// Remove existing file/link if exists
|
||||
os.Remove(target)
|
||||
|
||||
if err := os.Symlink(header.Linkname, target); err != nil {
|
||||
logger.Debug("创建符号链接失败 %s -> %s: %v", target, header.Linkname, err)
|
||||
return fmt.Errorf("failed to create symlink %s -> %s: %w", target, header.Linkname, err)
|
||||
}
|
||||
fileCount++
|
||||
|
||||
default:
|
||||
logger.Debug("跳过不支持的文件类型: %s (type: %v)", header.Name, header.Typeflag)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug("解压完成,共 %d 个文件", fileCount)
|
||||
return nil
|
||||
}
|
||||
47
pkg/archiver/archiver_test.go
Normal file
47
pkg/archiver/archiver_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package archiver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
)
|
||||
|
||||
func TestDownloadAndExtract(t *testing.T) {
|
||||
logger.SetLogLevel(logger.LogLevelDebug)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Example: download and extract a tar.gz file
|
||||
err := DownloadAndExtract(
|
||||
ctx,
|
||||
"https://example.com/archive.tar.gz",
|
||||
"/tmp/test-extract",
|
||||
WithInsecureSkipVerify(),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Logf("Expected error for test URL: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadAndExtractWithProgress(t *testing.T) {
|
||||
logger.SetLogLevel(logger.LogLevelDebug)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Example: with progress callback
|
||||
err := DownloadAndExtract(
|
||||
ctx,
|
||||
"https://example.com/archive.tar",
|
||||
"/tmp/test-extract",
|
||||
WithInsecureSkipVerify(),
|
||||
WithProgress(func(filename string, index int, total int) {
|
||||
t.Logf("Extracting: %s", filename)
|
||||
}),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Logf("Expected error for test URL: %v", err)
|
||||
}
|
||||
}
|
||||
195
pkg/downloader/downloader.go
Normal file
195
pkg/downloader/downloader.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package downloader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
)
|
||||
|
||||
// Options defines options for downloading files
|
||||
type Options struct {
|
||||
// InsecureSkipVerify skips TLS certificate verification
|
||||
InsecureSkipVerify bool
|
||||
// HTTPClient allows providing a custom HTTP client
|
||||
HTTPClient *http.Client
|
||||
// OnProgress is called during download with bytes downloaded and total size
|
||||
OnProgress func(downloaded, total int64)
|
||||
// CreateDirs automatically creates parent directories if they don't exist
|
||||
CreateDirs bool
|
||||
// Overwrite allows overwriting existing files
|
||||
Overwrite bool
|
||||
FileMode os.FileMode
|
||||
}
|
||||
|
||||
// Option is a functional option for configuring the downloader
|
||||
type Option func(*Options)
|
||||
|
||||
// WithInsecureSkipVerify skips TLS certificate verification
|
||||
func WithInsecureSkipVerify() Option {
|
||||
return func(o *Options) {
|
||||
o.InsecureSkipVerify = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithHTTPClient sets a custom HTTP client
|
||||
func WithHTTPClient(client *http.Client) Option {
|
||||
return func(o *Options) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
}
|
||||
|
||||
// WithProgress sets a progress callback
|
||||
func WithProgress(callback func(downloaded, total int64)) Option {
|
||||
return func(o *Options) {
|
||||
o.OnProgress = callback
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutCreateDirs disables automatic creation of parent directories
|
||||
func WithoutCreateDirs() Option {
|
||||
return func(o *Options) {
|
||||
o.CreateDirs = false
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutOverwrite prevents overwriting existing files
|
||||
func WithoutOverwrite() Option {
|
||||
return func(o *Options) {
|
||||
o.Overwrite = false
|
||||
}
|
||||
}
|
||||
|
||||
func WithFileMode(mode os.FileMode) Option {
|
||||
return func(o *Options) {
|
||||
o.FileMode = mode
|
||||
}
|
||||
}
|
||||
|
||||
// Download downloads a file from URL to the specified destination
|
||||
func Download(ctx context.Context, url, dest string, opts ...Option) error {
|
||||
options := &Options{
|
||||
InsecureSkipVerify: false,
|
||||
CreateDirs: true,
|
||||
Overwrite: true,
|
||||
FileMode: 0644,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
||||
logger.Debug("开始下载文件: %s -> %s", url, dest)
|
||||
|
||||
// Check if file exists and overwrite is disabled
|
||||
if !options.Overwrite {
|
||||
if _, err := os.Stat(dest); err == nil {
|
||||
logger.Debug("文件已存在且不允许覆盖: %s", dest)
|
||||
return fmt.Errorf("file already exists: %s", dest)
|
||||
}
|
||||
}
|
||||
|
||||
// Create parent directories if needed
|
||||
if options.CreateDirs {
|
||||
dir := filepath.Dir(dest)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
logger.Debug("创建目录失败 %s: %v", dir, err)
|
||||
return fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create HTTP client
|
||||
client := options.HTTPClient
|
||||
if client == nil {
|
||||
client = &http.Client{}
|
||||
if options.InsecureSkipVerify {
|
||||
logger.Debug("TLS 证书验证已禁用")
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create HTTP request
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
logger.Debug("创建请求失败: %v", err)
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
// Execute request
|
||||
logger.Debug("发起 HTTP 请求: %s", url)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logger.Debug("下载失败: %v", err)
|
||||
return fmt.Errorf("failed to download: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logger.Debug("HTTP 状态码异常: %d", resp.StatusCode)
|
||||
return fmt.Errorf("bad status: %s", resp.Status)
|
||||
}
|
||||
|
||||
// Get content length for progress reporting
|
||||
contentLength := resp.ContentLength
|
||||
logger.Debug("文件大小: %d bytes", contentLength)
|
||||
|
||||
outFile, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, options.FileMode)
|
||||
if err != nil {
|
||||
logger.Debug("创建文件失败 %s: %v", dest, err)
|
||||
return fmt.Errorf("failed to create file: %w", err)
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
// Copy content with optional progress reporting
|
||||
var written int64
|
||||
if options.OnProgress != nil && contentLength > 0 {
|
||||
// Use progress reader
|
||||
reader := &progressReader{
|
||||
reader: resp.Body,
|
||||
callback: options.OnProgress,
|
||||
total: contentLength,
|
||||
}
|
||||
written, err = io.Copy(outFile, reader)
|
||||
} else {
|
||||
written, err = io.Copy(outFile, resp.Body)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Debug("写入文件失败 %s: %v", dest, err)
|
||||
return fmt.Errorf("failed to write file: %w", err)
|
||||
}
|
||||
|
||||
if options.FileMode != 0 {
|
||||
if err := os.Chmod(dest, options.FileMode); err != nil {
|
||||
logger.Debug("设置文件权限失败 %s: %v", dest, err)
|
||||
return fmt.Errorf("failed to set file mode: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug("文件下载成功: %s (%d bytes)", dest, written)
|
||||
return nil
|
||||
}
|
||||
|
||||
// progressReader wraps an io.Reader to report progress
|
||||
type progressReader struct {
|
||||
reader io.Reader
|
||||
callback func(downloaded, total int64)
|
||||
total int64
|
||||
downloaded int64
|
||||
}
|
||||
|
||||
func (pr *progressReader) Read(p []byte) (int, error) {
|
||||
n, err := pr.reader.Read(p)
|
||||
pr.downloaded += int64(n)
|
||||
if pr.callback != nil {
|
||||
pr.callback(pr.downloaded, pr.total)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
62
pkg/resource/emqx.yaml
Normal file
62
pkg/resource/emqx.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: db-emqx
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: emqx
|
||||
namespace: db-emqx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: emqx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: emqx
|
||||
spec:
|
||||
containers:
|
||||
- name: emqx
|
||||
image: hub.yizhisec.com/external/emqx:5.1
|
||||
ports:
|
||||
- containerPort: 1883
|
||||
name: mqtt
|
||||
- containerPort: 8883
|
||||
name: mqtt-ssl
|
||||
- containerPort: 18083
|
||||
name: dashboard
|
||||
- containerPort: 18084
|
||||
name: websocket
|
||||
env:
|
||||
- name: EMQX_NODE_NAME
|
||||
value: "emqx@single-node"
|
||||
- name: EMQX_DASHBOARD__DEFAULT_PASSWORD
|
||||
value: "YizhiSEC@123"
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: emqx-service
|
||||
namespace: db-emqx
|
||||
spec:
|
||||
selector:
|
||||
app: emqx
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: mqtt
|
||||
port: 1883
|
||||
targetPort: 1883
|
||||
- name: mqtt-ssl
|
||||
port: 8883
|
||||
targetPort: 8883
|
||||
- name: dashboard
|
||||
port: 18083
|
||||
targetPort: 18083
|
||||
- name: websocket
|
||||
port: 18084
|
||||
targetPort: 18084
|
||||
|
||||
1817
pkg/resource/es.init.sh
Normal file
1817
pkg/resource/es.init.sh
Normal file
File diff suppressed because it is too large
Load Diff
121
pkg/resource/es.yaml
Normal file
121
pkg/resource/es.yaml
Normal file
@@ -0,0 +1,121 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: db-es
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: elasticsearch
|
||||
namespace: db-es
|
||||
spec:
|
||||
serviceName: elasticsearch
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: elasticsearch
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: elasticsearch
|
||||
spec:
|
||||
volumes:
|
||||
- name: shared-data
|
||||
emptyDir: {}
|
||||
securityContext:
|
||||
fsGroup: 1000
|
||||
initContainers:
|
||||
- name: fix-permissions
|
||||
image: hub.yizhisec.com/hybridscope/v2/es-init-helper:alpine-3.22.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
args:
|
||||
- |
|
||||
#/bin/sh
|
||||
cp -rf /data/plugins/* /app/shared/
|
||||
chown -R 1000:1000 /usr/share/elasticsearch/data
|
||||
volumeMounts:
|
||||
- name: es-data
|
||||
mountPath: /usr/share/elasticsearch/data
|
||||
- name: shared-data
|
||||
mountPath: /app/shared
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
containers:
|
||||
- name: elasticsearch
|
||||
image: hub.yizhisec.com/external/elasticsearch:7.17.28
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: discovery.type
|
||||
value: single-node
|
||||
- name: ES_JAVA_OPTS
|
||||
value: "-Xms%dg -Xmx%dg"
|
||||
- name: node.name
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
ports:
|
||||
- containerPort: 9200
|
||||
name: http
|
||||
- containerPort: 9300
|
||||
name: transport
|
||||
volumeMounts:
|
||||
- name: es-data
|
||||
mountPath: /usr/share/elasticsearch/data
|
||||
- name: shared-data
|
||||
mountPath: /usr/share/elasticsearch/plugins
|
||||
resources:
|
||||
requests:
|
||||
memory: "%dGi"
|
||||
cpu: "%d"
|
||||
limits:
|
||||
memory: "%dGi"
|
||||
cpu: "%d"
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: es-data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
storageClassName: longhorn
|
||||
resources:
|
||||
requests:
|
||||
storage: %dGi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: es-service
|
||||
namespace: db-es
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: elasticsearch
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 9200
|
||||
targetPort: http
|
||||
- name: transport
|
||||
protocol: TCP
|
||||
port: 9300
|
||||
targetPort: transport
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: es-init-job
|
||||
namespace: db-es
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: es-init
|
||||
image: hub.yizhisec.com/hybridscope/v2/es-init-helper:alpine-3.22.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/sh
|
||||
- /data/create_index.sh
|
||||
restartPolicy: Never
|
||||
backoffLimit: 2
|
||||
214
pkg/resource/flannel.yaml
Normal file
214
pkg/resource/flannel.yaml
Normal file
@@ -0,0 +1,214 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: flannel
|
||||
pod-security.kubernetes.io/enforce: privileged
|
||||
name: kube-flannel
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: flannel
|
||||
name: flannel
|
||||
namespace: kube-flannel
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: flannel
|
||||
name: flannel
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes/status
|
||||
verbs:
|
||||
- patch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: flannel
|
||||
name: flannel
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: flannel
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: flannel
|
||||
namespace: kube-flannel
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
cni-conf.json: |
|
||||
{
|
||||
"name": "cbr0",
|
||||
"cniVersion": "0.3.1",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "flannel",
|
||||
"delegate": {
|
||||
"hairpinMode": true,
|
||||
"isDefaultGateway": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "portmap",
|
||||
"capabilities": {
|
||||
"portMappings": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
net-conf.json: |
|
||||
{
|
||||
"Network": "10.244.0.0/16",
|
||||
"EnableNFTables": false,
|
||||
"Backend": {
|
||||
"Type": "vxlan"
|
||||
}
|
||||
}
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
labels:
|
||||
app: flannel
|
||||
k8s-app: flannel
|
||||
tier: node
|
||||
name: kube-flannel-cfg
|
||||
namespace: kube-flannel
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
labels:
|
||||
app: flannel
|
||||
k8s-app: flannel
|
||||
tier: node
|
||||
name: kube-flannel-ds
|
||||
namespace: kube-flannel
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: flannel
|
||||
k8s-app: flannel
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: flannel
|
||||
k8s-app: flannel
|
||||
tier: node
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: kubernetes.io/os
|
||||
operator: In
|
||||
values:
|
||||
- linux
|
||||
containers:
|
||||
- args:
|
||||
- --ip-masq
|
||||
- --kube-subnet-mgr
|
||||
command:
|
||||
- /opt/bin/flanneld
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: EVENT_QUEUE_DEPTH
|
||||
value: "5000"
|
||||
- name: CONT_WHEN_CACHE_NOT_READY
|
||||
value: "false"
|
||||
image: ghcr.io/flannel-io/flannel:v0.27.4
|
||||
name: kube-flannel
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
privileged: false
|
||||
volumeMounts:
|
||||
- mountPath: /run/flannel
|
||||
name: run
|
||||
- mountPath: /etc/kube-flannel/
|
||||
name: flannel-cfg
|
||||
- mountPath: /run/xtables.lock
|
||||
name: xtables-lock
|
||||
hostNetwork: true
|
||||
initContainers:
|
||||
- args:
|
||||
- -f
|
||||
- /flannel
|
||||
- /opt/cni/bin/flannel
|
||||
command:
|
||||
- cp
|
||||
image: ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1
|
||||
name: install-cni-plugin
|
||||
volumeMounts:
|
||||
- mountPath: /opt/cni/bin
|
||||
name: cni-plugin
|
||||
- args:
|
||||
- -f
|
||||
- /etc/kube-flannel/cni-conf.json
|
||||
- /etc/cni/net.d/10-flannel.conflist
|
||||
command:
|
||||
- cp
|
||||
image: ghcr.io/flannel-io/flannel:v0.27.4
|
||||
name: install-cni
|
||||
volumeMounts:
|
||||
- mountPath: /etc/cni/net.d
|
||||
name: cni
|
||||
- mountPath: /etc/kube-flannel/
|
||||
name: flannel-cfg
|
||||
priorityClassName: system-node-critical
|
||||
serviceAccountName: flannel
|
||||
tolerations:
|
||||
- effect: NoSchedule
|
||||
operator: Exists
|
||||
volumes:
|
||||
- hostPath:
|
||||
path: /run/flannel
|
||||
name: run
|
||||
- hostPath:
|
||||
path: /opt/cni/bin
|
||||
name: cni-plugin
|
||||
- hostPath:
|
||||
path: /etc/cni/net.d
|
||||
name: cni
|
||||
- configMap:
|
||||
name: kube-flannel-cfg
|
||||
name: flannel-cfg
|
||||
- hostPath:
|
||||
path: /run/xtables.lock
|
||||
type: FileOrCreate
|
||||
name: xtables-lock
|
||||
47
pkg/resource/kibana.yaml
Normal file
47
pkg/resource/kibana.yaml
Normal file
@@ -0,0 +1,47 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kibana
|
||||
namespace: db-es
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kibana
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kibana
|
||||
spec:
|
||||
containers:
|
||||
- name: kibana
|
||||
image: hub.yizhisec.com/external/kibana:7.17.28
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: ELASTICSEARCH_HOSTS
|
||||
value: http://es-service:9200
|
||||
- name: SERVER_HOST
|
||||
value: 0.0.0.0
|
||||
ports:
|
||||
- containerPort: 5601
|
||||
name: http
|
||||
resources:
|
||||
limits:
|
||||
memory: 2Gi
|
||||
cpu: 1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kibana-service
|
||||
namespace: db-es
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: kibana
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 5601
|
||||
targetPort: 5601
|
||||
nodePort: 31601
|
||||
83
pkg/resource/less-dns.yaml
Normal file
83
pkg/resource/less-dns.yaml
Normal file
@@ -0,0 +1,83 @@
|
||||
# k8s-hs-less-dns.yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: hs-net
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: config-less-dns
|
||||
namespace: hs-net
|
||||
data:
|
||||
config.yml: |
|
||||
{
|
||||
"log": {
|
||||
"level": "info"
|
||||
},
|
||||
"vnet4": "100.64.0.1/10",
|
||||
"vnet6": "fc00:eeaa:0000:0000::/48",
|
||||
"redis": {
|
||||
"custom": [
|
||||
{
|
||||
"username": null,
|
||||
"password": "HybridScope0xRed1s.",
|
||||
"host": "redis-master.db-redis",
|
||||
"port": 6379,
|
||||
"tls_insecure": null,
|
||||
"db": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
"mqtt": {
|
||||
"client_id": "dns_mqtt_client",
|
||||
"protocol": "tls",
|
||||
"host": "emqx-service.db-emqx",
|
||||
"port": 1883,
|
||||
"cert": "",
|
||||
"key": "",
|
||||
"keep_alive": 60
|
||||
}
|
||||
}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: less-dns
|
||||
namespace: hs-net
|
||||
labels:
|
||||
app: less-dns
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: less-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: less-dns
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app: less-dns
|
||||
containers:
|
||||
- name: less-dns
|
||||
image: hub.yizhisec.com/hybridscope/less_dns_service:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
mountPath: /etc/less_dns_service
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: config-less-dns
|
||||
items:
|
||||
- key: config.yml
|
||||
path: config.yml
|
||||
restartPolicy: Always
|
||||
28
pkg/resource/resource.go
Normal file
28
pkg/resource/resource.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed flannel.yaml
|
||||
YAMLFlannel []byte
|
||||
|
||||
//go:embed es.yaml
|
||||
YAMLES string
|
||||
|
||||
//go:embed kibana.yaml
|
||||
YAMLKibana []byte
|
||||
|
||||
//go:embed es.init.sh
|
||||
BashESInit []byte
|
||||
|
||||
//go:embed emqx.yaml
|
||||
YAMLEMQX []byte
|
||||
|
||||
//go:embed yosguard.create.sql
|
||||
SQLYosguard []byte
|
||||
|
||||
//go:embed less-dns.yaml
|
||||
YAMLLessDNS []byte
|
||||
)
|
||||
36
pkg/resource/yosguard.create.sql
Normal file
36
pkg/resource/yosguard.create.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
CREATE TABLE IF NOT EXISTS `pkg`
|
||||
(
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`path` TEXT,
|
||||
`installed` INTEGER,
|
||||
`create_timestamp` INTEGER,
|
||||
`install_timestamp` INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `patch`
|
||||
(
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`path` TEXT,
|
||||
`verified` INTEGER,
|
||||
`installed` INTEGER,
|
||||
`create_timestamp` INTEGER,
|
||||
`verify_timestamp` INTEGER,
|
||||
`install_timestamp` INTEGER
|
||||
);
|
||||
|
||||
-- 记录注册的机器的信息
|
||||
CREATE TABLE IF NOT EXISTS `machine`
|
||||
(
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`uuid` CHAR(32),
|
||||
`create_timestamp` INTEGER
|
||||
);
|
||||
|
||||
-- 记录下发的指令
|
||||
CREATE TABLE IF NOT EXISTS `action`
|
||||
(
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`uuid` CHAR(32),
|
||||
`action` INTEGER, -- 要下发的指令
|
||||
`create_timestamp` INTEGER -- 下发命令的时间戳,秒为单位
|
||||
);
|
||||
Reference in New Issue
Block a user