func (svc *sdkService) SendCommand(log log.T, documentName string, instanceIDs []string, parameters map[string][]*string, timeoutSeconds *int64, outputS3BucketName *string, outputS3KeyPrefix *string) (response *ssm.SendCommandOutput, err error) { params := ssm.SendCommandInput{ DocumentName: aws.String(documentName), InstanceIds: makeAwsStrings(instanceIDs), Comment: aws.String("Comment"), OutputS3BucketName: outputS3BucketName, OutputS3KeyPrefix: outputS3KeyPrefix, Parameters: parameters, TimeoutSeconds: timeoutSeconds, } log.Debug("SendCommand params:", params) response, err = svc.sdk.SendCommand(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("SendCommand Response", response) return }
//UpdateInstanceInformation calls the UpdateInstanceInformation SSM API. func (svc *sdkService) UpdateInstanceInformation( log log.T, agentVersion string, agentStatus string, ) (response *ssm.UpdateInstanceInformationOutput, err error) { params := ssm.UpdateInstanceInformationInput{ AgentStatus: aws.String(agentStatus), AgentVersion: aws.String(agentVersion), } goOS := runtime.GOOS switch goOS { case "windows": params.PlatformType = aws.String(ssm.PlatformTypeWindows) case "linux": params.PlatformType = aws.String(ssm.PlatformTypeLinux) default: return nil, fmt.Errorf("Cannot report platform type of unrecognized OS. %v", goOS) } if ip, err := platform.IP(); err == nil { params.IPAddress = aws.String(ip) } else { log.Warn(err) } if h, err := platform.Hostname(); err == nil { params.ComputerName = aws.String(h) } else { log.Warn(err) } if instID, err := platform.InstanceID(); err == nil { params.InstanceId = aws.String(instID) } else { log.Warn(err) } if n, err := platform.PlatformName(log); err == nil { params.PlatformName = aws.String(n) } else { log.Warn(err) } if v, err := platform.PlatformVersion(log); err == nil { params.PlatformVersion = aws.String(v) } else { log.Warn(err) } log.Debug("Calling UpdateInstanceInformation with params", params) response, err = svc.sdk.UpdateInstanceInformation(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("UpdateInstanceInformation Response", response) return }
// NewCoreManager creates a new core plugin manager. func NewCoreManager(instanceIdPtr *string, regionPtr *string, log logger.T) (cm *CoreManager, err error) { // initialize appconfig var config appconfig.SsmagentConfig if config, err = appconfig.Config(false); err != nil { log.Errorf("Could not load config file: %v", err) return } // initialize region if *regionPtr != "" { if err = platform.SetRegion(*regionPtr); err != nil { log.Errorf("error occured setting the region, %v", err) return } } var region string if region, err = platform.Region(); err != nil { log.Errorf("error fetching the region, %v", err) return } log.Debug("Using region:", region) // initialize instance ID if *instanceIdPtr != "" { if err = platform.SetInstanceID(*instanceIdPtr); err != nil { log.Errorf("error occured setting the instance ID, %v", err) return } } var instanceId string if instanceId, err = platform.InstanceID(); err != nil { log.Errorf("error fetching the instanceID, %v", err) return } log.Debug("Using instanceID:", instanceId) if err = fileutil.HardenDataFolder(); err != nil { log.Errorf("error initializing SSM data folder with hardened ACL, %v", err) return } //Initialize all folders where interim states of executing commands will be stored. if !initializeBookkeepingLocations(log, instanceId) { log.Error("unable to initialize. Exiting") return } context := context.Default(log, config).With("[instanceID=" + instanceId + "]") corePlugins := coreplugins.RegisteredCorePlugins(context) return &CoreManager{ context: context, corePlugins: *corePlugins, }, nil }
// killProcessOnTimeout waits for a timeout. // When the timeout is reached, 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 killProcessOnTimeout(log log.T, command *exec.Cmd, timer *time.Timer) { <-timer.C log.Debug("Process exceeded timeout. Attempting to stop process.") // task has been exceeded the allowed execution timeout, kill process if err := killProcess(command.Process); err != nil { log.Error(err) return } log.Debug("Process stopped successfully") }
func killProcessOnTimeout(log log.T, command *exec.Cmd, timer *time.Timer) { <-timer.C log.Debug("Process exceeded timeout. Attempting to kill process!") // task has been exceeded the allowed execution timeout, kill process if err := command.Process.Kill(); err != nil { log.Error(err) return } log.Debug("Done kill process!") }
// DeleteMessage calls the DeleteMessage MDS API. func (mds *sdkService) DeleteMessage(log log.T, messageID string) (err error) { params := &ssmmds.DeleteMessageInput{ MessageId: aws.String(messageID), // Required } log.Debug("Calling DeleteMessage with params", params) req, resp := mds.sdk.DeleteMessageRequest(params) if err = mds.sendRequest(req); err != nil { err = fmt.Errorf("DeleteMessage Error: %v", err) log.Debug(err) } else { log.Debug("DeleteMessage Response", resp) } return }
// 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.") } }
// processParams smartly divides the input parameter string into valid string blocks func processParams(log log.T, str string) []string { // Sample transformation: // str = "/v value "some path" myproperty=value" // result: []string{"/v", "value", "some path", "myproperty=value"} // contains the last split location of the string lastbit := 0 params := []string{} // true if first quote was encountered else false quoteInit := false // Iterate through each character in str for i, c := range str { // Look for quotes or spaces // By default we split a string using space as a delimiter // If a quote(") is encountered then wait for the next quote irrespective of any spaces in between if c == '"' { if quoteInit { quoteInit = false params = append(params, str[lastbit:i+1]) lastbit = i + 1 } else { quoteInit = true lastbit = i } } else if c == ' ' && !quoteInit { if lastbit != i { params = append(params, str[lastbit:i]) } lastbit = i + 1 } } // This handles the last word in str if lastbit < len(str) { params = append(params, str[lastbit:len(str)]) } log.Debug("Parameters after processing...") for _, param := range params { log.Debug(param) } return params }
// FailMessage calls the FailMessage MDS API. func (mds *sdkService) FailMessage(log log.T, messageID string, failureType FailureType) (err error) { params := &ssmmds.FailMessageInput{ FailureType: aws.String(string(failureType)), // Required MessageId: aws.String(messageID), // Required } log.Debug("Calling FailMessage with params", params) req, resp := mds.sdk.FailMessageRequest(params) if err = mds.sendRequest(req); err != nil { err = fmt.Errorf("FailMessage Error: %v", err) log.Debug(err) } else { log.Debug("FailMessage Response", resp) } return }
func (svc *sdkService) CancelCommand(log log.T, commandID string, instanceIDs []string) (response *ssm.CancelCommandOutput, err error) { params := ssm.CancelCommandInput{ CommandId: aws.String(commandID), } if len(instanceIDs) > 0 { params.InstanceIds = makeAwsStrings(instanceIDs) } log.Debug("CancelCommand params:", params) response, err = svc.sdk.CancelCommand(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("CancelCommand Response", response) return }
// SendReply calls the SendReply MDS API. func (mds *sdkService) SendReply(log log.T, messageID string, payload string) (err error) { uuid.SwitchFormat(uuid.CleanHyphen) replyID := uuid.NewV4().String() params := &ssmmds.SendReplyInput{ MessageId: aws.String(messageID), // Required Payload: aws.String(payload), // Required ReplyId: aws.String(replyID), // Required } log.Debug("Calling SendReply with params", params) req, resp := mds.sdk.SendReplyRequest(params) if err = mds.sendRequest(req); err != nil { err = fmt.Errorf("SendReply Error: %v", err) log.Debug(err) } else { log.Info("SendReply Response", resp) } return }
func (svc *sdkService) DeleteDocument(log log.T, docName string) (response *ssm.DeleteDocumentOutput, err error) { params := ssm.DeleteDocumentInput{ Name: aws.String(docName), // Required } response, err = svc.sdk.DeleteDocument(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("DeleteDocument Response", response) return }
func (svc *sdkService) CreateDocument(log log.T, docName string, docContent string) (response *ssm.CreateDocumentOutput, err error) { params := ssm.CreateDocumentInput{ Content: aws.String(docContent), Name: aws.String(docName), } response, err = svc.sdk.CreateDocument(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("CreateDocument Response", response) return }
// GetMessages calls the GetMessages MDS API. func (mds *sdkService) GetMessages(log log.T, instanceID string) (messages *ssmmds.GetMessagesOutput, err error) { uuid.SwitchFormat(uuid.CleanHyphen) uid := uuid.NewV4().String() params := &ssmmds.GetMessagesInput{ Destination: aws.String(instanceID), // Required MessagesRequestId: aws.String(uid), // Required VisibilityTimeoutInSeconds: aws.Int64(10), } log.Debug("Calling GetMessages with params", params) requestTime := time.Now() req, messages := mds.sdk.GetMessagesRequest(params) if requestErr := mds.sendRequest(req); requestErr != nil { log.Debug(requestErr) if isErrorUnexpected(log, requestErr, requestTime, time.Now()) { //GetMessages api responded with unexpected errors - we must return this as error err = fmt.Errorf("GetMessages Error: %v", requestErr) log.Debug(err) } } else { log.Debug("GetMessages Response", messages) } return }
// 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) }
//ListAssociations calls the ListAssociations SSM API. func (svc *sdkService) ListAssociations(log log.T, instanceID string) (response *ssm.ListAssociationsOutput, err error) { params := ssm.ListAssociationsInput{ AssociationFilterList: []*ssm.AssociationFilter{ { Key: aws.String("InstanceId"), Value: aws.String(instanceID), }, }, MaxResults: aws.Int64(1), } response, err = svc.sdk.ListAssociations(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("ListAssociations Response", response) return }
func (svc *sdkService) ListCommands(log log.T, instanceID string) (response *ssm.ListCommandsOutput, err error) { params := ssm.ListCommandsInput{ // Filters: []*ssm.CommandFilter{ // { // Required // Key: aws.String("CommandFilterKey"), // Required // Value: aws.String("CommandFilterValue"), // Required // }, // }, InstanceId: aws.String(instanceID), MaxResults: aws.Int64(25), } response, err = svc.sdk.ListCommands(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("ListCommands Response", response) return }
func (svc *sdkService) ListCommandInvocations(log log.T, instanceID string, commandID string) (response *ssm.ListCommandInvocationsOutput, err error) { params := ssm.ListCommandInvocationsInput{ CommandId: aws.String(commandID), Details: aws.Bool(true), // Filters: []*ssm.CommandFilter{ // { // Required // Key: aws.String("CommandFilterKey"), // Required // Value: aws.String("CommandFilterValue"), // Required // }, // // More values... // }, InstanceId: aws.String(instanceID), MaxResults: aws.Int64(25), // NextToken: aws.String("NextToken"), } response, err = svc.sdk.ListCommandInvocations(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("ListCommandInvocations Response", response) return }
// runCommands executes one set of commands and returns their output. func (p *Plugin) runCommands(log log.T, pluginInput PSModulePluginInput, orchestrationDirectory string, cancelFlag task.CancelFlag, outputS3BucketName string, outputS3KeyPrefix string) (out PSModulePluginOutput) { var err error // if no orchestration directory specified, create temp directory var useTempDirectory = (orchestrationDirectory == "") var tempDir string if useTempDirectory { if tempDir, err = ioutil.TempDir("", "Ec2RunCommand"); err != nil { out.Errors = append(out.Errors, err.Error()) log.Error(err) return } orchestrationDirectory = tempDir } orchestrationDir := fileutil.RemoveInvalidChars(filepath.Join(orchestrationDirectory, pluginInput.ID)) log.Debugf("Running commands %v in workingDirectory %v; orchestrationDir %v ", pluginInput.RunCommand, pluginInput.WorkingDirectory, orchestrationDir) // create orchestration dir if needed if err = fileutil.MakeDirsWithExecuteAccess(orchestrationDir); err != nil { log.Debug("failed to create orchestrationDir directory", orchestrationDir) out.Errors = append(out.Errors, err.Error()) return } // Create script file path scriptPath := filepath.Join(orchestrationDir, pluginutil.RunCommandScriptName) log.Debugf("Writing commands %v to file %v", pluginInput, scriptPath) // Create script file if err = pluginutil.CreateScriptFile(log, scriptPath, pluginInput.RunCommand); err != nil { out.Errors = append(out.Errors, err.Error()) log.Errorf("failed to create script file. %v", err) return } // Download file from source if available downloadOutput, err := pluginutil.DownloadFileFromSource(log, pluginInput.Source, pluginInput.SourceHash, pluginInput.SourceHashType) if err != nil || downloadOutput.IsHashMatched == false || downloadOutput.LocalFilePath == "" { errorString := fmt.Errorf("failed to download file reliably %v", pluginInput.Source) out.MarkAsFailed(log, errorString) return } else { // Uncompress the zip file received fileutil.Uncompress(downloadOutput.LocalFilePath, PowerShellModulesDirectory) } // Set execution time executionTimeout := pluginutil.ValidateExecutionTimeout(log, pluginInput.TimeoutSeconds) // Create output file paths stdoutFilePath := filepath.Join(orchestrationDir, p.StdoutFileName) stderrFilePath := filepath.Join(orchestrationDir, p.StderrFileName) log.Debugf("stdout file %v, stderr file %v", stdoutFilePath, stderrFilePath) // Construct Command Name and Arguments commandName := pluginutil.GetShellCommand() commandArguments := append(pluginutil.GetShellArguments(), scriptPath, pluginutil.ExitCodeTrap) // Execute Command stdout, stderr, exitCode, errs := p.ExecuteCommand(log, pluginInput.WorkingDirectory, stdoutFilePath, stderrFilePath, cancelFlag, executionTimeout, commandName, commandArguments) // Set output status out.ExitCode = exitCode out.Status = pluginutil.GetStatus(out.ExitCode, cancelFlag) if len(errs) > 0 { for _, err := range errs { out.Errors = append(out.Errors, err.Error()) if out.Status != contracts.ResultStatusCancelled && out.Status != contracts.ResultStatusTimedOut && out.Status != contracts.ResultStatusSuccessAndReboot { log.Error("failed to run commands: ", err) out.Status = contracts.ResultStatusFailed } } } // read (a prefix of) the standard output/error out.Stdout, err = pluginutil.ReadPrefix(stdout, p.MaxStdoutLength, p.OutputTruncatedSuffix) if err != nil { out.Errors = append(out.Errors, err.Error()) log.Error(err) } out.Stderr, err = pluginutil.ReadPrefix(stderr, p.MaxStderrLength, p.OutputTruncatedSuffix) if err != nil { out.Errors = append(out.Errors, err.Error()) log.Error(err) } // Upload output to S3 uploadOutputToS3BucketErrors := p.ExecuteUploadOutputToS3Bucket(log, pluginInput.ID, orchestrationDir, outputS3BucketName, outputS3KeyPrefix, useTempDirectory, tempDir, out.Stdout, out.Stderr) out.Errors = append(out.Errors, uploadOutputToS3BucketErrors...) // Return Json indented response responseContent, _ := jsonutil.Marshal(out) log.Debug("Returning response:\n", jsonutil.Indent(responseContent)) return }
// 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 }
// s3Download attempts to download a file via the aws sdk. func s3Download(log log.T, amazonS3URL s3util.AmazonS3URL, destFile string) (output DownloadOutput, err error) { log.Debugf("attempting to download as s3 download %v", destFile) eTagFile := destFile + ".etag" config := &aws.Config{} var appConfig appconfig.SsmagentConfig appConfig, err = appconfig.Config(false) if err != nil { log.Error("failed to read appconfig.") } else { creds, err1 := appConfig.ProfileCredentials() if err1 != nil { config.Credentials = creds } } config.S3ForcePathStyle = aws.Bool(amazonS3URL.IsPathStyle) config.Region = aws.String(amazonS3URL.Region) params := &s3.GetObjectInput{ Bucket: aws.String(amazonS3URL.Bucket), Key: aws.String(amazonS3URL.Key), } if fileutil.Exists(destFile) == true && fileutil.Exists(eTagFile) == true { var existingETag string existingETag, err = fileutil.ReadAllText(eTagFile) if err != nil { log.Debugf("failed to read etag file %v, %v", eTagFile, err) return } params.IfNoneMatch = aws.String(existingETag) } s3client := s3.New(session.New(config)) req, resp := s3client.GetObjectRequest(params) err = req.Send() if err != nil { if req.HTTPResponse == nil || req.HTTPResponse.StatusCode != http.StatusNotModified { log.Debug("failed to download from s3, ", err) fileutil.DeleteFile(destFile) fileutil.DeleteFile(eTagFile) return } log.Debugf("Unchanged file.") output.IsUpdated = false output.LocalFilePath = destFile return output, nil } if *resp.ETag != "" { log.Debug("files etag is ", *resp.ETag) err = fileutil.WriteAllText(eTagFile, *resp.ETag) if err != nil { log.Errorf("failed to write eTagfile %v, %v ", eTagFile, err) return } } defer resp.Body.Close() _, err = FileCopy(log, destFile, resp.Body) if err == nil { output.LocalFilePath = destFile output.IsUpdated = true } else { log.Errorf("failed to write destFile %v, %v ", destFile, err) } return }
// runCommands executes one set of commands and returns their output. func (p *Plugin) runCommands(log log.T, pluginInput ApplicationPluginInput, orchestrationDirectory string, cancelFlag task.CancelFlag, outputS3BucketName string, outputS3KeyPrefix string) (out ApplicationPluginOutput) { var err error // if no orchestration directory specified, create temp directory var useTempDirectory = (orchestrationDirectory == "") var tempDir string if useTempDirectory { if tempDir, err = ioutil.TempDir("", "Ec2RunCommand"); err != nil { out.Errors = append(out.Errors, err.Error()) log.Error(err) return } orchestrationDirectory = tempDir } orchestrationDir := fileutil.RemoveInvalidChars(filepath.Join(orchestrationDirectory, pluginInput.ID)) log.Debugf("OrchestrationDir %v ", orchestrationDir) // create orchestration dir if needed if err = fileutil.MakeDirs(orchestrationDir); err != nil { log.Debug("failed to create orchestrationDir directory", orchestrationDir, err) out.Errors = append(out.Errors, err.Error()) return } // Get application mode mode, err := getMsiApplicationMode(pluginInput) if err != nil { out.MarkAsFailed(log, err) return } log.Debugf("mode is %v", mode) // Download file from source if available downloadOutput, err := pluginutil.DownloadFileFromSource(log, pluginInput.Source, pluginInput.SourceHash, pluginInput.SourceHashType) if err != nil || downloadOutput.IsHashMatched == false || downloadOutput.LocalFilePath == "" { errorString := fmt.Errorf("failed to download file reliably %v", pluginInput.Source) out.MarkAsFailed(log, errorString) return } log.Debugf("local path to file is %v", downloadOutput.LocalFilePath) // Create msi related log file localSourceLogFilePath := downloadOutput.LocalFilePath + ".msiexec.log.txt" log.Debugf("log path is %v", localSourceLogFilePath) // TODO: This needs to be pulled out of this function as it runs multiple times getting initialized with the same values // Create output file paths stdoutFilePath := filepath.Join(orchestrationDir, p.StdoutFileName) stderrFilePath := filepath.Join(orchestrationDir, p.StderrFileName) log.Debugf("stdout file %v, stderr file %v", stdoutFilePath, stderrFilePath) // Construct Command Name and Arguments commandName := msiExecCommand commandArguments := []string{mode, downloadOutput.LocalFilePath, "/quiet", "/norestart", "/log", localSourceLogFilePath} if pluginInput.Parameters != "" { log.Debugf("Got Parameters \"%v\"", pluginInput.Parameters) params := processParams(log, pluginInput.Parameters) commandArguments = append(commandArguments, params...) } // Execute Command _, _, exitCode, errs := p.ExecuteCommand(log, defaultWorkingDirectory, stdoutFilePath, stderrFilePath, cancelFlag, defaultApplicationExecutionTimeoutInSeconds, commandName, commandArguments) // Set output status out.ExitCode = exitCode setMsiExecStatus(log, pluginInput, cancelFlag, &out) if len(errs) > 0 { for _, err := range errs { out.Errors = append(out.Errors, err.Error()) log.Error("failed to run commands: ", err) out.Status = contracts.ResultStatusFailed } return } // Upload output to S3 uploadOutputToS3BucketErrors := p.ExecuteUploadOutputToS3Bucket(log, pluginInput.ID, orchestrationDir, outputS3BucketName, outputS3KeyPrefix, useTempDirectory, tempDir, out.Stdout, out.Stderr) out.Errors = append(out.Errors, uploadOutputToS3BucketErrors...) // Return Json indented response responseContent, _ := jsonutil.Marshal(out) log.Debug("Returning response:\n", jsonutil.Indent(responseContent)) return }
// httpDownload attempts to download a file via http/s call func httpDownload(log log.T, fileURL string, destFile string) (output DownloadOutput, err error) { log.Debugf("attempting to download as http/https download %v", destFile) eTagFile := destFile + ".etag" var check http.Client var request *http.Request request, err = http.NewRequest("GET", fileURL, nil) if err != nil { return } if fileutil.Exists(destFile) == true && fileutil.Exists(eTagFile) == true { var existingETag string existingETag, err = fileutil.ReadAllText(eTagFile) request.Header.Add("If-None-Match", existingETag) } check = http.Client{ CheckRedirect: func(r *http.Request, via []*http.Request) error { r.URL.Opaque = r.URL.Path return nil }, } var resp *http.Response resp, err = check.Do(request) if err != nil { log.Debug("failed to download from http/https, ", err) fileutil.DeleteFile(destFile) fileutil.DeleteFile(eTagFile) return } if resp.StatusCode == http.StatusNotModified { log.Debugf("Unchanged file.") output.IsUpdated = false output.LocalFilePath = destFile return output, nil } else if resp.StatusCode != http.StatusOK { log.Debug("failed to download from http/https, ", err) fileutil.DeleteFile(destFile) fileutil.DeleteFile(eTagFile) err = fmt.Errorf("http request failed. status:%v statuscode:%v", resp.Status, resp.StatusCode) return } defer resp.Body.Close() eTagValue := resp.Header.Get("Etag") if eTagValue != "" { log.Debug("file eTagValue is ", eTagValue) err = fileutil.WriteAllText(eTagFile, eTagValue) if err != nil { log.Errorf("failed to write eTagfile %v, %v ", eTagFile, err) return } } _, err = FileCopy(log, destFile, resp.Body) if err == nil { output.LocalFilePath = destFile output.IsUpdated = true } else { log.Errorf("failed to write destFile %v, %v ", destFile, err) } return }