// StartBackgroundActions spawns goroutines that monitor various parts of the // execution - heartbeats, timeouts, logging, etc. func (agt *Agent) StartBackgroundActions(signalHandler TerminateHandler) chan FinalTaskFunc { completed := make(chan FinalTaskFunc) agt.heartbeater.StartHeartbeating() agt.statsCollector.LogStats(agt.taskConfig.Expansions) agt.timeoutWatcher.NotifyTimeouts(agt.signalChan) go signalHandler.HandleSignals(agt, completed) // listen for SIGQUIT and dump a stack trace to system logs if received. go util.DumpStackOnSIGQUIT(evergreen.NewInfoLoggingWriter(agt.logger.System)) return completed }
func (sc *StatsCollector) LogStats(exp *command.Expansions) { sc.expandCommands(exp) if sc.Interval < 0 { panic(fmt.Sprintf("Illegal interval: %v", sc.Interval)) } if sc.stop != nil { panic("StatsCollector goroutine already running!") } if sc.Interval == 0 { sc.Interval = 60 * time.Second } sysloggerInfoWriter := evergreen.NewInfoLoggingWriter(sc.logger) sysloggerErrWriter := evergreen.NewErrorLoggingWriter(sc.logger) sc.stop = make(chan bool) go func() { ticker := time.NewTicker(sc.Interval) for { select { case <-ticker.C: for _, cmd := range sc.Cmds { sc.logger.Logf(slogger.INFO, "Running %v", cmd) command := &command.LocalCommand{ CmdString: cmd, Stdout: sysloggerInfoWriter, Stderr: sysloggerErrWriter, } if err := command.Run(); err != nil { sc.logger.Logf(slogger.ERROR, "error running '%v': %v", cmd, err) } } case <-sc.stop: sc.logger.Logf(slogger.INFO, "StatsCollector ticker stopping.") ticker.Stop() sc.stop = nil return } } }() }
// constructPwdUpdateCommand returns a RemoteCommand struct used to // set the RDP password on a remote windows machine. func constructPwdUpdateCommand(settings *evergreen.Settings, hostObj *host.Host, password string) (*command.RemoteCommand, error) { cloudHost, err := providers.GetCloudHost(hostObj, settings) if err != nil { return nil, err } hostInfo, err := util.ParseSSHInfo(hostObj.Host) if err != nil { return nil, err } sshOptions, err := cloudHost.GetSSHOptions() if err != nil { return nil, err } outputLineHandler := evergreen.NewInfoLoggingWriter(&evergreen.Logger) errorLineHandler := evergreen.NewErrorLoggingWriter(&evergreen.Logger) updatePwdCmd := fmt.Sprintf("net user %v %v && sc config "+ "sshd obj= '.\\%v' password= \"%v\"", hostObj.User, password, hostObj.User, password) // construct the required termination command remoteCommand := &command.RemoteCommand{ CmdString: updatePwdCmd, Stdout: outputLineHandler, Stderr: errorLineHandler, LoggingDisabled: true, RemoteHostName: hostInfo.Hostname, User: hostObj.User, Options: append([]string{"-p", hostInfo.Port}, sshOptions...), Background: false, } return remoteCommand, nil }
func (sc *StatsCollector) LogStats(exp *command.Expansions) { sc.expandCommands(exp) if sc.Interval < 0 { panic(fmt.Sprintf("Illegal interval: %v", sc.Interval)) } if sc.Interval == 0 { sc.Interval = 60 * time.Second } sysloggerInfoWriter := evergreen.NewInfoLoggingWriter(sc.logger) sysloggerErrWriter := evergreen.NewErrorLoggingWriter(sc.logger) go func() { for { select { case <-sc.stop: sc.logger.Logf(slogger.INFO, "StatsCollector ticker stopping.") return default: for _, cmd := range sc.Cmds { sc.logger.Logf(slogger.INFO, "Running %v", cmd) command := &command.LocalCommand{ CmdString: cmd, Stdout: sysloggerInfoWriter, Stderr: sysloggerErrWriter, } if err := command.Run(); err != nil { sc.logger.Logf(slogger.ERROR, "error running '%v': %v", cmd, err) } } time.Sleep(sc.Interval) } } }() }
// MakePatchedConfig takes in the path to a remote configuration a stringified version // of the current project and returns an unmarshalled version of the project // with the patch applied func MakePatchedConfig(p *patch.Patch, remoteConfigPath, projectConfig string) ( *Project, error) { for _, patchPart := range p.Patches { // we only need to patch the main project and not any other modules if patchPart.ModuleName != "" { continue } // write patch file patchFilePath, err := util.WriteToTempFile(patchPart.PatchSet.Patch) if err != nil { return nil, fmt.Errorf("could not write patch file: %v", err) } defer os.Remove(patchFilePath) // write project configuration configFilePath, err := util.WriteToTempFile(projectConfig) if err != nil { return nil, fmt.Errorf("could not write config file: %v", err) } defer os.Remove(configFilePath) // clean the working directory workingDirectory := filepath.Dir(patchFilePath) localConfigPath := filepath.Join( workingDirectory, remoteConfigPath, ) parentDir := strings.Split( remoteConfigPath, string(os.PathSeparator), )[0] err = os.RemoveAll(filepath.Join(workingDirectory, parentDir)) if err != nil { return nil, err } if err = os.MkdirAll(filepath.Dir(localConfigPath), 0755); err != nil { return nil, err } // rename the temporary config file name to the remote config // file path if we are patching an existing remote config if len(projectConfig) > 0 { if err = os.Rename(configFilePath, localConfigPath); err != nil { return nil, fmt.Errorf("could not rename file '%v' to '%v': %v", configFilePath, localConfigPath, err) } defer os.Remove(localConfigPath) } // selectively apply the patch to the config file patchCommandStrings := []string{ fmt.Sprintf("set -o verbose"), fmt.Sprintf("set -o errexit"), fmt.Sprintf("git apply --whitespace=fix --include=%v < '%v'", remoteConfigPath, patchFilePath), } patchCmd := &command.LocalCommand{ CmdString: strings.Join(patchCommandStrings, "\n"), WorkingDirectory: workingDirectory, Stdout: evergreen.NewInfoLoggingWriter(&evergreen.Logger), Stderr: evergreen.NewErrorLoggingWriter(&evergreen.Logger), ScriptMode: true, } if err = patchCmd.Run(); err != nil { return nil, fmt.Errorf("could not run patch command: %v", err) } // read in the patched config file data, err := ioutil.ReadFile(localConfigPath) if err != nil { return nil, fmt.Errorf("could not read patched config file: %v", err) } project := &Project{} if err = LoadProjectInto(data, p.Project, project); err != nil { return nil, err } return project, nil } return nil, fmt.Errorf("no patch on project") }
func dumpToLogs(task, command string, stack []byte, agt *Agent) { if agt != nil && agt.logger != nil { logWriter := evergreen.NewInfoLoggingWriter(agt.logger.System) dumpDebugInfo(task, command, stack, logWriter) } }