Пример #1
0
// Execute runs multiple sets of commands and returns their outputs.
// res.Output will contain a slice of RunCommandPluginOutput.
func (p *Plugin) Execute(context context.T, config contracts.Configuration, cancelFlag task.CancelFlag) (res contracts.PluginResult) {
	log := context.Log()
	log.Info("RunCommand started with configuration ", config)
	util := new(updateutil.Utility)
	manager := new(updateManager)

	res.StartDateTime = time.Now()
	defer func() { res.EndDateTime = time.Now() }()

	//loading Properties as list since aws:updateSsmAgent uses properties as list
	var properties []interface{}
	if properties, res = pluginutil.LoadParametersAsList(log, config.Properties); res.Code != 0 {
		return res
	}

	out := make([]UpdatePluginOutput, len(properties))
	for i, prop := range properties {
		// check if a reboot has been requested
		if rebooter.RebootRequested() {
			log.Info("A plugin has requested a reboot.")
			break
		}

		if cancelFlag.ShutDown() {
			out[i] = UpdatePluginOutput{}
			out[i].Errors = []string{"Execution canceled due to ShutDown"}
			break
		} else if cancelFlag.Canceled() {
			out[i] = UpdatePluginOutput{}
			out[i].Errors = []string{"Execution canceled"}
			break
		}

		out[i] = updateAgent(p,
			config,
			log,
			manager,
			util,
			prop,
			cancelFlag,
			config.OutputS3BucketName,
			config.OutputS3KeyPrefix,
			res.StartDateTime)

		res.Code = out[i].ExitCode
		res.Status = out[i].Status
		res.Output = fmt.Sprintf("%v", out[i].String())
	}

	return
}
Пример #2
0
// Execute runs multiple sets of commands and returns their outputs.
// res.Output will contain a slice of RunCommandPluginOutput.
func (p *Plugin) Execute(context context.T, config contracts.Configuration, cancelFlag task.CancelFlag) (res contracts.PluginResult) {
	log := context.Log()
	log.Infof("%v started with configuration %v", Name(), config)
	res.StartDateTime = time.Now()
	defer func() { res.EndDateTime = time.Now() }()

	//loading Properties as list since aws:runPowershellScript & aws:runShellScript uses properties as list
	var properties []interface{}
	if properties, res = pluginutil.LoadParametersAsList(log, config.Properties); res.Code != 0 {

		pluginutil.PersistPluginInformationToCurrent(log, Name(), config, res)
		return res
	}

	out := make([]contracts.PluginOutput, len(properties))
	for i, prop := range properties {
		// check if a reboot has been requested
		if rebooter.RebootRequested() {
			log.Info("A plugin has requested a reboot.")
			return
		}

		if cancelFlag.ShutDown() {
			out[i] = contracts.PluginOutput{Errors: []string{"Execution canceled due to ShutDown"}}
			out[i].ExitCode = 1
			out[i].Status = contracts.ResultStatusFailed
			break
		}

		if cancelFlag.Canceled() {
			out[i] = contracts.PluginOutput{Errors: []string{"Execution canceled"}}
			out[i].ExitCode = 1
			out[i].Status = contracts.ResultStatusCancelled
			break
		}

		out[i] = p.runCommandsRawInput(log, prop, config.OrchestrationDirectory, cancelFlag, config.OutputS3BucketName, config.OutputS3KeyPrefix)
	}

	// TODO: instance here we have to do more result processing, where individual sub properties results are merged smartly into plugin response.
	// Currently assuming we have only one work.
	if len(properties) > 0 {
		res.Code = out[0].ExitCode
		res.Status = out[0].Status
		res.Output = out[0].String()
	}

	pluginutil.PersistPluginInformationToCurrent(log, Name(), config, res)

	return res
}
Пример #3
0
// killProcessOnCancel waits for a cancel request.
// If a cancel request is received, this method kills the underlying
// process of the command. This will unblock the command.Wait() call.
// If the task completed successfully this method returns with no action.
func killProcessOnCancel(log log.T, command *exec.Cmd, cancelFlag task.CancelFlag) {
	cancelFlag.Wait()
	if cancelFlag.Canceled() {
		log.Debug("Process cancelled. Attempting to stop process.")

		// task has been asked to cancel, kill process
		if err := killProcess(command.Process); err != nil {
			log.Error(err)
			return
		}

		log.Debug("Process stopped successfully.")
	}
}
Пример #4
0
// setMsiExecStatus sets the exit status and output to be returned to the user based on exit code
func setMsiExecStatus(log log.T, pluginInput ApplicationPluginInput, cancelFlag task.CancelFlag, out *ApplicationPluginOutput) {
	out.Stdout = pluginInput.Source
	out.Stderr = ""
	out.Status = contracts.ResultStatusFailed
	isUnKnownError := false

	switch out.ExitCode {
	case appconfig.SuccessExitCode:
		out.Status = contracts.ResultStatusSuccess
	case ErrorUnknownProduct:
		if pluginInput.Action == UNINSTALL {
			// Uninstall will skip, if product is not currently installed.
			// This is needed to support idempotent behavior.
			out.Status = contracts.ResultStatusSuccess
		}
	case ErrorSuccessRebootInitiated:
		fallthrough
	case appconfig.RebootExitCode:
		out.Status = contracts.ResultStatusSuccessAndReboot
	case pluginutil.CommandStoppedPreemptivelyExitCode:
		if cancelFlag.ShutDown() {
			out.Status = contracts.ResultStatusFailed
		}
		if cancelFlag.Canceled() {
			out.Status = contracts.ResultStatusCancelled
		}
		out.Status = contracts.ResultStatusTimedOut
	default:
		isUnKnownError = true
	}

	if isUnKnownError {
		// Note: Sample Stderr:
		// Action:{Installed}; Status:{Failed};
		// ErrorCode:{1620}; ErrorMsg:{ERROR_INSTALL_PACKAGE_INVALID};
		// Description:{This installation package could not be opened. Contact the application vendor to verify that this is a valid Windows Installer package.};
		// Source:{https:///}

		// Construct stderr in above format using StandardMsiErrorCodes
		out.Stderr = fmt.Sprintf("Action:{%v}; Status:{%v}; ErrorCode:{%v}; %v Source:{%v};", pluginInput.Action, out.Status, out.ExitCode, getExitCodeDescription(out.ExitCode), pluginInput.Source)
	}

	// Logging msiexec.Result
	log.Debug("logging stdouts & errors after setting final status for msiexec")
	log.Debugf("resultCode: %v", out.ExitCode)
	log.Debugf("stdout: %v", out.Stdout)
	log.Debugf("stderr: %v", out.Stderr)
}
Пример #5
0
// GetStatus returns a ResultStatus variable based on the received exitCode
func GetStatus(exitCode int, cancelFlag task.CancelFlag) contracts.ResultStatus {
	switch exitCode {
	case appconfig.SuccessExitCode:
		return contracts.ResultStatusSuccess
	case CommandStoppedPreemptivelyExitCode:
		if cancelFlag.ShutDown() {
			return contracts.ResultStatusFailed
		}
		if cancelFlag.Canceled() {
			return contracts.ResultStatusCancelled
		}
		return contracts.ResultStatusTimedOut
	default:
		return contracts.ResultStatusFailed
	}
}
Пример #6
0
func testCommandInvokerCancel(t *testing.T, invoke CommandInvoker, cancelFlag task.CancelFlag, testCase TestCase) {
	go func() {
		time.Sleep(100 * time.Millisecond)
		cancelFlag.Set(task.Canceled)
	}()

	start := time.Now()
	stdout, stderr, exitCode, errs := invoke(testCase.Commands)
	duration := time.Since(start)

	// test that the job returned before the normal time
	assert.True(t, duration.Seconds() <= cancelWaitTimeoutSeconds, "The command took too long to kill (%v)!", duration)

	// test that we receive kill exception
	assert.Equal(t, len(errs), 1)
	assert.IsType(t, &exec.ExitError{}, errs[0])

	assertReaderEquals(t, testCase.ExpectedStdout, stdout)
	assertReaderEquals(t, testCase.ExpectedStderr, stderr)

	assert.Equal(t, exitCode, testCase.ExpectedExitCode)
}
Пример #7
0
// Execute runs multiple sets of commands and returns their outputs.
// res.Output will contain a slice of PluginOutput.
func (p *Plugin) Execute(context context.T, config contracts.Configuration, cancelFlag task.CancelFlag) (res contracts.PluginResult) {
	log := context.Log()
	log.Infof("%v started with configuration %v", Name(), config)
	res.StartDateTime = time.Now()
	defer func() { res.EndDateTime = time.Now() }()

	//loading Properties as list since aws:applications uses properties as list
	var properties []interface{}
	if properties, res = pluginutil.LoadParametersAsList(log, config.Properties); res.Code != 0 {

		pluginutil.PersistPluginInformationToCurrent(log, Name(), config, res)
		return res
	}

	msiFailureCount := 0
	atleastOneRequestedReboot := false
	finalStdOut := ""
	finalStdErr := ""
	out := make([]ApplicationPluginOutput, len(properties))
	for i, prop := range properties {
		// check if a reboot has been requested
		if rebooter.RebootRequested() {
			log.Infof("Stopping execution of %v plugin due to an external reboot request.", Name())
			return
		}

		if cancelFlag.ShutDown() {
			res.Code = 1
			res.Status = contracts.ResultStatusFailed
			pluginutil.PersistPluginInformationToCurrent(log, Name(), config, res)
			return
		}

		if cancelFlag.Canceled() {
			res.Code = 1
			res.Status = contracts.ResultStatusCancelled
			pluginutil.PersistPluginInformationToCurrent(log, Name(), config, res)
			return
		}

		out[i] = p.runCommandsRawInput(log, prop, config.OrchestrationDirectory, cancelFlag, config.OutputS3BucketName, config.OutputS3KeyPrefix)

		if out[i].Status == contracts.ResultStatusFailed {
			msiFailureCount++

			if out[i].Stdout != "" {
				finalStdOut = fmt.Sprintf("%v\n%v", finalStdOut, out[i].Stdout)
			}

			if out[i].Stderr != "" {
				finalStdErr = fmt.Sprintf("%v\n%v", finalStdErr, out[i].Stderr)
			}
		}

		if out[i].Status == contracts.ResultStatusSuccessAndReboot {
			atleastOneRequestedReboot = true
			res.Code = out[i].ExitCode
		}
	}

	if atleastOneRequestedReboot {
		res.Status = contracts.ResultStatusSuccessAndReboot
	} else {
		res.Status = contracts.ResultStatusSuccess
		res.Code = 0
	}

	if msiFailureCount > 0 {
		finalStdOut = fmt.Sprintf("Number of Failures: %v\n%v", msiFailureCount, finalStdOut)
		res.Status = contracts.ResultStatusFailed
		res.Code = 1
	}

	finalOut := contracts.PluginOutput{
		Stdout: finalStdOut,
		Stderr: finalStdErr,
	}

	res.Output = finalOut.String()
	pluginutil.PersistPluginInformationToCurrent(log, Name(), config, res)

	return res
}
Пример #8
0
// RunCommand runs the given commands using the given working directory.
// Standard output and standard error are sent to the given writers.
func RunCommand(log log.T,
	cancelFlag task.CancelFlag,
	workingDir string,
	stdoutWriter io.Writer,
	stderrWriter io.Writer,
	executionTimeout int,
	commandName string,
	commandArguments []string,
) (exitCode int, err error) {

	command := exec.Command(commandName, commandArguments...)
	command.Dir = workingDir
	command.Stdout = stdoutWriter
	command.Stderr = stderrWriter
	exitCode = 0

	// configure OS-specific process settings
	prepareProcess(command)

	log.Debug()
	log.Debugf("Running in directory %v, command: %v %v.", workingDir, commandName, commandArguments)
	log.Debug()
	if err = command.Start(); err != nil {
		log.Error("error occurred starting the command", err)
		exitCode = 1
		return
	}

	go killProcessOnCancel(log, command, cancelFlag)

	timer := time.NewTimer(time.Duration(executionTimeout) * time.Second)
	go killProcessOnTimeout(log, command, timer)

	err = command.Wait()
	timedOut := !timer.Stop() // returns false if called previously - indicates timedOut.
	if err != nil {
		exitCode = 1
		log.Debugf("command failed to run %v", err)
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				exitCode = status.ExitStatus()
				// First try to handle Cancel and Timeout scenarios
				// SIGKILL will result in an exitcode of -1
				if exitCode == -1 {
					if cancelFlag.Canceled() {
						// set appropriate exit code based on cancel or timeout
						exitCode = pluginutil.CommandStoppedPreemptivelyExitCode
						log.Infof("The execution of command was cancelled.")
					} else if timedOut {
						// set appropriate exit code based on cancel or timeout
						exitCode = pluginutil.CommandStoppedPreemptivelyExitCode
						log.Infof("The execution of command was timedout.")
					}
				} else {
					log.Infof("The execution of command returned Exit Status: %d", exitCode)
				}
			}
		}
	} else {
		// check if cancellation or timeout failed to kill the process
		// This will not occur as we do a SIGKILL, which is not recoverable.
		if cancelFlag.Canceled() {
			// This is when the cancellation failed and the command completed successfully
			log.Errorf("the cancellation failed to stop the process.")
			// do not return as the command could have been cancelled and also timedout
		}
		if timedOut {
			// This is when the timeout failed and the command completed successfully
			log.Errorf("the timeout failed to stop the process.")
		}
	}

	log.Debug("Done waiting!")
	return
}