|
|
package framework
|
|
|
|
|
|
import (
|
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
|
"os"
|
|
|
"os/exec"
|
|
|
"path/filepath"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
)
|
|
|
|
|
|
// 使用步骤:
|
|
|
// 1. installer := NewInstaller(config)
|
|
|
// 2. installer.Install()
|
|
|
|
|
|
type Installer struct {
|
|
|
Steps []Install `json:"steps"`
|
|
|
|
|
|
config *Config
|
|
|
once sync.Once
|
|
|
}
|
|
|
|
|
|
func NewInstaller(config *Config) *Installer {
|
|
|
return &Installer{config: config}
|
|
|
}
|
|
|
|
|
|
// 加载配置到 installer 对象
|
|
|
func (i *Installer) init() error {
|
|
|
var err error
|
|
|
// 只能执行一次
|
|
|
i.once.Do(func() {
|
|
|
// 安全检查,config不能为空
|
|
|
if i.config == nil || i.config.Sub("install") == nil {
|
|
|
err = field.Invalid(
|
|
|
field.NewPath("install"),
|
|
|
nil,
|
|
|
"Not inital config object")
|
|
|
return
|
|
|
}
|
|
|
|
|
|
installer := new(Installer)
|
|
|
if e := i.config.Sub("install").Unmarshal(installer); e != nil {
|
|
|
err = e
|
|
|
return
|
|
|
}
|
|
|
if installer.Steps != nil {
|
|
|
i.Steps = installer.Steps
|
|
|
}
|
|
|
})
|
|
|
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
func (i *Installer) Install() error {
|
|
|
// 1. 初始化,加载配置
|
|
|
if err := i.init(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
// 2. 判断执行的队列是否存在
|
|
|
if len(i.Steps) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// 3. 遍历这个队列,执行 validate
|
|
|
root := field.NewPath("install")
|
|
|
for index, inst := range i.Steps {
|
|
|
fld := root.Index(index)
|
|
|
if err := (&inst).validate(fld); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 4. 遍历这个队列,执行 install
|
|
|
for _, inst := range i.Steps {
|
|
|
inst.config = i.config
|
|
|
if err := (&inst).install(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
type Install struct {
|
|
|
Name string `json:"name"`
|
|
|
Cmd string `json:"cmd"`
|
|
|
Args []string `json:"args"`
|
|
|
Path string `json:"path"`
|
|
|
IgnoreFail bool `json:"ignoreFail"`
|
|
|
|
|
|
config *Config `json:"-"`
|
|
|
}
|
|
|
|
|
|
func (i *Install) validate(root *field.Path) error {
|
|
|
errs := field.ErrorList{}
|
|
|
// 1. 验证 name 字段
|
|
|
if strings.TrimSpace(i.Name) == "" {
|
|
|
errs = append(errs, field.Invalid(root.Child("name"), i.Name, "Cannot be empty"))
|
|
|
}
|
|
|
|
|
|
// 2. 验证 cmd 字段
|
|
|
if strings.TrimSpace(i.Cmd) == "" {
|
|
|
errs = append(errs, field.Invalid(root.Child("cmd"), i.Cmd, "Cannot be empty"))
|
|
|
}
|
|
|
|
|
|
// 3. 检查并设置 path 默认值
|
|
|
if strings.TrimSpace(i.Path) == "" {
|
|
|
i.Path = "."
|
|
|
}
|
|
|
|
|
|
return errs.ToAggregate()
|
|
|
}
|
|
|
|
|
|
func (i *Install) install() error {
|
|
|
// 1. 获取当前路径
|
|
|
currentDir, err := os.Getwd()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
absPath := i.Path
|
|
|
if !filepath.IsAbs(absPath) {
|
|
|
if absPath, err = filepath.Abs(absPath); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
// 2. 切换路径
|
|
|
// 2.1 判断路径是否相同
|
|
|
if currentDir != absPath {
|
|
|
// 2.1.1 不同责切换路径
|
|
|
if err := os.Chdir(absPath); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 3. defer 推出前回到之前的路径
|
|
|
defer func() { _ = os.Chdir(currentDir) }()
|
|
|
|
|
|
// 4. 执行命令
|
|
|
cmd := exec.Command(i.Cmd, i.Args...)
|
|
|
cmd.Stdout = i.config.Stdout
|
|
|
cmd.Stderr = i.config.Stderr
|
|
|
return cmd.Run()
|
|
|
}
|