eggo/0011-run-hook-with-envs-of-cluster-info.patch
zhangxiaoyu a7c766a98c upgrade to v0.9.4-2
Signed-off-by: zhangxiaoyu <zhangxiaoyu58@huawei.com>
2021-11-26 09:40:50 +08:00

469 lines
15 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 f1d2766bda44a878777ea266573236271dcc65ed Mon Sep 17 00:00:00 2001
From: haozi007 <liuhao27@huawei.com>
Date: Thu, 11 Nov 2021 11:15:37 +0000
Subject: [PATCH 11/12] run hook with envs of cluster info
Signed-off-by: haozi007 <liuhao27@huawei.com>
---
docs/hooks_of_eggo.md | 2 +-
pkg/api/tools.go | 29 ++++++++
pkg/api/types.go | 42 ++++++++++++
.../binary/infrastructure/infrastructure.go | 10 +--
pkg/utils/dependency/dependency.go | 40 ++++++++---
pkg/utils/dependency/dependency_test.go | 52 +++++++++++++++
pkg/utils/dependency/install.go | 66 +++++++++++++++----
pkg/utils/runner/runner.go | 16 ++---
8 files changed, 216 insertions(+), 41 deletions(-)
create mode 100644 pkg/utils/dependency/dependency_test.go
diff --git a/docs/hooks_of_eggo.md b/docs/hooks_of_eggo.md
index 92b9a38..b1f09cb 100644
--- a/docs/hooks_of_eggo.md
+++ b/docs/hooks_of_eggo.md
@@ -22,6 +22,7 @@
- 脚本目录下的所有脚本都会被执行,而子目录中的脚本不会被执行;
- 每个脚本的超时时间为60s
+- role可以为master,worker,etcd或者loadbalance
### 配置文件参数方式
@@ -60,4 +61,3 @@ eggo会在hook执行时通过环境变量传递部分信息用于脚本执
| EGGO_NODE_ROLE | hook执行的节点角色 |
| EGGO_HOOK_TYPE | hook的类型prehook或者posthook |
| EGGO_OPERATOR | 当前的操作deploycleanupjoindelete。 |
-
diff --git a/pkg/api/tools.go b/pkg/api/tools.go
index 861d70a..4c65dc2 100644
--- a/pkg/api/tools.go
+++ b/pkg/api/tools.go
@@ -55,6 +55,10 @@ func (p PackageSrcConfig) GetPkgDstPath() string {
return p.DstPath
}
+func (ep APIEndpoint) GetUrl() string {
+ return fmt.Sprintf("%s/%v", ep.AdvertiseAddress, ep.BindPort)
+}
+
func GetClusterHomePath(cluster string) string {
return filepath.Join(EggoHomePath, cluster)
}
@@ -191,3 +195,28 @@ func ParseScheduleType(schedule string) (ScheduleType, error) {
return SchedulePreJoin, fmt.Errorf("invalid schedule type: %s", schedule)
}
}
+
+func GetRoleString(roles uint16) []string {
+ var roleStrs []string
+ if roles&Master != 0 {
+ roleStrs = append(roleStrs, "master")
+ }
+ if roles&Worker != 0 {
+ roleStrs = append(roleStrs, "worker")
+ }
+ if roles&ETCD != 0 {
+ roleStrs = append(roleStrs, "etcd")
+ }
+ if roles&LoadBalance != 0 {
+ roleStrs = append(roleStrs, "loadbalance")
+ }
+
+ return roleStrs
+}
+
+func GetUserTempDir(user string) string {
+ if user == "root" {
+ return constants.DefaultRootCopyTempDirHome
+ }
+ return fmt.Sprintf(constants.DefaultUserCopyTempHomeFormat, user)
+}
diff --git a/pkg/api/types.go b/pkg/api/types.go
index 6a1351e..fea3f26 100644
--- a/pkg/api/types.go
+++ b/pkg/api/types.go
@@ -35,6 +35,37 @@ const (
SchedulePostCleanup ScheduleType = "postcleanup"
)
+type HookOperator string
+
+const (
+ HookOpDeploy HookOperator = "deploy"
+ HookOpCleanup HookOperator = "cleanup"
+ HookOpJoin HookOperator = "join"
+ HookOpDelete HookOperator = "delete"
+)
+
+type HookType string
+
+const (
+ PreHookType HookType = "prehook"
+ PostHookType HookType = "posthook"
+)
+
+type HookRunConfig struct {
+ ClusterID string
+ ClusterApiEndpoint string
+ ClusterConfigDir string
+
+ HookType HookType
+ Operator HookOperator
+
+ Node *HostConfig
+ Scheduler ScheduleType
+
+ HookDir string
+ Hooks []*PackageConfig
+}
+
type RoleInfra struct {
OpenPorts []*OpenPorts `json:"open-ports"`
Softwares []*PackageConfig `json:"softwares"`
@@ -201,6 +232,14 @@ type AddonConfig struct {
Filename string `json:"filename"`
}
+type ClusterHookConf struct {
+ Type HookType
+ Operator HookOperator
+ Target uint16
+ HookDir string
+ HookFiles []string
+}
+
type ClusterConfig struct {
Name string `json:"name"`
DeployDriver string `json:"deploy-driver"` // default is binary
@@ -218,6 +257,9 @@ type ClusterConfig struct {
WorkerConfig WorkerConfig `json:"workerconfig"`
RoleInfra map[uint16]*RoleInfra `json:"role-infra"`
+ // do not encode hooks, just set before use it
+ HooksConf *ClusterHookConf `json:"-"`
+
// TODO: add other configurations at here
}
diff --git a/pkg/clusterdeployment/binary/infrastructure/infrastructure.go b/pkg/clusterdeployment/binary/infrastructure/infrastructure.go
index 634e338..68faf36 100644
--- a/pkg/clusterdeployment/binary/infrastructure/infrastructure.go
+++ b/pkg/clusterdeployment/binary/infrastructure/infrastructure.go
@@ -27,7 +27,6 @@ import (
"isula.org/eggo/pkg/api"
"isula.org/eggo/pkg/clusterdeployment/binary/cleanupcluster"
- "isula.org/eggo/pkg/constants"
"isula.org/eggo/pkg/utils"
"isula.org/eggo/pkg/utils/dependency"
"isula.org/eggo/pkg/utils/nodemanager"
@@ -321,13 +320,6 @@ func (it *DestroyInfraTask) Name() string {
return "DestroyInfraTask"
}
-func getCopyDefaultDir(user string) string {
- if user == "root" {
- return constants.DefaultRootCopyTempDirHome
- }
- return fmt.Sprintf(constants.DefaultUserCopyTempHomeFormat, user)
-}
-
func (it *DestroyInfraTask) Run(r runner.Runner, hcg *api.HostConfig) error {
if hcg == nil {
return fmt.Errorf("empty host config")
@@ -348,7 +340,7 @@ func (it *DestroyInfraTask) Run(r runner.Runner, hcg *api.HostConfig) error {
logrus.Errorf("path %s not in White List and cannot remove", dstDir)
return nil
}
- copyTempDir := getCopyDefaultDir(hcg.UserName)
+ copyTempDir := api.GetUserTempDir(hcg.UserName)
if _, err := r.RunCommand(fmt.Sprintf("sudo -E /bin/sh -c \"rm -rf %s %s %s\"", dstDir, copyTempDir, it.k8sConfigDir)); err != nil {
return fmt.Errorf("rm dependency failed: %v", err)
}
diff --git a/pkg/utils/dependency/dependency.go b/pkg/utils/dependency/dependency.go
index 9e6ac22..2c5dc26 100644
--- a/pkg/utils/dependency/dependency.go
+++ b/pkg/utils/dependency/dependency.go
@@ -293,6 +293,7 @@ func (dy *dependencyYaml) Remove(r runner.Runner) error {
}
type dependencyShell struct {
+ envs []string
srcPath string
shell []*api.PackageConfig
}
@@ -305,22 +306,45 @@ func NewDependencyShell(srcPath string, shell []*api.PackageConfig) *dependencyS
}
func (ds *dependencyShell) Install(r runner.Runner) error {
- var sb strings.Builder
+ shellTemplate := `
+#!/bin/bash
+{{- range $i, $v := .Envs }}
+export {{ $v }}
+{{- end }}
- sb.WriteString("sudo -E /bin/sh -c \"")
- for _, s := range ds.shell {
- sb.WriteString(fmt.Sprintf("chmod +x %s/%s && ", ds.srcPath, s.Name))
+{{- $tout := .Timeouts }}
+{{- range $i, $v := .Shells }}
+chmod +x {{ $v }} && timeout -s SIGKILL {{index $tout $i}} {{ $v }} > /dev/null
+if [ $? -ne 0 ]; then
+ echo "run {{ $v }} failed"
+ exit 1
+fi
+{{- end }}
+exit 0
+`
+ datastore := map[string]interface{}{}
+ datastore["Envs"] = ds.envs
+ var shells []string
+ var timeouts []string
+ for _, s := range ds.shell {
+ shells = append(shells, fmt.Sprintf("%s/%s", ds.srcPath, s.Name))
timeout := s.TimeOut
if timeout == "" {
timeout = "30s"
}
- sb.WriteString(fmt.Sprintf("timeout -s SIGKILL %s %s/%s > /dev/null ; ", timeout, ds.srcPath, s.Name))
+ timeouts = append(timeouts, timeout)
}
- sb.WriteString("\"")
+ datastore["Shells"] = shells
+ datastore["Timeouts"] = timeouts
- if _, err := r.RunCommand(sb.String()); err != nil {
- return fmt.Errorf("shell execute failed: %v", err)
+ parsedShell, err := template.TemplateRender(shellTemplate, datastore)
+ if err != nil {
+ return err
+ }
+
+ if _, err := r.RunShell(parsedShell, "exechook"); err != nil {
+ return fmt.Errorf("hook execute failed: %v", err)
}
return nil
diff --git a/pkg/utils/dependency/dependency_test.go b/pkg/utils/dependency/dependency_test.go
new file mode 100644
index 0000000..58ea756
--- /dev/null
+++ b/pkg/utils/dependency/dependency_test.go
@@ -0,0 +1,52 @@
+package dependency
+
+import (
+ "testing"
+
+ "github.com/sirupsen/logrus"
+ "isula.org/eggo/pkg/api"
+)
+
+type MockRunner struct {
+}
+
+func (m *MockRunner) Copy(src, dst string) error {
+ logrus.Infof("copy %s to %s", src, dst)
+ return nil
+}
+
+func (m *MockRunner) RunCommand(cmd string) (string, error) {
+ logrus.Infof("run command: %s", cmd)
+ return "", nil
+}
+
+func (m *MockRunner) RunShell(shell string, name string) (string, error) {
+ logrus.Infof("run shell: %s, name: %s", shell, name)
+ return "", nil
+}
+
+func (m *MockRunner) Reconnect() error {
+ logrus.Infof("reconnect")
+ return nil
+}
+
+func (m *MockRunner) Close() {
+ logrus.Infof("close")
+}
+
+func TestNewDependencyShell(t *testing.T) {
+ var mr MockRunner
+
+ shell := &api.PackageConfig{
+ Name: "test.sh",
+ Type: "shell",
+ Dst: "/root",
+ Schedule: api.SchedulePreJoin,
+ TimeOut: "30s",
+ }
+
+ dp := NewDependencyShell("/tmp", []*api.PackageConfig{shell})
+ if err := dp.Install(&mr); err != nil {
+ t.Fatalf("run test failed: %v", err)
+ }
+}
diff --git a/pkg/utils/dependency/install.go b/pkg/utils/dependency/install.go
index 8687602..8cb80f6 100644
--- a/pkg/utils/dependency/install.go
+++ b/pkg/utils/dependency/install.go
@@ -349,33 +349,77 @@ func getShell(roleInfra *api.RoleInfra, schedule api.ScheduleType) []*api.Packag
return shell
}
-func ExecuteShell(roleInfra *api.RoleInfra, packagePath string, hcf *api.HostConfig, schedule api.ScheduleType) error {
- shell := getShell(roleInfra, schedule)
- if len(shell) == 0 {
+func ExecuteHooks(hookConf *api.HookRunConfig) error {
+ if hookConf == nil || len(hookConf.Hooks) == 0 {
return nil
}
- logrus.Debugf("run %s shell %v on %v\n", string(schedule), shell, hcf.Address)
- dp := &dependencyShell{
- srcPath: path.Join(packagePath, constants.DefaultFilePath),
- shell: shell,
+ var hookStr []string
+ for _, h := range hookConf.Hooks {
+ hookStr = append(hookStr, h.Name)
}
+ logrus.Debugf("run %s shell %v on %v\n", string(hookConf.Scheduler), hookStr, hookConf.Node.Address)
+
+ dp := &dependencyShell{
+ srcPath: hookConf.HookDir,
+ shell: hookConf.Hooks,
+ }
+ envs := make([]string, 9)
+ envs[0] = fmt.Sprintf("EGGO_CLUSTER_ID=%s", hookConf.ClusterID)
+ envs[1] = fmt.Sprintf("EGGO_CLUSTER_API_ENDPOINT=%s", hookConf.ClusterApiEndpoint)
+ envs[2] = fmt.Sprintf("EGGO_CLUSTER_CONFIG_DIR=%s", hookConf.ClusterConfigDir)
+ envs[3] = fmt.Sprintf("EGGO_NODE_IP=%s", hookConf.Node.Address)
+ envs[4] = fmt.Sprintf("EGGO_NODE_NAME=%s", hookConf.Node.Name)
+ envs[5] = fmt.Sprintf("EGGO_NODE_ARCH=%s", hookConf.Node.Arch)
+ envs[6] = fmt.Sprintf("EGGO_NODE_ROLE=%s", strings.Join(api.GetRoleString(hookConf.Node.Type), ","))
+ envs[7] = fmt.Sprintf("EGGO_HOOK_TYPE=%s", hookConf.HookType)
+ envs[8] = fmt.Sprintf("EGGO_OPERATOR=%s", hookConf.Operator)
+ dp.envs = envs
dependencyTask := task.NewTaskInstance(&DependencyTask{
dp: dp,
})
- if api.IsCleanupSchedule(schedule) {
+ if api.IsCleanupSchedule(hookConf.Scheduler) {
task.SetIgnoreErrorFlag(dependencyTask)
}
- if err := nodemanager.RunTaskOnNodes(dependencyTask, []string{hcf.Address}); err != nil {
- logrus.Errorf("Hook %s failed for %s: %v", string(api.SchedulePreJoin), hcf.Address, err)
+ if err := nodemanager.RunTaskOnNodes(dependencyTask, []string{hookConf.Node.Address}); err != nil {
+ logrus.Errorf("Hook %s failed for %s: %v", string(api.SchedulePreJoin), hookConf.Node.Address, err)
return err
}
return nil
}
+func executeShell(ccfg *api.ClusterConfig, role uint16, hcf *api.HostConfig, schedule api.ScheduleType) error {
+ shell := getShell(ccfg.RoleInfra[role], schedule)
+ if len(shell) == 0 {
+ return nil
+ }
+
+ htype := api.PreHookType
+ if strings.HasPrefix(string(schedule), "post") {
+ htype = api.PostHookType
+ }
+ oper := api.HookOpJoin
+ if strings.HasSuffix(string(schedule), "cleanup") {
+ oper = api.HookOpCleanup
+ }
+
+ hookConf := &api.HookRunConfig{
+ ClusterID: ccfg.Name,
+ ClusterApiEndpoint: ccfg.APIEndpoint.GetUrl(),
+ ClusterConfigDir: ccfg.ConfigDir,
+ HookType: htype,
+ Operator: oper,
+ Node: hcf,
+ HookDir: path.Join(ccfg.PackageSrc.GetPkgDstPath(), constants.DefaultFilePath),
+ Hooks: shell,
+ }
+
+ return ExecuteHooks(hookConf)
+}
+
func HookSchedule(ccfg *api.ClusterConfig, nodes []*api.HostConfig, role []uint16, schedule api.ScheduleType) error {
for _, n := range nodes {
for _, r := range role {
@@ -383,7 +427,7 @@ func HookSchedule(ccfg *api.ClusterConfig, nodes []*api.HostConfig, role []uint1
continue
}
- if err := ExecuteShell(ccfg.RoleInfra[r], ccfg.PackageSrc.GetPkgDstPath(), n, schedule); err != nil {
+ if err := executeShell(ccfg, r, n, schedule); err != nil {
if api.IsCleanupSchedule(schedule) {
logrus.Errorf("execute shell failed for %s at %s: %v", n.Address, string(schedule), err)
} else {
diff --git a/pkg/utils/runner/runner.go b/pkg/utils/runner/runner.go
index c7088df..83a81e9 100644
--- a/pkg/utils/runner/runner.go
+++ b/pkg/utils/runner/runner.go
@@ -30,7 +30,6 @@ import (
"github.com/kubesphere/kubekey/pkg/util/ssh"
"github.com/sirupsen/logrus"
"isula.org/eggo/pkg/api"
- "isula.org/eggo/pkg/constants"
)
const (
@@ -164,7 +163,7 @@ func (ssh *SSHRunner) Reconnect() error {
func clearUserTempDir(conn ssh.Connection, host *kkv1alpha1.HostCfg) {
tmpShell := "/tmp/" + RunnerShellPrefix + "*"
// scp to tmp file
- dir := getCopyDefaultDir(host.User)
+ dir := api.GetUserTempDir(host.User)
_, err := conn.Exec(fmt.Sprintf("sudo -E /bin/sh -c \"rm -rf %s; rm -rf %s\"", dir, tmpShell), host)
if err != nil {
logrus.Warnf("[%s] remove temp dir: %s failed: %v", host.Name, dir, err)
@@ -175,7 +174,7 @@ func clearUserTempDir(conn ssh.Connection, host *kkv1alpha1.HostCfg) {
func prepareUserTempDir(conn ssh.Connection, host *kkv1alpha1.HostCfg) error {
// scp to tmp file
- dir := getCopyDefaultDir(host.User)
+ dir := api.GetUserTempDir(host.User)
var sb strings.Builder
sb.WriteString("sudo -E /bin/sh -c \"")
sb.WriteString(fmt.Sprintf("mkdir -p %s", dir))
@@ -191,18 +190,11 @@ func prepareUserTempDir(conn ssh.Connection, host *kkv1alpha1.HostCfg) error {
return nil
}
-func getCopyDefaultDir(user string) string {
- if user == "root" {
- return constants.DefaultRootCopyTempDirHome + "/temp"
- }
- return fmt.Sprintf(constants.DefaultUserCopyTempHomeFormat, user) + "/temp"
-}
-
func (ssh *SSHRunner) copyFile(src, dst string) error {
if ssh.Conn == nil {
return fmt.Errorf("[%s] SSH runner is not connected", ssh.Host.Name)
}
- tempDir := getCopyDefaultDir(ssh.Host.User)
+ tempDir := api.GetUserTempDir(ssh.Host.User)
// scp to tmp file
tempCpyFile := filepath.Join(tempDir, filepath.Base(src))
err := ssh.Conn.Scp(src, tempCpyFile)
@@ -248,7 +240,7 @@ func (ssh *SSHRunner) copyDir(srcDir, dstDir string) error {
logrus.Errorf("[%s] create cert tmp tar failed: %v", ssh.Host.Name, err)
return err
}
- tmpCpyDir := getCopyDefaultDir(ssh.Host.User)
+ tmpCpyDir := api.GetUserTempDir(ssh.Host.User)
tmpPkiFile := filepath.Join(tmpCpyDir, "pkg.tar")
// scp to user home directory
err = ssh.Copy(tmpPkgFile, tmpPkiFile)
--
2.25.1