From b71e61f898351dbf0cde344abc01b015e4cdd7bb Mon Sep 17 00:00:00 2001 From: KingsonKai <562419972@qq.com> Date: Wed, 27 Sep 2023 17:59:49 +0800 Subject: [PATCH] fix: jvm inject in container; feat: support set env from target process --- .../templates/chaosmeta-daemon-template.yaml | 2 +- .../templates/chaosmeta-inject-template.yaml | 4 +- chaosmeta-inject-operator/Makefile | 2 +- .../build/yamls/chaosmeta-daemonset.yaml | 2 +- .../build/yamls/chaosmeta-inject.yaml | 2 +- .../config/chaosmeta-inject.json | 2 +- .../config/manager/kustomization.yaml | 2 +- .../build/chaosmeta-daemonset.Dockerfile | 4 +- chaosmetad/build/chaosmetad-demo.Dockerfile | 9 ++-- chaosmetad/build/ci/build.sh | 2 +- chaosmetad/pkg/exec/execns/chaosmeta_execns.c | 45 ++++++++++++++++++- chaosmetad/pkg/injector/jvm/constant.go | 13 +++++- chaosmetad/pkg/injector/jvm/methoddelay.go | 4 ++ .../pkg/injector/jvm/methodexception.go | 4 ++ chaosmetad/pkg/injector/jvm/methodreturn.go | 4 ++ chaosmetad/pkg/utils/cmdexec/cmd.go | 11 +++-- chaosmetad/pkg/utils/namespace/namespace.go | 4 +- chaosmetad/pkg/utils/namespace/type.go | 1 + chaosmetad/test/main.go | 2 +- 19 files changed, 97 insertions(+), 22 deletions(-) diff --git a/chaosmeta-deploy/templates/chaosmeta-daemon-template.yaml b/chaosmeta-deploy/templates/chaosmeta-daemon-template.yaml index 37a8097..33215f4 100644 --- a/chaosmeta-deploy/templates/chaosmeta-daemon-template.yaml +++ b/chaosmeta-deploy/templates/chaosmeta-daemon-template.yaml @@ -19,7 +19,7 @@ spec: # chaos-role.chaosmeta.io: chaosmeta-daemon containers: - name: chaosmeta-daemon - image: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-daemon:v0.3.0 + image: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-daemon:v0.3.2 securityContext: privileged: true volumeMounts: diff --git a/chaosmeta-deploy/templates/chaosmeta-inject-template.yaml b/chaosmeta-deploy/templates/chaosmeta-inject-template.yaml index 0f39300..5b80f69 100644 --- a/chaosmeta-deploy/templates/chaosmeta-inject-template.yaml +++ b/chaosmeta-deploy/templates/chaosmeta-inject-template.yaml @@ -386,7 +386,7 @@ spec: - --leader-elect command: - /manager - image: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-inject-controller:v0.1.0 + image: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-inject-controller:v0.1.1 livenessProbe: httpGet: path: /healthz @@ -507,7 +507,7 @@ data: "executor": { "mode": "daemonset", "executor": "chaosmetad", - "version": "0.3.0", + "version": "0.3.2", "agentConfig": { "agentPort": 29595 }, diff --git a/chaosmeta-inject-operator/Makefile b/chaosmeta-inject-operator/Makefile index efb92c3..c5f6f09 100644 --- a/chaosmeta-inject-operator/Makefile +++ b/chaosmeta-inject-operator/Makefile @@ -1,6 +1,6 @@ # Image URL to use all building/pushing image targets -IMG ?= registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-inject-controller:v0.1.0 +IMG ?= registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-inject-controller:v0.1.1 # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.25.0 diff --git a/chaosmeta-inject-operator/build/yamls/chaosmeta-daemonset.yaml b/chaosmeta-inject-operator/build/yamls/chaosmeta-daemonset.yaml index b117507..5168292 100644 --- a/chaosmeta-inject-operator/build/yamls/chaosmeta-daemonset.yaml +++ b/chaosmeta-inject-operator/build/yamls/chaosmeta-daemonset.yaml @@ -19,7 +19,7 @@ spec: # chaos-role.chaosmeta.io: chaosmeta-daemon containers: - name: chaosmeta-daemon - image: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-daemon:v0.1.1 + image: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-daemon:v0.3.2 securityContext: privileged: true volumeMounts: diff --git a/chaosmeta-inject-operator/build/yamls/chaosmeta-inject.yaml b/chaosmeta-inject-operator/build/yamls/chaosmeta-inject.yaml index d0230d7..ea5c64c 100644 --- a/chaosmeta-inject-operator/build/yamls/chaosmeta-inject.yaml +++ b/chaosmeta-inject-operator/build/yamls/chaosmeta-inject.yaml @@ -396,7 +396,7 @@ spec: - --leader-elect command: - /manager - image: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-inject-controller:v0.1.0 + image: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-inject-controller:v0.1.1 livenessProbe: httpGet: path: /healthz diff --git a/chaosmeta-inject-operator/config/chaosmeta-inject.json b/chaosmeta-inject-operator/config/chaosmeta-inject.json index 6b1e903..97a3f90 100644 --- a/chaosmeta-inject-operator/config/chaosmeta-inject.json +++ b/chaosmeta-inject-operator/config/chaosmeta-inject.json @@ -8,7 +8,7 @@ "executor": { "mode": "daemonset", "executor": "chaosmetad", - "version": "0.3.0", + "version": "0.3.2", "agentConfig": { "agentPort": 29595 }, diff --git a/chaosmeta-inject-operator/config/manager/kustomization.yaml b/chaosmeta-inject-operator/config/manager/kustomization.yaml index 29d3d02..fc53f8a 100644 --- a/chaosmeta-inject-operator/config/manager/kustomization.yaml +++ b/chaosmeta-inject-operator/config/manager/kustomization.yaml @@ -5,4 +5,4 @@ kind: Kustomization images: - name: controller newName: registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-inject-controller - newTag: v0.1.0 + newTag: v0.1.1 diff --git a/chaosmetad/build/chaosmeta-daemonset.Dockerfile b/chaosmetad/build/chaosmeta-daemonset.Dockerfile index 906c6a0..db4afca 100644 --- a/chaosmetad/build/chaosmeta-daemonset.Dockerfile +++ b/chaosmetad/build/chaosmeta-daemonset.Dockerfile @@ -1,5 +1,5 @@ -# docker build -t registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-daemon:v0.3.0 -f chaosmeta-daemonset.Dockerfile . +# docker build -t registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmeta-daemon:v0.3.2 -f chaosmeta-daemonset.Dockerfile . From centos:centos7 -ENV CHAOSMETAD_VERSION=0.3.0 +ENV CHAOSMETAD_VERSION=0.3.2 ADD ./chaosmetad-$CHAOSMETAD_VERSION.tar.gz /opt/chaosmeta CMD while true; do if [ ! -d "/tmp/chaosmetad-$CHAOSMETAD_VERSION" ]; then cp -r /opt/chaosmeta/chaosmetad-$CHAOSMETAD_VERSION /tmp/chaosmetad-$CHAOSMETAD_VERSION; fi; sleep 600; done diff --git a/chaosmetad/build/chaosmetad-demo.Dockerfile b/chaosmetad/build/chaosmetad-demo.Dockerfile index 62d67fd..8092481 100644 --- a/chaosmetad/build/chaosmetad-demo.Dockerfile +++ b/chaosmetad/build/chaosmetad-demo.Dockerfile @@ -1,7 +1,10 @@ -# docker build -t registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmetad-demo:v0.3.0 -f chaosmetad-demo.Dockerfile . +# docker build -t registry.cn-hangzhou.aliyuncs.com/chaosmeta/chaosmetad-demo:v0.3.2 -f chaosmetad-demo.Dockerfile . From centos:centos7 ADD ./jdk-8u361-linux-x64.tar.gz /usr/local RUN yum install -y iproute && yum clean all -ENV CHAOSMETAD_VERSION=0.3.0 +ENV CHAOSMETAD_VERSION=0.3.2 \ + JAVA_HOME=/usr/local/jdk1.8.0_361 \ + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/chaosmeta/chaosmetad-0.3.2:/usr/local/jdk1.8.0_361/bin + ADD ./chaosmetad-$CHAOSMETAD_VERSION.tar.gz /opt/chaosmeta -RUN echo 'export JAVA_HOME=/usr/local/jdk1.8.0_361' >> /etc/profile && echo 'export PATH=$PATH:$JAVA_HOME/bin' >> /etc/profile && echo 'export PATH=$PATH:/opt/chaosmeta/chaosmetad-'${CHAOSMETAD_VERSION} >> /etc/profile +#RUN echo 'export JAVA_HOME=/usr/local/jdk1.8.0_361' >> /etc/profile && echo 'export PATH=$PATH:$JAVA_HOME/bin' >> /etc/profile && echo 'export PATH=$PATH:/opt/chaosmeta/chaosmetad-'${CHAOSMETAD_VERSION} >> /etc/profile diff --git a/chaosmetad/build/ci/build.sh b/chaosmetad/build/ci/build.sh index 2692f25..1cbc155 100644 --- a/chaosmetad/build/ci/build.sh +++ b/chaosmetad/build/ci/build.sh @@ -27,7 +27,7 @@ fi # base info BUILD_NAME="chaosmetad" -VERSION="0.3.0" +VERSION="0.3.2" BUILD_DATE=$(date "+%Y-%m-%d %H:%M:%S") # env var diff --git a/chaosmetad/pkg/exec/execns/chaosmeta_execns.c b/chaosmetad/pkg/exec/execns/chaosmeta_execns.c index 31d8b4d..0f3c186 100644 --- a/chaosmetad/pkg/exec/execns/chaosmeta_execns.c +++ b/chaosmetad/pkg/exec/execns/chaosmeta_execns.c @@ -25,6 +25,38 @@ #include #include +int set_env(int pid) { + char cmd[64]; + snprintf(cmd, sizeof(cmd), "cat /proc/%d/environ | tr '\\0' '\\t'", pid); + + FILE* pipe = popen(cmd, "r"); + if (pipe == NULL) { + fprintf(stderr, "Failed to execute command: %s\n", cmd); + return -1; + } + + char env_data[4096]; + fgets(env_data, sizeof(env_data), pipe); + pclose(pipe); + + char* tmp; + int length = strlen(env_data); + tmp = (char*)malloc(length + 1); + strcpy(tmp, env_data); + + char* env_var; + env_var = strtok(tmp, "\t"); + while (env_var != NULL) { + if (putenv(env_var) != 0) { + fprintf(stderr, "failed to set environment variable[%s].\n", env_var); + return -1; + } + env_var = strtok(NULL, "\t"); + } + + return 0; +} + int enter_ns(int pid, const char* type) { char path[64], selfpath[64]; snprintf(path, sizeof(path), "/proc/%d/ns/%s", pid, type); @@ -76,7 +108,8 @@ int main(int argc, char *argv[]) { int netns = 0; int pidns = 0; int mntns = 0; - char *string = "c:t:mpuni"; + int envns = 0; + char *string = "c:t:mpunie"; while((opt =getopt(argc, argv, string))!= -1) { switch (opt) { @@ -101,6 +134,9 @@ int main(int argc, char *argv[]) { case 'i': ipcns = 1; break; + case 'e': + envns = 1; + break; default: break; } @@ -116,6 +152,13 @@ int main(int argc, char *argv[]) { return 1; } + if(envns) { + int re = set_env(target); + if (re != 0) { + return re; + } + } + if(ipcns) { int re = enter_ns(target, "ipc"); if (re != 0) { diff --git a/chaosmetad/pkg/injector/jvm/constant.go b/chaosmetad/pkg/injector/jvm/constant.go index 8bdf7ca..778bbab 100644 --- a/chaosmetad/pkg/injector/jvm/constant.go +++ b/chaosmetad/pkg/injector/jvm/constant.go @@ -110,6 +110,17 @@ func writeRule(ctx context.Context, cId string, pid int, ruleBytes []byte) error return nil } +// TODO: It is currently impossible to determine accurately because the ExecContainer function does not completely determine whether it is successful based on the return code. +func checkJavaCmd(ctx context.Context, cr, cId string) error { + cmd := "java -help" + if cr != "" { + _, err := cmdexec.ExecContainer(ctx, cr, cId, []string{namespace.MNT, namespace.ENV}, cmd, cmdexec.ExecRun) + return err + } else { + return cmdexec.RunBashCmdWithoutOutput(ctx, cmd) + } +} + func doInject(ctx context.Context, cr, cId string, pidList []int, ruleBytes []byte) error { // create rule file for _, pid := range pidList { @@ -143,7 +154,7 @@ func doInject(ctx context.Context, cr, cId string, pidList []int, ruleBytes []by for _, pid := range pidList { cmd := fmt.Sprintf("cd %s && java -cp .:tools.jar %s %d %s %s", JVMContainerDir, AttacherTool, pid, fmt.Sprintf("%s/%s", JVMContainerDir, JVMAgentTool), getContainerRuleFile(pid)) - if _, err := cmdexec.ExecContainer(ctx, cr, cId, []string{namespace.MNT, namespace.PID, namespace.MNT}, cmd, cmdexec.ExecStart); err != nil { + if _, err := cmdexec.ExecContainer(ctx, cr, cId, []string{namespace.MNT, namespace.PID, namespace.IPC, namespace.ENV}, cmd, cmdexec.ExecStart); err != nil { return fmt.Errorf("execute fault for process[%d] error: %s", pid, err.Error()) } } diff --git a/chaosmetad/pkg/injector/jvm/methoddelay.go b/chaosmetad/pkg/injector/jvm/methoddelay.go index 3cfbe66..e922f18 100644 --- a/chaosmetad/pkg/injector/jvm/methoddelay.go +++ b/chaosmetad/pkg/injector/jvm/methoddelay.go @@ -91,6 +91,10 @@ func (i *MethodDelayInjector) Validator(ctx context.Context) error { return fmt.Errorf("\"method\" is invalid: %s", err.Error()) } + if err := checkJavaCmd(ctx, i.Info.ContainerRuntime, i.Info.ContainerId); err != nil { + return fmt.Errorf("check java exec error: %s", err.Error()) + } + return nil } diff --git a/chaosmetad/pkg/injector/jvm/methodexception.go b/chaosmetad/pkg/injector/jvm/methodexception.go index 596f38f..008dee6 100644 --- a/chaosmetad/pkg/injector/jvm/methodexception.go +++ b/chaosmetad/pkg/injector/jvm/methodexception.go @@ -90,6 +90,10 @@ func (i *MethodExceptionInjector) Validator(ctx context.Context) error { return fmt.Errorf("\"method\" is invalid: %s", err.Error()) } + if err := checkJavaCmd(ctx, i.Info.ContainerRuntime, i.Info.ContainerId); err != nil { + return fmt.Errorf("check java exec error: %s", err.Error()) + } + return nil } diff --git a/chaosmetad/pkg/injector/jvm/methodreturn.go b/chaosmetad/pkg/injector/jvm/methodreturn.go index 813b79e..a4b9096 100644 --- a/chaosmetad/pkg/injector/jvm/methodreturn.go +++ b/chaosmetad/pkg/injector/jvm/methodreturn.go @@ -90,6 +90,10 @@ func (i *MethodReturnInjector) Validator(ctx context.Context) error { return fmt.Errorf("\"method\" is invalid: %s", err.Error()) } + if err := checkJavaCmd(ctx, i.Info.ContainerRuntime, i.Info.ContainerId); err != nil { + return fmt.Errorf("check java exec error: %s", err.Error()) + } + return nil } diff --git a/chaosmetad/pkg/utils/cmdexec/cmd.go b/chaosmetad/pkg/utils/cmdexec/cmd.go index 6570697..5d0e6cb 100644 --- a/chaosmetad/pkg/utils/cmdexec/cmd.go +++ b/chaosmetad/pkg/utils/cmdexec/cmd.go @@ -186,16 +186,17 @@ func RunBashCmdWithOutput(ctx context.Context, cmd string) (string, error) { reByte, err := c.CombinedOutput() re := string(reByte) + log.GetLogger(ctx).Debugf("output: %s, err: %v", re, err) if err != nil { if c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() == errutil.ExpectedErr { - return "", fmt.Errorf(re) + return "", fmt.Errorf("output: %s, error: %s", re, err.Error()) } if c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() == errutil.TestFileErr { - return "", fmt.Errorf("exit code: %d, error: %s", errutil.TestFileErr, re) + return "", fmt.Errorf("exit code: %d, output: %s, error: %s", errutil.TestFileErr, re, err.Error()) } - return "", fmt.Errorf("%s, output: %s", err.Error(), re) + return "", fmt.Errorf("error: %s, output: %s", err.Error(), re) } return re, nil @@ -329,7 +330,9 @@ func ExecContainerRaw(ctx context.Context, cr, cId, cmd string) (string, error) } log.GetLogger(ctx).Debugf("container: %s, exec cmd: %s", cId, cmd) - return client.Exec(ctx, cId, cmd) + re, err := client.Exec(ctx, cId, cmd) + log.GetLogger(ctx).Debugf("container: %s, output: %s, err: %v", cId, re, err) + return re, err } func ExecCommon(ctx context.Context, cr, cId, cmd string) (string, error) { diff --git a/chaosmetad/pkg/utils/namespace/namespace.go b/chaosmetad/pkg/utils/namespace/namespace.go index bd30723..a881e9d 100644 --- a/chaosmetad/pkg/utils/namespace/namespace.go +++ b/chaosmetad/pkg/utils/namespace/namespace.go @@ -40,8 +40,10 @@ func GetNsOption(namespaces []string) string { nsOptionStr += " -n" case IPC: nsOptionStr += " -i" + case ENV: + nsOptionStr += " -e" } } return nsOptionStr -} \ No newline at end of file +} diff --git a/chaosmetad/pkg/utils/namespace/type.go b/chaosmetad/pkg/utils/namespace/type.go index af0a7a7..3ab614d 100644 --- a/chaosmetad/pkg/utils/namespace/type.go +++ b/chaosmetad/pkg/utils/namespace/type.go @@ -24,4 +24,5 @@ const ( NET = "net" PID = "pid" UTS = "uts" + ENV = "env" ) diff --git a/chaosmetad/test/main.go b/chaosmetad/test/main.go index 767ed77..d5d183f 100644 --- a/chaosmetad/test/main.go +++ b/chaosmetad/test/main.go @@ -82,7 +82,7 @@ func main() { path = filepath.Dir(path) pathArr := strings.Split(path, "/") rootPath := strings.Join(pathArr[:len(pathArr)-1], "/") - tool := fmt.Sprintf("%s/build/chaosmetad-0.3.0/chaosmetad", rootPath) + tool := fmt.Sprintf("%s/build/chaosmetad-0.3.2/chaosmetad", rootPath) fmt.Println(tool) var testCases = getTestCases()