重构架构

This commit is contained in:
2026-02-20 18:44:43 +08:00
parent aba7b68439
commit cc71248ef4
52 changed files with 1404 additions and 2360 deletions

View File

@@ -1,68 +0,0 @@
package initcmd
import (
"fmt"
"os"
"sunhpc/internal/auth"
"sunhpc/internal/config"
"sunhpc/internal/log"
"github.com/spf13/cobra"
)
// NewConfigCmd 创建 "init config" 命令
func NewConfigCmd() *cobra.Command {
var (
force bool
path string
verbose bool
)
cmd := &cobra.Command{
Use: "config",
Short: "生成默认配置文件",
Long: `
在指定路径生成 SunHPC 默认配置文件 (sunhpc.yaml)
示例:
sunhpc init config # 生成默认配置文件
sunhpc init config -f # 强制覆盖已有配置文件
sunhpc init config -p /etc/sunhpc/sunhpc.yaml # 指定路径
`,
Annotations: map[string]string{
"require-root": "true", // 假设需要 root你可自定义策略
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := auth.RequireRoot(); err != nil {
return err
}
if path == "" {
path = "/etc/sunhpc/sunhpc.yaml"
}
if !force {
if _, err := os.Stat(path); err == nil {
return fmt.Errorf("配置文件已存在: %s (使用 --force 覆盖)", path)
}
}
if err := config.WriteDefaultConfig(path); err != nil {
return fmt.Errorf("写入配置失败: %w", err)
}
log.Infof("✅ 配置文件已生成: %s", path)
return nil
},
}
// 定义局部 flags
cmd.Flags().BoolVarP(&force, "force", "f", false, "强制覆盖已有配置文件")
cmd.Flags().StringVarP(&path, "path", "p", "", "指定配置文件路径")
cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "显示详细日志")
return cmd
}

View File

@@ -1,44 +0,0 @@
package initcmd
import (
"sunhpc/internal/auth"
"sunhpc/internal/db"
"sunhpc/internal/log"
"github.com/spf13/cobra"
)
func NewDatabaseCmd() *cobra.Command {
var force bool
cmd := &cobra.Command{
Use: "database",
Short: "初始化数据库",
Long: `初始化SQLite数据库,创建所有表结构和默认数据。
示例:
sunhpc init database # 初始化数据库
sunhpc init database --force # 强制重新初始化`,
Annotations: map[string]string{
"skip-db-check": "true", // 标记此命令跳过数据库检查
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := auth.RequireRoot(); err != nil {
return err
}
log.Info("初始化数据库...")
dbInst := db.MustGetDB() // panic if fail (ok for CLI tool)
if err := dbInst.InitSchema(force); err != nil {
return err
}
log.Info("数据库初始化完成")
return nil
},
}
cmd.Flags().BoolVarP(&force, "force", "f", false, "强制重新初始化")
return cmd
}

View File

@@ -1,18 +0,0 @@
package initcmd
import (
"github.com/spf13/cobra"
)
// 仅定义 Cmd 注册子命令,只负责组装命令树,尽量不包含业务逻辑
var Cmd = &cobra.Command{
Use: "init",
Short: "初始化集群配置",
Long: "初始化 SunHPC 配置文件、数据库、系统参数及相关服务",
}
func init() {
// 注册所有子命令(通过工厂函数创建, 例如 DatabaseCmd()
Cmd.AddCommand(NewDatabaseCmd())
Cmd.AddCommand(NewConfigCmd())
}

9
cmd/insert/main.go Normal file
View File

@@ -0,0 +1,9 @@
package main
import (
"fmt"
)
func main() {
fmt.Println("✓ 计算节点添加成功")
}

View File

@@ -1,71 +0,0 @@
package cmd
import (
initcmd "sunhpc/cmd/init"
"sunhpc/cmd/soft"
"sunhpc/cmd/tmpl"
"sunhpc/internal/auth"
"sunhpc/internal/db"
"sunhpc/internal/log"
"github.com/spf13/cobra"
)
var (
cfgFile string
verbose bool
noColor bool
)
var rootCmd = &cobra.Command{
Use: "sunhpc",
Short: "SunHPC - HPC集群一体化运维工具",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// 初始化日志verbose=false 不显示调试信息)
log.Init(verbose)
// 是否禁用颜色
log.EnableColor(!noColor)
log.Debugf("当前命令 Annotations: %+v", cmd.Annotations)
_, err := db.CheckDB()
if err != nil {
log.Warnf("数据库检查失败: %v", err)
}
// 需要 root 权限
if cmd.Annotations["require-root"] == "true" {
if err := auth.RequireRoot(); err != nil {
log.Fatalf("需要 root 权限: %v", err)
}
}
log.Debugf("当前命令: %s", cmd.Name())
log.Debugf("详细模式: %v", verbose)
log.Debugf("禁用颜色: %v", noColor)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
// 同步日志
log.Sync()
log.Close()
},
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}
func Execute() error {
return rootCmd.Execute()
}
func init() {
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件路径 (默认为 /etc/sunhpc/sunhpc.yaml)")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "启用详细日志输出")
rootCmd.PersistentFlags().BoolVar(&noColor, "no-color", false, "禁用彩色输出")
// 注册一级子命令下的子命令树
rootCmd.AddCommand(initcmd.Cmd)
rootCmd.AddCommand(soft.Cmd)
rootCmd.AddCommand(tmpl.Cmd)
}

