cmd/xray-migration/xray-migration.go (180 lines of code) (raw):

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT package main import ( "flag" "fmt" "os" "os/exec" "path/filepath" "strconv" "syscall" "time" "github.com/aws/amazon-cloudwatch-agent/tool/util" "github.com/aws/amazon-cloudwatch-agent/tool/xraydaemonmigration" ) func main() { //downtime flag so user can configure their downtime before calling the migration tool. downTime := flag.Int("downTime", 5, "Set Daemon restart delay on CloudWatch failure. Note: trace data in this interval might be lost") flag.Parse() fmt.Printf("If the Cloudwatch agent doesn’t start within %v seconds, the X-ray daemon will restart. While the CloudWatch agent is starting, trace data generated during that time is lost.\n", *downTime) var userInput string fmt.Println("Enter 0 to cancel and exit, or enter a different number of seconds to wait for the CloudWatch agent to start (minimum of 2 seconds). Any trace data generated while waiting for the CloudWatch agent to start is lost.") fmt.Scanln(&userInput) var timeOut int var err error //assigning timeout duration if userInput == "" { //make sure downtime is at least 2 seconds if *downTime < 2 { timeOut = 2 } else { timeOut = *downTime } } else { timeOut, err = strconv.Atoi(userInput) if err != nil { fmt.Println("Input given is not a number", err) os.Exit(1) } if timeOut == 0 { os.Exit(1) } } processes, _ := xraydaemonmigration.FindAllDaemons() //Can not migrate if Daemon is not running if len(processes) == 0 { fmt.Println("X-Ray Daemon is not running") return } //Before running wizard need to know if we need to use the user current configuration fetchOrAppend := configExists(defaultConfigLocation) err = RunWizard(fetchOrAppend) if err != nil { fmt.Println("There was a problem trying to run config wizard", err) os.Exit(1) } //Save Daemon Information before shutting down for restart Daemon function argList, _ := processes[0].CmdlineSlice() cwd, _ := processes[0].Cwd() isService := checkXrayStatus() err = TerminateXray(processes[0], checkXrayStatus) if err != nil { fmt.Println("There was a problem terminating X-Ray Daemon: ", err) os.Exit(1) } //Call Fetch or Append config depending on if user already has Daemon configuration if fetchOrAppend == Fetch { err = FetchConfig() } else { err = AppendConfig() } //need to restart Daemon if Fetch/Append does not work or CWA does not start within the timeout duration if err != nil || !IsCWAOn(time.Duration(timeOut)*time.Second, checkCWAStatus) { fmt.Println("There was a problem starting the Cloudwatch Agent. Restarting X-Ray Daemon") err := restartDaemon(argList[0], argList[1:], cwd, isService) if err != nil { fmt.Println("Could not restart X-Ray Daemon: ", err) return } else { fmt.Println("X-Ray Daemon has been restarted") return } } else { fmt.Println("Cloudwatch Agent has started and it is running traces!") } } // CmdInterface defines the methods we need from exec.Cmd. type CmdInterface interface { Start() error Wait() error SetDir(string) SetSysProcAttr(*syscall.SysProcAttr) SetStdout(*os.File) SetStderr(*os.File) } type CmdWrapper struct { *exec.Cmd } func (cw *CmdWrapper) Start() error { return cw.Cmd.Start() } func (cw *CmdWrapper) Wait() error { return cw.Cmd.Wait() } func (cw *CmdWrapper) SetDir(dir string) { cw.Cmd.Dir = dir } func (cw *CmdWrapper) SetSysProcAttr(attr *syscall.SysProcAttr) { cw.Cmd.SysProcAttr = attr } func (cw *CmdWrapper) SetStdout(out *os.File) { cw.Cmd.Stdout = out } func (cw *CmdWrapper) SetStderr(err *os.File) { cw.Cmd.Stderr = err } var execCommand = func(name string, args ...string) CmdInterface { cmd := exec.Command(name, args...) return &CmdWrapper{ Cmd: cmd, } } func RunWizard(fetchOrAppend startType) error { var err error var cmd *exec.Cmd if fetchOrAppend == Append { //need to save generated traces file in a different file cmd = exec.Command(pathToWizard, "-tracesOnly", "-configOutputPath", filepath.Join(pathToWizardDir, "config-traces.json"), "-nonInteractiveXrayMigration", "true") } else { cmd = exec.Command(pathToWizard, "-tracesOnly", "-nonInteractiveXrayMigration", "true") } cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() return err } type startType int64 const ( Fetch startType = 0 Append startType = 1 ) func configExists(configLocation string) startType { _, err := os.Stat(configLocation) //if file does not exist we just need to make traces file and fetch if os.IsNotExist(err) { return Fetch } //need to append current config with generated traces return Append } func TerminateXray(process xraydaemonmigration.Process, checkXrayStatus func() bool) error { var err error if checkXrayStatus() { err = StopXrayService() } else { err = process.Terminate() } return err } func IsCWAOn(timeout time.Duration, checkCWAStatus func() bool) bool { startTime := time.Now() //Check for duration of timeout for { if time.Since(startTime) > timeout { return false } if checkCWAStatus() { return true } time.Sleep(time.Second) } } func restartDaemon(daemonPath string, daemonArgs []string, cwd string, isService bool) error { var err error if isService { var cmd CmdInterface curOs := util.CurOS() if curOs == "windows" { cmd = execCommand("net", "start", "XRay") } else { cmd = execCommand("sudo", "systemctl", "start", "xray") } cmd.SetSysProcAttr(newSysProcAttr()) cmd.SetStdout(os.Stdout) cmd.SetStderr(os.Stderr) err := cmd.Start() return err } cmd := execCommand(daemonPath, daemonArgs...) cmd.SetDir(cwd) cmd.SetSysProcAttr(newSysProcAttr()) cmd.SetStdout(os.Stdout) err = cmd.Start() return err }