// New creates a new agent to run a given task. func New(apiServerURL, taskId, taskSecret, logFile, cert, pidFilePath string) (*Agent, error) { sh := &SignalHandler{} sh.makeChannels() // set up communicator with API server httpCommunicator, err := comm.NewHTTPCommunicator(apiServerURL, taskId, taskSecret, cert, sh.communicatorChan) if err != nil { return nil, err } // set up logger to API server apiLogger := comm.NewAPILogger(httpCommunicator) idleTimeoutWatcher := comm.NewTimeoutWatcher(sh.stopBackgroundChan) idleTimeoutWatcher.SetDuration(DefaultIdleTimeout) // set up timeout logger, local and API logger streams streamLogger, err := comm.NewStreamLogger(idleTimeoutWatcher, apiLogger, logFile) if err != nil { return nil, err } httpCommunicator.Logger = streamLogger.Execution // set up the heartbeat ticker hbTicker := comm.NewHeartbeatTicker(sh.stopBackgroundChan) hbTicker.MaxFailedHeartbeats = 10 hbTicker.SignalChan = sh.heartbeatChan hbTicker.TaskCommunicator = httpCommunicator hbTicker.Logger = httpCommunicator.Logger hbTicker.Interval = DefaultHeartbeatInterval // set up the system stats collector statsCollector := NewSimpleStatsCollector( streamLogger.System, DefaultStatsInterval, sh.stopBackgroundChan, "df -h", "${ps|ps}", ) agt := &Agent{ signalHandler: sh, logger: streamLogger, TaskCommunicator: httpCommunicator, heartbeater: hbTicker, statsCollector: statsCollector, idleTimeoutWatcher: idleTimeoutWatcher, APILogger: apiLogger, Registry: plugin.NewSimpleRegistry(), KillChan: make(chan bool), endChan: make(chan *apimodels.TaskEndDetail, 1), pidFilePath: pidFilePath, } return agt, nil }
// RunTask manages the process of running a task. It returns a response // indicating the end result of the task. func (agt *Agent) RunTask() (*apimodels.TaskEndResponse, error) { agt.CheckIn(InitialSetupCommand, InitialSetupTimeout) agt.logger.LogLocal(slogger.INFO, "Local logger initialized.") agt.logger.LogTask(slogger.INFO, "Task logger initialized (agent revision: %v)", evergreen.BuildRevision) agt.logger.LogExecution(slogger.INFO, "Execution logger initialized.") agt.logger.LogSystem(slogger.INFO, "System logger initialized.") httpAgentComm, ok := agt.TaskCommunicator.(*comm.HTTPCommunicator) if ok && len(httpAgentComm.HttpsCert) == 0 { agt.logger.LogTask(slogger.WARN, "Running agent without a https certificate.") } taskConfig, err := agt.GetTaskConfig() if err != nil { agt.logger.LogExecution(slogger.ERROR, "Error fetching task configuration: %v", err) return nil, err } agt.logger.LogTask(slogger.INFO, "Starting task %v, execution %v.", taskConfig.Task.Id, taskConfig.Task.Execution) pt := taskConfig.Project.FindProjectTask(taskConfig.Task.DisplayName) if pt.ExecTimeoutSecs == 0 { // if unspecified in the project task and the project, use the default value if taskConfig.Project.ExecTimeoutSecs != 0 { pt.ExecTimeoutSecs = taskConfig.Project.ExecTimeoutSecs } else { pt.ExecTimeoutSecs = DefaultExecTimeoutSecs } } execTimeout := time.Duration(pt.ExecTimeoutSecs) * time.Second // Set master task timeout, only if included in the taskConfig if execTimeout != 0 { agt.maxExecTimeoutWatcher = comm.NewTimeoutWatcher( agt.signalHandler.stopBackgroundChan) agt.maxExecTimeoutWatcher.SetDuration(execTimeout) } agt.logger.LogExecution(slogger.INFO, "Fetching expansions for project %v...", taskConfig.Task.Project) expVars, err := agt.FetchExpansionVars() if err != nil { agt.logger.LogExecution(slogger.ERROR, "error fetching project expansion variables: %v", err) return nil, err } taskConfig.Expansions.Update(*expVars) agt.taskConfig = taskConfig // start the heartbeater, timeout watcher, system stats collector, and signal listener agt.StartBackgroundActions(agt.signalHandler) err = agt.createTaskDirectory(taskConfig) if err != nil { agt.signalHandler.directoryChan <- comm.DirectoryFailure return nil, err } taskConfig.Expansions.Put("workdir", taskConfig.WorkDir) // register plugins needed for execution if err = registerPlugins(agt.Registry, plugin.CommandPlugins, agt.logger); err != nil { agt.logger.LogExecution(slogger.ERROR, "error initializing agent plugins: %v", err) return agt.finishAndAwaitCleanup(evergreen.TaskFailed) } // notify API server that the task has been started. agt.logger.LogExecution(slogger.INFO, "Reporting task started.") if err = agt.Start(strconv.Itoa(os.Getpid())); err != nil { agt.logger.LogExecution(slogger.ERROR, "error marking task started: %v", err) return agt.finishAndAwaitCleanup(evergreen.TaskFailed) } if agt.taskConfig.Project.Pre != nil { agt.logger.LogExecution(slogger.INFO, "Running pre-task commands.") err = agt.RunCommands(agt.taskConfig.Project.Pre.List(), false, agt.callbackTimeoutSignal()) if err != nil { agt.logger.LogExecution(slogger.ERROR, "Running pre-task script failed: %v", err) } agt.logger.LogExecution(slogger.INFO, "Finished running pre-task commands.") } return agt.RunTaskCommands() }