eggo/0018-implement-cmd-hooks.patch
zhangxiaoyu 3c0e052f45 update from openeuler
Signed-off-by: zhangxiaoyu <zhangxiaoyu58@huawei.com>
2023-02-03 15:03:58 +08:00

847 lines
27 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 27a99ca97da9540200068d75152896492ecd0064 Mon Sep 17 00:00:00 2001
From: jikui <jikui2@huawei.com>
Date: Tue, 14 Dec 2021 16:33:02 +0800
Subject: [PATCH 18/24] implement cmd hooks
---
cmd/checker.go | 52 ++++++++++
cmd/cleanup.go | 10 +-
cmd/configs.go | 125 ++++++++++++++++++++++++-
cmd/configs_test.go | 2 +-
cmd/delete.go | 12 ++-
cmd/deploy.go | 9 +-
cmd/join.go | 14 ++-
cmd/opts.go | 12 +++
docs/hooks_of_eggo.md | 3 +-
pkg/api/types.go | 18 ++--
pkg/clusterdeployment/binary/binary.go | 25 +++++
pkg/constants/constants.go | 5 +
pkg/utils/dependency/cmdhooks.go | 115 +++++++++++++++++++++++
pkg/utils/dependency/cmdhooks_test.go | 43 +++++++++
pkg/utils/runner/runner.go | 4 +-
pkg/utils/utils.go | 15 +++
16 files changed, 444 insertions(+), 20 deletions(-)
create mode 100644 pkg/utils/dependency/cmdhooks.go
create mode 100644 pkg/utils/dependency/cmdhooks_test.go
diff --git a/cmd/checker.go b/cmd/checker.go
index 9d1fda6..07068e9 100644
--- a/cmd/checker.go
+++ b/cmd/checker.go
@@ -19,11 +19,15 @@ import (
"fmt"
"net"
"net/url"
+ "os"
+ "path"
"path/filepath"
"strconv"
+ "strings"
"time"
"isula.org/eggo/pkg/api"
+ "isula.org/eggo/pkg/constants"
"isula.org/eggo/pkg/utils"
"isula.org/eggo/pkg/utils/endpoint"
chain "isula.org/eggo/pkg/utils/responsibilitychain"
@@ -383,6 +387,54 @@ func checkPackageConfig(pc *PackageConfig) error {
return nil
}
+func checkCmdHooksParameter(pa ...string) error {
+ for _, v := range pa {
+ if v == "" {
+ continue
+ }
+ res := strings.Split(v, ",")
+ if len(res) < 1 || len(res) > 2 {
+ return fmt.Errorf("invalid hook parameter with:%s\n", v)
+ }
+ }
+
+ return nil
+}
+
+func checkHookFile(fileName string) error {
+ file, err := os.Stat(fileName)
+ if err != nil {
+ return err
+
+ }
+
+ if !path.IsAbs(fileName) {
+ return fmt.Errorf("%s is not Abs path", fileName)
+ }
+ if !file.Mode().IsRegular() {
+ return fmt.Errorf("%s is not regular file", file.Name())
+ }
+ if file.Mode().Perm() != os.FileMode(constants.HookFileMode) {
+ return fmt.Errorf("file mode of %s is incorrect", file.Name())
+ }
+ if file.Size() > constants.MaxHookFileSize || file.Size() == 0 {
+ return fmt.Errorf("%s is too large or small", file.Name())
+ }
+ if !(strings.HasSuffix(fileName, ".sh") || strings.HasSuffix(fileName, ".bash")) {
+ return fmt.Errorf("%s is not shell file", file.Name())
+ }
+
+ user, group, err := utils.GetUserIDAndGroupID(fileName)
+ if err != nil {
+ return fmt.Errorf("get user ID and group ID with file %s failed", file.Name())
+ }
+ if user != os.Getuid() && group != os.Getgid() {
+ return fmt.Errorf("user id and group id of %s mismatch with process", file.Name())
+ }
+
+ return nil
+}
+
func (ccr *InstallConfigResponsibility) Execute() error {
if ccr.conf.PackageSrc != nil {
if ccr.conf.PackageSrc.DstPath != "" {
diff --git a/cmd/cleanup.go b/cmd/cleanup.go
index 37bb87f..7a78b15 100644
--- a/cmd/cleanup.go
+++ b/cmd/cleanup.go
@@ -54,17 +54,25 @@ func cleanupCluster(cmd *cobra.Command, args []string) error {
return fmt.Errorf("load deploy config file %v failed: %v", confPath, err)
}
+ if err = checkCmdHooksParameter(opts.clusterPrehook, opts.clusterPosthook); err != nil {
+ return err
+ }
if err = RunChecker(conf); err != nil {
return err
}
+ hooksConf, err := getClusterHookConf(api.HookOpCleanup)
+ if err != nil {
+ return fmt.Errorf("get cmd hooks config failed:%v", err)
+ }
+
holder, err := NewProcessPlaceHolder(eggoPlaceHolderPath(conf.ClusterID))
if err != nil {
return fmt.Errorf("create process holder failed: %v, mayebe other eggo is running with cluster: %s", err, conf.ClusterID)
}
defer holder.Remove()
- if err = cleanup(toClusterdeploymentConfig(conf)); err != nil {
+ if err = cleanup(toClusterdeploymentConfig(conf, hooksConf)); err != nil {
return err
}
diff --git a/cmd/configs.go b/cmd/configs.go
index 326e889..4d7a4b9 100644
--- a/cmd/configs.go
+++ b/cmd/configs.go
@@ -20,6 +20,7 @@ import (
"io/ioutil"
"net"
"os"
+ "path"
"path/filepath"
"strconv"
"strings"
@@ -559,7 +560,7 @@ func fillExtrArgs(ccfg *api.ClusterConfig, eargs []*ConfigExtraArgs) {
}
}
-func toClusterdeploymentConfig(conf *DeployConfig) *api.ClusterConfig {
+func toClusterdeploymentConfig(conf *DeployConfig, hooks []*api.ClusterHookConf) *api.ClusterConfig {
ccfg := getDefaultClusterdeploymentConfig()
setIfStrConfigNotEmpty(&ccfg.Name, conf.ClusterID)
@@ -601,10 +602,132 @@ func toClusterdeploymentConfig(conf *DeployConfig) *api.ClusterConfig {
ccfg.WorkerConfig.KubeletConf.EnableServer = conf.EnableKubeletServing
fillExtrArgs(ccfg, conf.ConfigExtraArgs)
+ ccfg.HooksConf = hooks
return ccfg
}
+func getClusterHookConf(op api.HookOperator) ([]*api.ClusterHookConf, error) {
+ var hooks []*api.ClusterHookConf
+
+ if opts.clusterPrehook != "" {
+ hook, err := getCmdHooks(opts.clusterPrehook, api.ClusterPrehookType, op)
+ if err != nil {
+ return nil, err
+ }
+ hooks = append(hooks, hook)
+ }
+
+ if opts.clusterPosthook != "" {
+ hook, err := getCmdHooks(opts.clusterPosthook, api.ClusterPosthookType, op)
+ if err != nil {
+ return nil, err
+ }
+ hooks = append(hooks, hook)
+ }
+
+ if opts.prehook != "" {
+ hook, err := getCmdHooks(opts.prehook, api.PreHookType, op)
+ if err != nil {
+ return nil, err
+ }
+ hooks = append(hooks, hook)
+ }
+
+ if opts.posthook != "" {
+ hook, err := getCmdHooks(opts.posthook, api.PostHookType, op)
+ if err != nil {
+ return nil, err
+ }
+ hooks = append(hooks, hook)
+ }
+ return hooks, nil
+}
+
+func getCmdHooks(hopts string, ty api.HookType, op api.HookOperator) (*api.ClusterHookConf, error) {
+ path, target, err := getHookPathAndTarget(hopts)
+ if err != nil {
+ return nil, err
+ }
+ hook, err := getResolvedHook(path, ty, op, target)
+ if err != nil {
+ return nil, err
+ }
+ return hook, nil
+}
+
+func getHookPathAndTarget(hook string) (string, uint16, error) {
+ pathAndTarget := strings.Split(hook, ",")
+ if len(pathAndTarget) == 1 {
+ pathAndTarget = append(pathAndTarget, "master")
+ }
+ target, ok := toTypeInt[pathAndTarget[1]]
+ if !ok {
+ return "", 0x0, fmt.Errorf("invalid role:%s", pathAndTarget[1])
+ }
+
+ return pathAndTarget[0], target, nil
+}
+
+func getResolvedHook(path string, ty api.HookType, op api.HookOperator, target uint16) (*api.ClusterHookConf, error) {
+
+ dir, shells, err := getDirAndShells(path)
+ if err != nil {
+ return nil, err
+ }
+
+ return &api.ClusterHookConf{
+ Type: ty,
+ Operator: op,
+ Target: target,
+ HookSrcDir: dir,
+ HookFiles: shells,
+ }, nil
+}
+
+func getDirAndShells(path string) (string, []string, error) {
+ file, err := os.Stat(path)
+ if err != nil {
+ return "", nil, err
+ }
+
+ if !file.IsDir() {
+ return resolveFile(path)
+ }
+
+ return resolvePath(path)
+}
+
+func resolveFile(p string) (string, []string, error) {
+ dir := path.Dir(p)
+ fileName := path.Base(p)
+ if err := checkHookFile(p); err != nil {
+ return "", nil, err
+ }
+
+ return dir, []string{fileName}, nil
+}
+
+func resolvePath(p string) (string, []string, error) {
+ var files []string
+ rd, err := ioutil.ReadDir(p)
+ if err != nil {
+ return "", nil, err
+ }
+
+ for _, fi := range rd {
+ if err := checkHookFile(path.Join(p, fi.Name())); err == nil {
+ files = append(files, fi.Name())
+ } else {
+ logrus.Debugf("check hook file failed:%v", err)
+ }
+ }
+ if len(files) == 0 {
+ return "", nil, fmt.Errorf("empty folder:%s", p)
+ }
+ return p, files, nil
+}
+
func getHostconfigs(format string, ips []string) []*HostConfig {
var confs []*HostConfig
for i, ip := range ips {
diff --git a/cmd/configs_test.go b/cmd/configs_test.go
index 46cb163..04afc51 100644
--- a/cmd/configs_test.go
+++ b/cmd/configs_test.go
@@ -44,7 +44,7 @@ func TestCmdConfigs(t *testing.T) {
t.Fatalf("load deploy config file failed: %v", err)
}
- ccfg := toClusterdeploymentConfig(conf)
+ ccfg := toClusterdeploymentConfig(conf, nil)
d, err := yaml.Marshal(ccfg)
if err != nil {
t.Fatalf("marshal cluster config failed: %v", err)
diff --git a/cmd/delete.go b/cmd/delete.go
index 9d911a9..5990a42 100644
--- a/cmd/delete.go
+++ b/cmd/delete.go
@@ -63,7 +63,7 @@ func getDeletedAndDiffConfigs(conf *DeployConfig, delNames []string) (*DeployCon
return nil, nil, fmt.Errorf("forbidden to delete first master")
}
- clusterConfig := toClusterdeploymentConfig(&diffConfig)
+ clusterConfig := toClusterdeploymentConfig(&diffConfig, nil)
if len(clusterConfig.Nodes) == 0 {
return nil, nil, fmt.Errorf("no valid ip or name found")
}
@@ -89,11 +89,19 @@ func deleteCluster(cmd *cobra.Command, args []string) error {
return fmt.Errorf("load saved deploy config failed: %v", err)
}
+ if err := checkCmdHooksParameter(opts.prehook, opts.posthook); err != nil {
+ return err
+ }
// check saved deploy config
if err = RunChecker(conf); err != nil {
return err
}
+ hooksConf, err := getClusterHookConf(api.HookOpDelete)
+ if err != nil {
+ return fmt.Errorf("get cmd hooks config failed:%v", err)
+ }
+
holder, err := NewProcessPlaceHolder(eggoPlaceHolderPath(conf.ClusterID))
if err != nil {
return fmt.Errorf("create process holder failed: %v, mayebe other eggo is running with cluster: %s", err, conf.ClusterID)
@@ -110,7 +118,7 @@ func deleteCluster(cmd *cobra.Command, args []string) error {
return err
}
- if err = clusterdeployment.DeleteNodes(toClusterdeploymentConfig(conf), diffHostconfigs); err != nil {
+ if err = clusterdeployment.DeleteNodes(toClusterdeploymentConfig(conf, hooksConf), diffHostconfigs); err != nil {
return err
}
diff --git a/cmd/deploy.go b/cmd/deploy.go
index e21bcc5..2d7c441 100644
--- a/cmd/deploy.go
+++ b/cmd/deploy.go
@@ -71,7 +71,11 @@ func deploy(conf *DeployConfig) error {
return fmt.Errorf("save deploy config failed: %v", err)
}
- ccfg := toClusterdeploymentConfig(conf)
+ hooksConf, err := getClusterHookConf(api.HookOpDeploy)
+ if err != nil {
+ return fmt.Errorf("get cmd hooks config failed:%v", err)
+ }
+ ccfg := toClusterdeploymentConfig(conf, hooksConf)
cstatus, err := clusterdeployment.CreateCluster(ccfg, opts.deployEnableRollback)
if err != nil {
@@ -116,6 +120,9 @@ func deployCluster(cmd *cobra.Command, args []string) error {
return fmt.Errorf("load deploy config file failed: %v", err)
}
+ if err = checkCmdHooksParameter(opts.clusterPrehook, opts.clusterPosthook); err != nil {
+ return err
+ }
if err = RunChecker(conf); err != nil {
return err
}
diff --git a/cmd/join.go b/cmd/join.go
index 79d68fc..d035bfe 100644
--- a/cmd/join.go
+++ b/cmd/join.go
@@ -128,7 +128,7 @@ func getMergedAndDiffConfigs(conf *DeployConfig, joinConf *DeployConfig) (*Deplo
diffConfig.Workers = append(diffConfig.Workers, h)
}
- return &mergedConfig, toClusterdeploymentConfig(&diffConfig).Nodes, nil
+ return &mergedConfig, toClusterdeploymentConfig(&diffConfig, nil).Nodes, nil
}
func getFailedConfigs(diffConfigs []*api.HostConfig, cstatus api.ClusterStatus) []*api.HostConfig {
@@ -206,6 +206,9 @@ func joinCluster(cmd *cobra.Command, args []string) error {
}
var err error
+ if err = checkCmdHooksParameter(opts.prehook, opts.posthook); err != nil {
+ return err
+ }
joinConf, err := parseJoinInput(opts.joinYaml, &opts.joinHost, opts.joinType, opts.joinClusterID)
if err != nil {
return err
@@ -237,11 +240,16 @@ func joinCluster(cmd *cobra.Command, args []string) error {
return err
}
- cstatus, err := clusterdeployment.JoinNodes(toClusterdeploymentConfig(conf), diffConfigs)
+ hooksConf, err := getClusterHookConf(api.HookOpJoin)
+ if err != nil {
+ return fmt.Errorf("get cmd hooks config failed:%v", err)
+ }
+
+ cstatus, err := clusterdeployment.JoinNodes(toClusterdeploymentConfig(conf, hooksConf), diffConfigs)
if err != nil {
failedConfigs := getFailedConfigs(diffConfigs, cstatus)
// rollback
- if err1 := clusterdeployment.DeleteNodes(toClusterdeploymentConfig(mergedConf), failedConfigs); err1 != nil {
+ if err1 := clusterdeployment.DeleteNodes(toClusterdeploymentConfig(mergedConf, nil), failedConfigs); err1 != nil {
logrus.Errorf("delete nodes failed when join failed: %v", err1)
}
diff --git a/cmd/opts.go b/cmd/opts.go
index f5204f2..7bb8297 100644
--- a/cmd/opts.go
+++ b/cmd/opts.go
@@ -43,6 +43,10 @@ type eggoOptions struct {
joinYaml string
joinHost HostConfig
delClusterID string
+ clusterPrehook string
+ clusterPosthook string
+ prehook string
+ posthook string
}
var opts eggoOptions
@@ -66,12 +70,16 @@ func setupDeployCmdOpts(deployCmd *cobra.Command) {
flags := deployCmd.Flags()
flags.StringVarP(&opts.deployConfig, "file", "f", defaultDeployConfigPath(), "location of cluster deploy config file, default $HOME/.eggo/deploy.yaml")
flags.BoolVarP(&opts.deployEnableRollback, "rollback", "", true, "rollback failed node to cleanup")
+ flags.StringVarP(&opts.clusterPrehook, "cluster-prehook", "", "", "cluser prehooks when deploy cluser")
+ flags.StringVarP(&opts.clusterPosthook, "cluster-posthook", "", "", "cluster posthook when deploy cluster")
}
func setupCleanupCmdOpts(cleanupCmd *cobra.Command) {
flags := cleanupCmd.Flags()
flags.StringVarP(&opts.cleanupConfig, "file", "f", "", "location of cluster deploy config file")
flags.StringVarP(&opts.cleanupClusterID, "id", "", "", "cluster id")
+ flags.StringVarP(&opts.clusterPrehook, "cluster-prehook", "", "", "cluser prehooks when clenaup cluser")
+ flags.StringVarP(&opts.clusterPosthook, "cluster-posthook", "", "", "cluster posthook when cleaup cluster")
}
func setupJoinCmdOpts(joinCmd *cobra.Command) {
@@ -82,11 +90,15 @@ func setupJoinCmdOpts(joinCmd *cobra.Command) {
flags.IntVarP(&opts.joinHost.Port, "port", "p", 0, "host's ssh port")
flags.StringVarP(&opts.joinClusterID, "id", "", "", "cluster id")
flags.StringVarP(&opts.joinYaml, "file", "f", "", "yaml file contain nodes infomation")
+ flags.StringVarP(&opts.prehook, "prehook", "", "", "prehook when join cluster")
+ flags.StringVarP(&opts.posthook, "posthook", "", "", "posthook when join cluster")
}
func setupDeleteCmdOpts(deleteCmd *cobra.Command) {
flags := deleteCmd.Flags()
flags.StringVarP(&opts.delClusterID, "id", "", "", "cluster id")
+ flags.StringVarP(&opts.prehook, "prehook", "", "", "prehook when delete cluster")
+ flags.StringVarP(&opts.posthook, "posthook", "", "", "posthook when delete cluster")
}
func setupTemplateCmdOpts(templateCmd *cobra.Command) {
diff --git a/docs/hooks_of_eggo.md b/docs/hooks_of_eggo.md
index b1f09cb..fd9ce35 100644
--- a/docs/hooks_of_eggo.md
+++ b/docs/hooks_of_eggo.md
@@ -21,8 +21,9 @@
说明:
- 脚本目录下的所有脚本都会被执行,而子目录中的脚本不会被执行;
-- 每个脚本的超时时间为60s
+- 每个脚本的超时时间为120s
- role可以为master,worker,etcd或者loadbalance
+- 命令行参数指定的hooks脚本默认拷贝到目标机器的/root/.eggo/package/file/cmdhooks目录下,脚本大小限制1M字节;
### 配置文件参数方式
diff --git a/pkg/api/types.go b/pkg/api/types.go
index e5e1958..5cb7121 100644
--- a/pkg/api/types.go
+++ b/pkg/api/types.go
@@ -47,8 +47,10 @@ const (
type HookType string
const (
- PreHookType HookType = "prehook"
- PostHookType HookType = "posthook"
+ ClusterPrehookType HookType = "cluster-prehook"
+ ClusterPosthookType HookType = "cluster-posthook"
+ PreHookType HookType = "prehook"
+ PostHookType HookType = "posthook"
)
type HookRunConfig struct {
@@ -233,11 +235,11 @@ type AddonConfig struct {
}
type ClusterHookConf struct {
- Type HookType
- Operator HookOperator
- Target uint16
- HookDir string
- HookFiles []string
+ Type HookType
+ Operator HookOperator
+ Target uint16
+ HookSrcDir string
+ HookFiles []string
}
type ClusterConfig struct {
@@ -258,7 +260,7 @@ type ClusterConfig struct {
RoleInfra map[uint16]*RoleInfra `json:"role-infra"`
// do not encode hooks, just set before use it
- HooksConf *ClusterHookConf `json:"-"`
+ HooksConf []*ClusterHookConf `json:"-"`
// TODO: add other configurations at here
}
diff --git a/pkg/clusterdeployment/binary/binary.go b/pkg/clusterdeployment/binary/binary.go
index 363de0e..478e081 100644
--- a/pkg/clusterdeployment/binary/binary.go
+++ b/pkg/clusterdeployment/binary/binary.go
@@ -419,6 +419,10 @@ func (bcp *BinaryClusterDeployment) Finish() {
func (bcp *BinaryClusterDeployment) PreCreateClusterHooks() error {
role := []uint16{api.LoadBalance, api.ETCD, api.Master, api.Worker}
+ if err := dependency.ExecuteCmdHooks(bcp.config, bcp.config.Nodes, api.HookOpDeploy, api.ClusterPrehookType); err != nil {
+ return err
+ }
+
if err := dependency.HookSchedule(bcp.config, bcp.config.Nodes, role, api.SchedulePreJoin); err != nil {
return err
}
@@ -434,11 +438,17 @@ func (bcp *BinaryClusterDeployment) PostCreateClusterHooks(nodes []*api.HostConf
if err := checkK8sServices(nodes); err != nil {
return err
}
+ if err := dependency.ExecuteCmdHooks(bcp.config, bcp.config.Nodes, api.HookOpDeploy, api.ClusterPosthookType); err != nil {
+ return err
+ }
return nil
}
func (bcp *BinaryClusterDeployment) PreDeleteClusterHooks() {
role := []uint16{api.Worker, api.Master, api.ETCD, api.LoadBalance}
+ if err := dependency.ExecuteCmdHooks(bcp.config, bcp.config.Nodes, api.HookOpCleanup, api.ClusterPrehookType); err != nil {
+ logrus.Warnf("Ignore: Delete cluster prehook failed:%v", err)
+ }
if err := dependency.HookSchedule(bcp.config, bcp.config.Nodes, role, api.SchedulePreCleanup); err != nil {
logrus.Warnf("Ignore: Delete cluster PreHook failed: %v", err)
}
@@ -449,10 +459,16 @@ func (bcp *BinaryClusterDeployment) PostDeleteClusterHooks() {
if err := dependency.HookSchedule(bcp.config, bcp.config.Nodes, role, api.SchedulePostCleanup); err != nil {
logrus.Warnf("Ignore: Delete cluster PostHook failed: %v", err)
}
+ if err := dependency.ExecuteCmdHooks(bcp.config, bcp.config.Nodes, api.HookOpCleanup, api.ClusterPosthookType); err != nil {
+ logrus.Warnf("Ignore: Delete cluster posthook failed:%v", err)
+ }
}
func (bcp *BinaryClusterDeployment) PreNodeJoinHooks(node *api.HostConfig) error {
role := []uint16{api.Master, api.Worker, api.ETCD}
+ if err := dependency.ExecuteCmdHooks(bcp.config, []*api.HostConfig{node}, api.HookOpJoin, api.PreHookType); err != nil {
+ return err
+ }
if err := dependency.HookSchedule(bcp.config, []*api.HostConfig{node}, role, api.SchedulePreJoin); err != nil {
return err
}
@@ -525,6 +541,9 @@ func (bcp *BinaryClusterDeployment) PostNodeJoinHooks(node *api.HostConfig) erro
if err := dependency.HookSchedule(bcp.config, []*api.HostConfig{node}, role, api.SchedulePostJoin); err != nil {
return err
}
+ if err := dependency.ExecuteCmdHooks(bcp.config, []*api.HostConfig{node}, api.HookOpJoin, api.PostHookType); err != nil {
+ return err
+ }
// taint and label for master node
roles := node.Type
@@ -552,6 +571,9 @@ func (bcp *BinaryClusterDeployment) PostNodeJoinHooks(node *api.HostConfig) erro
func (bcp *BinaryClusterDeployment) PreNodeCleanupHooks(node *api.HostConfig) {
role := []uint16{api.Worker, api.Master, api.ETCD}
+ if err := dependency.ExecuteCmdHooks(bcp.config, []*api.HostConfig{node}, api.HookOpDelete, api.PreHookType); err != nil {
+ logrus.Warnf("Ignore: Delete Node Cmd Prehook failed: %v", err)
+ }
if err := dependency.HookSchedule(bcp.config, []*api.HostConfig{node}, role, api.SchedulePreCleanup); err != nil {
logrus.Warnf("Ignore: Delete Node PreHook failed: %v", err)
}
@@ -562,6 +584,9 @@ func (bcp *BinaryClusterDeployment) PostNodeCleanupHooks(node *api.HostConfig) {
if err := dependency.HookSchedule(bcp.config, []*api.HostConfig{node}, role, api.SchedulePostCleanup); err != nil {
logrus.Warnf("Ignore: Delete Node PostHook failed: %v", err)
}
+ if err := dependency.ExecuteCmdHooks(bcp.config, []*api.HostConfig{node}, api.HookOpDelete, api.PostHookType); err != nil {
+ logrus.Warnf("Ignore: Delete Node Cmd Posthook failed: %v", err)
+ }
}
func (bcp *BinaryClusterDeployment) CleanupLastStep(nodeName string) error {
diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go
index ee02e24..c60d061 100644
--- a/pkg/constants/constants.go
+++ b/pkg/constants/constants.go
@@ -17,6 +17,7 @@ const (
DefaultPkgPath = "/pkg"
DefaultBinPath = "/bin"
DefaultFilePath = "/file"
+ DefaultHookPath = "/file/cmdhook"
DefaultDirPath = "/dir"
DefaultImagePath = "/image"
@@ -27,4 +28,8 @@ const (
// network plugin arguments key
NetworkPluginArgKeyYamlPath = "NetworkYamlPath"
+
+ MaxHookFileSize = int64(1 << 20)
+ // 750: rwxr-x---
+ HookFileMode = uint32(0750)
)
diff --git a/pkg/utils/dependency/cmdhooks.go b/pkg/utils/dependency/cmdhooks.go
new file mode 100644
index 0000000..e6fd9af
--- /dev/null
+++ b/pkg/utils/dependency/cmdhooks.go
@@ -0,0 +1,115 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
+ * eggo licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: jikui
+ * Create: 2021-12-11
+ * Description: eggo cmd hooks implement
+ ******************************************************************************/
+
+package dependency
+
+import (
+ "fmt"
+ "path"
+
+ "github.com/sirupsen/logrus"
+ "isula.org/eggo/pkg/api"
+ "isula.org/eggo/pkg/constants"
+ "isula.org/eggo/pkg/utils"
+ "isula.org/eggo/pkg/utils/nodemanager"
+ "isula.org/eggo/pkg/utils/runner"
+ "isula.org/eggo/pkg/utils/task"
+)
+
+type CopyHooksTask struct {
+ hooks *api.ClusterHookConf
+}
+
+func (ch *CopyHooksTask) Name() string {
+ return "CopyHooksTask"
+}
+
+func (ch *CopyHooksTask) Run(r runner.Runner, hcg *api.HostConfig) error {
+ dstDir := path.Join(constants.DefaultPackagePath, constants.DefaultHookPath)
+
+ if _, err := r.RunCommand(fmt.Sprintf("sudo -E /bin/sh -c \"test -d %s || mkdir -p %s\"", dstDir, dstDir)); err != nil {
+ return err
+ }
+
+ if err := r.Copy(ch.hooks.HookSrcDir, dstDir); err != nil {
+ return fmt.Errorf("copy from %s to %s for %s failed:%v", ch.hooks.HookSrcDir, dstDir, hcg.Address, err)
+ }
+
+ return nil
+}
+
+func ExecuteCmdHooks(ccfg *api.ClusterConfig, nodes []*api.HostConfig, op api.HookOperator, ty api.HookType) error {
+ for _, hooks := range ccfg.HooksConf {
+ for _, node := range nodes {
+ if !utils.IsType(node.Type, hooks.Target) {
+ continue
+ }
+
+ shell := getCmdShell(hooks, hooks.Target, op, ty)
+ if shell == nil {
+ return nil
+ }
+ if err := doCopyHooks(hooks, node); err != nil {
+ return err
+ }
+ if err := executeCmdHooks(ccfg, hooks, node, shell); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func executeCmdHooks(ccfg *api.ClusterConfig, hooks *api.ClusterHookConf, hcf *api.HostConfig, shell []*api.PackageConfig) error {
+ hookConf := &api.HookRunConfig{
+ ClusterID: ccfg.Name,
+ ClusterApiEndpoint: ccfg.APIEndpoint.GetUrl(),
+ ClusterConfigDir: ccfg.ConfigDir,
+ HookType: hooks.Type,
+ Operator: hooks.Operator,
+ Node: hcf,
+ HookDir: path.Join(ccfg.PackageSrc.GetPkgDstPath(), constants.DefaultHookPath),
+ Hooks: shell,
+ }
+
+ return ExecuteHooks(hookConf)
+}
+
+func getCmdShell(hooks *api.ClusterHookConf, target uint16, op api.HookOperator, ty api.HookType) []*api.PackageConfig {
+ res := make([]*api.PackageConfig, len(hooks.HookFiles))
+
+ if hooks.Target != target || hooks.Operator != op || hooks.Type != ty {
+ return nil
+ }
+ for i, v := range hooks.HookFiles {
+ res[i] = &api.PackageConfig{
+ Name: v,
+ TimeOut: "120s",
+ }
+ }
+ return res
+}
+
+func doCopyHooks(hcc *api.ClusterHookConf, node *api.HostConfig) error {
+ copyHooksTask := task.NewTaskInstance(&CopyHooksTask{
+ hooks: hcc,
+ })
+
+ if err := nodemanager.RunTaskOnNodes(copyHooksTask, []string{node.Address}); err != nil {
+ logrus.Errorf("Copy hooks failed with:%v", err)
+ return err
+ }
+ return nil
+}
diff --git a/pkg/utils/dependency/cmdhooks_test.go b/pkg/utils/dependency/cmdhooks_test.go
new file mode 100644
index 0000000..106518a
--- /dev/null
+++ b/pkg/utils/dependency/cmdhooks_test.go
@@ -0,0 +1,43 @@
+package dependency
+
+import (
+ "testing"
+
+ "isula.org/eggo/pkg/api"
+)
+
+func TestCopyHooks(t *testing.T) {
+ var mr MockRunner
+
+ hs := &api.ClusterHookConf{
+ Type: api.PreHookType,
+ Operator: api.HookOpDeploy,
+ Target: api.Master,
+ HookSrcDir: "/tmp",
+ HookFiles: []string{"test.sh", "test2.bash"},
+ }
+
+ node := &api.HostConfig{}
+
+ ct := &CopyHooksTask{hooks: hs}
+ if err := ct.Run(&mr, node); err != nil {
+ t.Fatalf("run test failed: %v", err)
+ }
+}
+
+func TestExecuteCmdHooks(t *testing.T) {
+ hooks := &api.ClusterHookConf{
+ Target: api.Master,
+ Operator: api.HookOpDeploy,
+ Type: api.PreHookType,
+ }
+ host := &api.HostConfig{
+ Type: api.Master,
+ }
+ ccfg := &api.ClusterConfig{
+ HooksConf: []*api.ClusterHookConf{hooks},
+ }
+ if err := ExecuteCmdHooks(ccfg, []*api.HostConfig{host}, api.HookOpJoin, api.PostHookType); err != nil {
+ t.Fatalf("run test failed: %v", err)
+ }
+}
diff --git a/pkg/utils/runner/runner.go b/pkg/utils/runner/runner.go
index 9a739ca..09c9e1d 100644
--- a/pkg/utils/runner/runner.go
+++ b/pkg/utils/runner/runner.go
@@ -227,7 +227,7 @@ func (ssh *SSHRunner) copyDir(srcDir, dstDir string) error {
return err
}
tmpCpyDir := api.GetUserTempDir(ssh.Host.User)
- tmpPkiFile := filepath.Join(tmpCpyDir, "pkg.tar")
+ tmpPkiFile := filepath.Join(tmpCpyDir, "remote-pkg.tar")
// scp to user home directory
err = ssh.Copy(tmpPkgFile, tmpPkiFile)
if err != nil {
@@ -235,7 +235,7 @@ func (ssh *SSHRunner) copyDir(srcDir, dstDir string) error {
return err
}
// untar tmp file
- _, err = ssh.RunCommand(fmt.Sprintf("sudo -E /bin/sh -c \"cd %s && mv %s . && tar -xf %s && rm -rf %s\"", dstDir, tmpPkiFile, "pki.tar", tmpPkiFile))
+ _, err = ssh.RunCommand(fmt.Sprintf("sudo -E /bin/sh -c \"cd %s && mv %s . && tar -xf %s && rm -rf %s\"", dstDir, tmpPkiFile, "remote-pkg.tar", "remote-pkg.tar"))
if err != nil {
logrus.Errorf("[%s] untar tmp tar failed: %v", ssh.Host.Name, err)
return err
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
index 8272439..059516c 100644
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -16,10 +16,12 @@
package utils
import (
+ "fmt"
"os"
"os/user"
"path/filepath"
"strings"
+ "syscall"
"isula.org/eggo/pkg/api"
)
@@ -107,3 +109,16 @@ func IsDocker(engine string) bool {
func IsContainerd(engine string) bool {
return strings.ToLower(engine) == "containerd"
}
+
+func GetUserIDAndGroupID(file string) (int, int, error) {
+ fileInfo, err := os.Stat(file)
+ if err != nil {
+ return 0, 0, err
+ }
+ statInfo, ok := fileInfo.Sys().(*syscall.Stat_t)
+ if !ok {
+ return 0, 0, fmt.Errorf("Assert failed when stat %s", file)
+ }
+
+ return int(statInfo.Uid), int(statInfo.Gid), nil
+}
--
2.25.1