View File

@@ -1,109 +0,0 @@
// cmd/soft/install.go
package soft
import (
"fmt"
"sunhpc/internal/auth"
"sunhpc/internal/log"
"sunhpc/internal/soft"
"github.com/spf13/cobra"
)
var (
installType string // --type, -t
srcPath string // --src-path, -s
binPath string // --bin-path, -b
prefix string // --prefix, -p
version string // --version, -v
forceInstall bool // --force, -f
dryRun bool // --dry-run, -n
keepSource bool // --keep-source, -k
jobs int // --jobs, -j
offlineMode bool // --offline, -o
)
var installCmd = &cobra.Command{
Use: "install <software>",
Short: "安装软件",
Long: `安装指定的软件包,支持多种安装方式。
安装类型:
source - 从源码编译安装
binary - 从二进制压缩包安装
rpm - 通过 RPM 包管理器安装
deb - 通过 APT 包管理器安装
示例:
sunhpc soft install vasp --type source --src-path /tmp/vasp.tar.gz
sunhpc soft install openmpi --type binary --bin-path openmpi.tar.gz -p /opt/openmpi
sunhpc soft install htop --type rpm --force
sunhpc soft install nginx --type deb --dry-run`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if err := auth.RequireRoot(); err != nil {
return err
}
software := args[0]
if dryRun {
log.Infof("[干运行] 将要安装 %s", software)
log.Infof(" 安装类型: %s", installType)
if srcPath != "" {
log.Infof(" 源码路径: %s", srcPath)
}
if binPath != "" {
log.Infof(" 二进制包: %s", binPath)
}
if prefix != "" {
log.Infof(" 安装路径: %s", prefix)
}
return nil
}
ctx := &soft.InstallContext{
Force: forceInstall,
DryRun: dryRun,
KeepSource: keepSource,
Jobs: jobs,
Offline: offlineMode,
}
switch installType {
case "source":
return soft.InstallFromSource(software, srcPath, prefix, version, ctx)
case "binary":
return soft.InstallFromBinary(software, binPath, prefix, ctx)
case "rpm", "deb":
return soft.InstallFromPackage(software, installType, ctx)
default:
return fmt.Errorf("不支持的安装类型: %s", installType)
}
},
}
func init() {
// 必选参数
installCmd.Flags().StringVarP(&installType, "type", "t", "", "安装类型: source/binary/rpm/deb")
installCmd.MarkFlagRequired("type")
// 路径参数
installCmd.Flags().StringVarP(&srcPath, "src-path", "s", "", "源码路径或URL")
installCmd.Flags().StringVarP(&binPath, "bin-path", "b", "", "二进制压缩包路径")
installCmd.Flags().StringVarP(&prefix, "prefix", "p", "/opt/sunhpc/software", "安装路径")
// 版本参数
installCmd.Flags().StringVarP(&version, "version", "v", "", "软件版本号")
// 行为控制
installCmd.Flags().BoolVarP(&forceInstall, "force", "f", false, "强制安装,覆盖已有版本")
installCmd.Flags().BoolVarP(&dryRun, "dry-run", "n", false, "仅显示将要执行的操作")
installCmd.Flags().BoolVarP(&keepSource, "keep-source", "k", false, "保留源码文件")
installCmd.Flags().IntVarP(&jobs, "jobs", "j", 4, "编译线程数")
installCmd.Flags().BoolVarP(&offlineMode, "offline", "o", false, "离线模式,不联网下载")
// 参数互斥
installCmd.MarkFlagsMutuallyExclusive("src-path", "bin-path")
installCmd.MarkFlagsOneRequired("src-path", "bin-path")
}

View File

@@ -1,16 +0,0 @@
package soft
import (
"github.com/spf13/cobra"
)
var Cmd = &cobra.Command{
Use: "soft",
Short: "软件包管理",
Long: "安装、卸载、编译软件支持源码、RPM、DEB、二进制压缩包",
}
func init() {
Cmd.AddCommand(installCmd)
// 后续可添加 remove、list 等子命令
}

