Projects
Eulaceura:Mainline
eggo
_service:obs_scm:0018-implement-cmd-hooks.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:obs_scm:0018-implement-cmd-hooks.patch of Package eggo
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
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2