...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/jvm_server.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package chaosdaemon
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"strings"
    24  
    25  	"github.com/golang/protobuf/ptypes/empty"
    26  
    27  	"github.com/chaos-mesh/chaos-mesh/pkg/bpm"
    28  	pb "github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/pb"
    29  )
    30  
    31  const (
    32  	bmInstallCommand = "bminstall.sh -b -Dorg.jboss.byteman.transform.all -Dorg.jboss.byteman.verbose -p %d %d"
    33  	bmSubmitCommand  = "bmsubmit.sh -p %d -%s %s"
    34  )
    35  
    36  func (s *DaemonServer) InstallJVMRules(ctx context.Context,
    37  	req *pb.InstallJVMRulesRequest) (*empty.Empty, error) {
    38  	log := s.getLoggerFromContext(ctx)
    39  	log.Info("InstallJVMRules", "request", req)
    40  	pid, err := s.crClient.GetPidFromContainerID(ctx, req.ContainerId)
    41  	if err != nil {
    42  		log.Error(err, "GetPidFromContainerID")
    43  		return nil, err
    44  	}
    45  
    46  	bytemanHome := os.Getenv("BYTEMAN_HOME")
    47  	if len(bytemanHome) == 0 {
    48  		return nil, fmt.Errorf("environment variable BYTEMAN_HOME not set")
    49  	}
    50  
    51  	// copy agent.jar to container's namespace
    52  	if req.EnterNS {
    53  		processBuilder := bpm.DefaultProcessBuilder("sh", "-c", fmt.Sprintf("mkdir -p %s/lib/", bytemanHome)).SetContext(ctx).SetNS(pid, bpm.MountNS)
    54  		output, err := processBuilder.Build(ctx).CombinedOutput()
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  		if len(output) > 0 {
    59  			log.Info("mkdir", "output", string(output))
    60  		}
    61  
    62  		agentFile, err := os.Open(fmt.Sprintf("%s/lib/byteman.jar", bytemanHome))
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  		processBuilder = bpm.DefaultProcessBuilder("sh", "-c", "cat > /usr/local/byteman/lib/byteman.jar").SetContext(ctx)
    67  		processBuilder = processBuilder.SetNS(pid, bpm.MountNS).SetStdin(agentFile)
    68  		output, err = processBuilder.Build(ctx).CombinedOutput()
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  		if len(output) > 0 {
    73  			log.Info("copy agent.jar", "output", string(output))
    74  		}
    75  	}
    76  
    77  	bmInstallCmd := fmt.Sprintf(bmInstallCommand, req.Port, pid)
    78  	processBuilder := bpm.DefaultProcessBuilder("sh", "-c", bmInstallCmd).SetContext(ctx)
    79  	if req.EnterNS {
    80  		processBuilder = processBuilder.EnableLocalMnt()
    81  	}
    82  
    83  	cmd := processBuilder.Build(ctx)
    84  	output, err := cmd.CombinedOutput()
    85  	if err != nil {
    86  		// this error will occured when install agent more than once, and will ignore this error and continue to submit rule
    87  		errMsg1 := "Agent JAR loaded but agent failed to initialize"
    88  
    89  		// these two errors will occured when java version less or euqal to 1.8, and don't know why
    90  		// but it can install agent success even with this error, so just ignore it now.
    91  		// TODO: Investigate the cause of these two error
    92  		errMsg2 := "Provider sun.tools.attach.LinuxAttachProvider not found"
    93  		errMsg3 := "install java.io.IOException: Non-numeric value found"
    94  
    95  		// this error is caused by the different attach result codes in different java versions. In fact, the agent has attached success, just ignore it here.
    96  		// refer to https://stackoverflow.com/questions/54340438/virtualmachine-attach-throws-com-sun-tools-attach-agentloadexception-0-when-usi/54454418#54454418
    97  		errMsg4 := "install com.sun.tools.attach.AgentLoadException"
    98  		if !strings.Contains(string(output), errMsg1) && !strings.Contains(string(output), errMsg2) &&
    99  			!strings.Contains(string(output), errMsg3) && !strings.Contains(string(output), errMsg4) {
   100  			log.Error(err, string(output))
   101  			return nil, err
   102  		}
   103  		log.Info("exec comamnd", "cmd", cmd.String(), "output", string(output), "error", err.Error())
   104  	}
   105  
   106  	// submit rules
   107  	filename, err := writeDataIntoFile(req.Rule, "rule.btm")
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	bmSubmitCmd := fmt.Sprintf(bmSubmitCommand, req.Port, "l", filename)
   113  	processBuilder = bpm.DefaultProcessBuilder("sh", "-c", bmSubmitCmd).SetContext(ctx)
   114  	if req.EnterNS {
   115  		processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
   116  	}
   117  	output, err = processBuilder.Build(ctx).CombinedOutput()
   118  	if err != nil {
   119  		log.Error(err, string(output))
   120  		return nil, err
   121  	}
   122  	if len(output) > 0 {
   123  		log.Info("submit rules", "output", string(output))
   124  	}
   125  
   126  	return &empty.Empty{}, nil
   127  }
   128  
   129  func (s *DaemonServer) UninstallJVMRules(ctx context.Context,
   130  	req *pb.UninstallJVMRulesRequest) (*empty.Empty, error) {
   131  	log := s.getLoggerFromContext(ctx)
   132  	log.Info("InstallJVMRules", "request", req)
   133  	pid, err := s.crClient.GetPidFromContainerID(ctx, req.ContainerId)
   134  	if err != nil {
   135  		log.Error(err, "GetPidFromContainerID")
   136  		return nil, err
   137  	}
   138  
   139  	filename, err := writeDataIntoFile(req.Rule, "rule.btm")
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	log.Info("create btm file", "file", filename)
   144  
   145  	bmSubmitCmd := fmt.Sprintf(bmSubmitCommand, req.Port, "u", filename)
   146  	processBuilder := bpm.DefaultProcessBuilder("sh", "-c", bmSubmitCmd).SetContext(ctx)
   147  	if req.EnterNS {
   148  		processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
   149  	}
   150  	output, err := processBuilder.Build(ctx).CombinedOutput()
   151  	if err != nil {
   152  		log.Error(err, string(output))
   153  		if strings.Contains(string(output), "No rule scripts to remove") {
   154  			return &empty.Empty{}, nil
   155  		}
   156  		return nil, err
   157  	}
   158  
   159  	if len(output) > 0 {
   160  		log.Info(string(output))
   161  	}
   162  
   163  	return &empty.Empty{}, nil
   164  }
   165  
   166  func writeDataIntoFile(data string, filename string) (string, error) {
   167  	tmpfile, err := ioutil.TempFile("", filename)
   168  	if err != nil {
   169  		return "", err
   170  	}
   171  
   172  	if _, err := tmpfile.WriteString(data); err != nil {
   173  		return "", err
   174  	}
   175  
   176  	if err := tmpfile.Close(); err != nil {
   177  		return "", err
   178  	}
   179  
   180  	return tmpfile.Name(), err
   181  }
   182