This commit is contained in:
2026-02-15 07:18:14 +08:00
parent d7cd899983
commit 8a7bf8a39c
39 changed files with 1578 additions and 2868 deletions

58
cmd/tmpl/dump.go Normal file
View File

@@ -0,0 +1,58 @@
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
}

16
cmd/tmpl/init.go Normal file
View File

@@ -0,0 +1,16 @@
// 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())
}

96
cmd/tmpl/render.go Normal file
View File

@@ -0,0 +1,96 @@
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
}