// getPluginConfigurations converts from PluginConfig (structure from the MDS message) to plugin.Configuration (structure expected by the plugin) func getPluginConfigurations(runtimeConfig map[string]*contracts.PluginConfig, orchestrationDir, s3BucketName, s3KeyPrefix, messageID string) (res map[string]*contracts.Configuration) { res = make(map[string]*contracts.Configuration) for pluginName, pluginConfig := range runtimeConfig { res[pluginName] = &contracts.Configuration{ Properties: pluginConfig.Properties, OutputS3BucketName: s3BucketName, OutputS3KeyPrefix: path.Join(s3KeyPrefix, fileutil.RemoveInvalidChars(pluginName)), OrchestrationDirectory: filepath.Join(orchestrationDir, fileutil.RemoveInvalidChars(pluginName)), MessageId: messageID, BookKeepingFileName: getCommandID(messageID), } } return }
func setExecuterExpectations(mockExecuter *executers.MockCommandExecuter, t TestCase, cancelFlag task.CancelFlag, p *Plugin) { orchestrationDir := filepath.Join(orchestrationDirectory, fileutil.RemoveInvalidChars(t.Input.ID)) stdoutFilePath := filepath.Join(orchestrationDir, p.StdoutFileName) stderrFilePath := filepath.Join(orchestrationDir, p.StderrFileName) mockExecuter.On("Execute", mock.Anything, t.Input.WorkingDirectory, stdoutFilePath, stderrFilePath, cancelFlag, mock.Anything, mock.Anything, mock.Anything).Return( readerFromString(t.Output.Stdout), readerFromString(t.Output.Stderr), t.Output.ExitCode, t.ExecuterErrors) }
// 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 }
func setS3UploaderExpectations(mockS3Uploader *pluginutil.MockDefaultPlugin, t TestCase, p *Plugin) { var emptyArray []string orchestrationDir := filepath.Join(orchestrationDirectory, fileutil.RemoveInvalidChars(t.Input.ID)) mockS3Uploader.On("UploadOutputToS3Bucket", mock.Anything, t.Input.ID, orchestrationDir, s3BucketName, s3KeyPrefix, false, "", t.Output.Stdout, t.Output.Stderr).Return(emptyArray) }
// 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 }