Example #1
0
// Execute starts the shell with its given parameters.
func (sec *ShellExecCommand) Execute(pluginLogger plugin.Logger,
	pluginCom plugin.PluginCommunicator,
	conf *model.TaskConfig,
	stop chan bool) error {
	pluginLogger.LogExecution(slogger.DEBUG, "Preparing script...")

	logWriterInfo := pluginLogger.GetTaskLogWriter(slogger.INFO)
	logWriterErr := pluginLogger.GetTaskLogWriter(slogger.ERROR)
	if sec.SystemLog {
		logWriterInfo = pluginLogger.GetSystemLogWriter(slogger.INFO)
		logWriterErr = pluginLogger.GetSystemLogWriter(slogger.ERROR)
	}

	outBufferWriter := util.NewLineBufferingWriter(logWriterInfo)
	errorBufferWriter := util.NewLineBufferingWriter(logWriterErr)
	defer outBufferWriter.Flush()
	defer errorBufferWriter.Flush()

	localCmd := &command.LocalCommand{
		CmdString:  sec.Script,
		Stdout:     outBufferWriter,
		Stderr:     errorBufferWriter,
		ScriptMode: true,
	}

	if sec.WorkingDir != "" {
		localCmd.WorkingDirectory = filepath.Join(conf.WorkDir, sec.WorkingDir)
	} else {
		localCmd.WorkingDirectory = conf.WorkDir
	}

	if sec.Shell != "" {
		localCmd.Shell = sec.Shell
	}

	err := localCmd.PrepToRun(conf.Expansions)
	if err != nil {
		return fmt.Errorf("Failed to apply expansions: %v", err)
	}
	if sec.Silent {
		pluginLogger.LogExecution(slogger.INFO, "Executing script with %s (source hidden)...",
			localCmd.Shell)
	} else {
		pluginLogger.LogExecution(slogger.INFO, "Executing script with %s: %v",
			localCmd.Shell, localCmd.CmdString)
	}

	doneStatus := make(chan error)
	go func() {
		var err error
		env := os.Environ()
		env = append(env, fmt.Sprintf("EVR_TASK_ID=%v", conf.Task.Id))
		env = append(env, fmt.Sprintf("EVR_AGENT_PID=%v", os.Getpid()))
		localCmd.Environment = env
		err = localCmd.Start()
		if err == nil {
			pluginLogger.LogSystem(slogger.DEBUG, "spawned shell process with pid %v", localCmd.Cmd.Process.Pid)

			// Call the platform's process-tracking function. On some OSes this will be a noop,
			// on others this may need to do some additional work to track the process so that
			// it can be cleaned up later.
			projTask := conf.Project.FindProjectTask(conf.Task.DisplayName)
			if conf.Project.DisableCleanup || (projTask != nil && projTask.DisableCleanup) {
				pluginLogger.LogSystem(slogger.DEBUG, "skipping process tracking for pid %v", localCmd.Cmd.Process.Pid)
			} else {
				trackProcess(conf.Task.Id, localCmd.Cmd.Process.Pid, pluginLogger)
			}

			if !sec.Background {
				err = localCmd.Cmd.Wait()
			}
		} else {
			pluginLogger.LogSystem(slogger.DEBUG, "error spawning shell process: %v", err)
		}
		doneStatus <- err
	}()

	defer pluginLogger.Flush()
	select {
	case err = <-doneStatus:
		if err != nil {
			if sec.ContinueOnError {
				pluginLogger.LogExecution(slogger.INFO, "(ignoring) Script finished with error: %v", err)
				return nil
			} else {
				pluginLogger.LogExecution(slogger.INFO, "Script finished with error: %v", err)
				return err
			}
		} else {
			pluginLogger.LogExecution(slogger.INFO, "Script execution complete.")
		}
	case <-stop:
		pluginLogger.LogExecution(slogger.INFO, "Got kill signal")

		// need to check command has started
		if localCmd.Cmd != nil {
			pluginLogger.LogExecution(slogger.INFO, "Stopping process: %v", localCmd.Cmd.Process.Pid)

			// try and stop the process
			if err := localCmd.Stop(); err != nil {
				pluginLogger.LogExecution(slogger.ERROR, "Error occurred stopping process: %v", err)
			}
		}

		return fmt.Errorf("Shell command interrupted.")
	}

	return nil
}