github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/plugin/builtin/attach/results_command.go (about)

     1  package attach
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  
     7  	"github.com/evergreen-ci/evergreen/model"
     8  	"github.com/evergreen-ci/evergreen/model/task"
     9  	"github.com/evergreen-ci/evergreen/plugin"
    10  	"github.com/evergreen-ci/evergreen/util"
    11  	"github.com/mitchellh/mapstructure"
    12  	"github.com/mongodb/grip/slogger"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  // AttachResultsCommand is used to attach MCI test results in json
    17  // format to the task page.
    18  type AttachResultsCommand struct {
    19  	// FileLoc describes the relative path of the file to be sent.
    20  	// Note that this can also be described via expansions.
    21  	FileLoc string `mapstructure:"file_location" plugin:"expand"`
    22  }
    23  
    24  func (self *AttachResultsCommand) Name() string {
    25  	return AttachResultsCmd
    26  }
    27  
    28  func (self *AttachResultsCommand) Plugin() string {
    29  	return AttachPluginName
    30  }
    31  
    32  // ParseParams decodes the S3 push command parameters that are
    33  // specified as part of an AttachPlugin command; this is required
    34  // to satisfy the 'Command' interface
    35  func (self *AttachResultsCommand) ParseParams(params map[string]interface{}) error {
    36  	if err := mapstructure.Decode(params, self); err != nil {
    37  		return errors.Wrapf(err, "error decoding '%v' params", self.Name())
    38  	}
    39  
    40  	if err := self.validateAttachResultsParams(); err != nil {
    41  		return errors.Wrapf(err, "error validating '%v' params", self.Name())
    42  	}
    43  	return nil
    44  }
    45  
    46  // validateAttachResultsParams is a helper function that ensures all
    47  // the fields necessary for attaching a results are present
    48  func (self *AttachResultsCommand) validateAttachResultsParams() (err error) {
    49  	if self.FileLoc == "" {
    50  		return errors.New("file_location cannot be blank")
    51  	}
    52  	return nil
    53  }
    54  
    55  func (self *AttachResultsCommand) expandAttachResultsParams(
    56  	taskConfig *model.TaskConfig) (err error) {
    57  	self.FileLoc, err = taskConfig.Expansions.ExpandString(self.FileLoc)
    58  	if err != nil {
    59  		return errors.Wrap(err, "error expanding file_location")
    60  	}
    61  	return nil
    62  }
    63  
    64  // Execute carries out the AttachResultsCommand command - this is required
    65  // to satisfy the 'Command' interface
    66  func (self *AttachResultsCommand) Execute(pluginLogger plugin.Logger,
    67  	pluginCom plugin.PluginCommunicator,
    68  	taskConfig *model.TaskConfig,
    69  	stop chan bool) error {
    70  
    71  	if err := self.expandAttachResultsParams(taskConfig); err != nil {
    72  		return errors.WithStack(err)
    73  	}
    74  
    75  	errChan := make(chan error)
    76  	go func() {
    77  		reportFileLoc := self.FileLoc
    78  		if !filepath.IsAbs(self.FileLoc) {
    79  			reportFileLoc = filepath.Join(taskConfig.WorkDir, self.FileLoc)
    80  		}
    81  
    82  		// attempt to open the file
    83  		reportFile, err := os.Open(reportFileLoc)
    84  		if err != nil {
    85  			errChan <- errors.Wrapf(err, "Couldn't open report file '%s'", reportFileLoc)
    86  			return
    87  		}
    88  
    89  		results := &task.TestResults{}
    90  		if err = util.ReadJSONInto(reportFile, results); err != nil {
    91  			errChan <- errors.Wrapf(err, "Couldn't read report file '%s'", reportFileLoc)
    92  			return
    93  		}
    94  		if err := reportFile.Close(); err != nil {
    95  			pluginLogger.LogExecution(slogger.INFO, "Error closing file: %v", err)
    96  		}
    97  		errChan <- errors.WithStack(SendJSONResults(taskConfig, pluginLogger, pluginCom, results))
    98  	}()
    99  
   100  	select {
   101  	case err := <-errChan:
   102  		return errors.WithStack(err)
   103  	case <-stop:
   104  		pluginLogger.LogExecution(slogger.INFO, "Received signal to terminate"+
   105  			" execution of attach results command")
   106  		return nil
   107  	}
   108  }
   109  
   110  // SendJSONResults is responsible for sending the
   111  // specified file to the API Server
   112  func SendJSONResults(taskConfig *model.TaskConfig,
   113  	pluginLogger plugin.Logger, pluginCom plugin.PluginCommunicator,
   114  	results *task.TestResults) error {
   115  	for i, res := range results.Results {
   116  
   117  		if res.LogRaw != "" {
   118  			pluginLogger.LogExecution(slogger.INFO, "Attaching raw test logs")
   119  			testLogs := &model.TestLog{
   120  				Name:          res.TestFile,
   121  				Task:          taskConfig.Task.Id,
   122  				TaskExecution: taskConfig.Task.Execution,
   123  				Lines:         []string{res.LogRaw},
   124  			}
   125  
   126  			id, err := pluginCom.TaskPostTestLog(testLogs)
   127  			if err != nil {
   128  				pluginLogger.LogExecution(slogger.ERROR, "Error posting raw logs from results: %v", err)
   129  			} else {
   130  				results.Results[i].LogId = id
   131  			}
   132  
   133  			// clear the logs from the TestResult struct after it has been saved in the test logs. Since they are
   134  			// being saved in the test_logs collection, we can clear them to prevent them from being saved in the task
   135  			// collection.
   136  			results.Results[i].LogRaw = ""
   137  
   138  		}
   139  	}
   140  
   141  	pluginLogger.LogExecution(slogger.INFO, "Attaching test results")
   142  	err := pluginCom.TaskPostResults(results)
   143  	if err != nil {
   144  		return errors.WithStack(err)
   145  	}
   146  
   147  	pluginLogger.LogTask(slogger.INFO, "Attach test results succeeded")
   148  	return nil
   149  }