13
cmd/sunhpc/main.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"os"
"sunhpc/internal/cli"
)
func main() {
rootCmd := cli.NewRootCmd()
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}

BIN
cmd/sunhpc/sunhpc Executable file

Binary file not shown.

View File

@@ -1,58 +0,0 @@
package tmpl
import (
"fmt"
"sunhpc/internal/log"
"sunhpc/internal/templating"
"github.com/spf13/cobra"
)
func newDumpCmd() *cobra.Command {
var output string
cmd := &cobra.Command{
Use: "dump <template-name>",
Short: "导出内置模板到文件",
Long: `
将内置的 YAML 模板导出为可编辑的文件。
示例:
sunhpc tmpl dump autofs --output ./my-autofs.yaml`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
// 检查模板是否存在
available, _ := templating.ListEmbeddedTemplates()
found := false
for _, n := range available {
if n == name {
found = true
break
}
}
if !found {
return fmt.Errorf("内置模板 '%s' 不存在。可用模板: %v", name, available)
}
outPath := output
if outPath == "" {
outPath = name + ".yaml"
}
if err := templating.DumpEmbeddedTemplateToFile(name, outPath); err != nil {
return err
}
log.Infof("内置模板 '%s' 已导出到: %s", name, outPath)
log.Infof("你可以编辑此文件,然后用以下命令使用它:")
log.Infof(" sunhpc tmpl render %s -f %s [flags]", name, outPath)
return nil
},
}
cmd.Flags().StringVarP(&output, "output", "o", "", "输出文件路径(默认: <name>.yaml")
return cmd
}

View File

@@ -1,16 +0,0 @@
// cmd/tmpl/init.go
package tmpl
import "github.com/spf13/cobra"
// Cmd 是 sunhpc tmpl 的根命令
var Cmd = &cobra.Command{
Use: "tmpl",
Short: "管理配置模板",
Long: "从 YAML 模板生成配置文件或脚本,支持变量替换和多阶段执行",
}
func init() {
Cmd.AddCommand(newRenderCmd())
Cmd.AddCommand(newDumpCmd())
}

View File

@@ -1,96 +0,0 @@
package tmpl
import (
"fmt"
"sunhpc/internal/log"
"sunhpc/internal/templating"
"github.com/spf13/cobra"
)
var (
tmplFile string
hostname string
domain string
oldHostname string
ip string
clusterName string
outputRoot string
)
func newRenderCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "render <template-name>",
Short: "渲染配置模板",
Long: "根据 YAML 模板和上下文变量生成配置文件或脚本",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
tmplName := args[0]
var template *templating.Template
var err error
// 优先使用 -f 指定的外部模版文件
if tmplFile != "" {
template, err = templating.LoadTemplate(tmplFile)
if err != nil {
return fmt.Errorf("加载外部模板失败: %w", err)
}
log.Infof("✅ 外部模板 '%s' 已加载\n", tmplFile)
} else {
// 否则从内置模板加载
template, err = templating.LoadEmbeddedTemplate(tmplName)
if err != nil {
return err
}
log.Infof("✅ 内置模板 '%s' 已加载\n", tmplName)
}
ctx := templating.Context{
Node: templating.NodeInfo{
Hostname: hostname,
OldHostname: oldHostname,
Domain: domain,
IP: ip,
},
Cluster: templating.ClusterInfo{
Name: clusterName,
},
}
rendered, err := template.Render(ctx)
if err != nil {
return fmt.Errorf("模板渲染失败: %w", err)
}
// 处理 post 阶段
if steps, ok := rendered["post"]; ok {
fmt.Println(">>> 执行 post 阶段")
if err := templating.WriteFiles(steps, outputRoot); err != nil {
return err
}
templating.PrintScripts(steps)
}
// 处理 configure 阶段
if steps, ok := rendered["configure"]; ok {
fmt.Println(">>> 执行 configure 阶段")
templating.PrintScripts(steps)
}
fmt.Println("✅ 模板渲染完成")
return nil
},
}
cmd.Flags().StringVarP(&tmplFile, "file", "f", "", "指定模板文件路径(覆盖默认查找)")
cmd.Flags().StringVar(&hostname, "hostname", "", "节点主机名")
cmd.Flags().StringVar(&domain, "domain", "cluster.local", "DNS 域名")
cmd.Flags().StringVar(&oldHostname, "old-hostname", "", "旧主机名(用于迁移)")
cmd.Flags().StringVar(&ip, "ip", "", "节点 IP 地址")
cmd.Flags().StringVar(&clusterName, "cluster", "default", "集群名称")
cmd.Flags().StringVarP(&outputRoot, "output", "o", "/", "文件输出根目录")
_ = cmd.MarkFlagRequired("hostname")
return cmd
}