github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/plugin/builtin/archive/tar_gz_pack_command.go (about)

     1  package archive
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  
     7  	"github.com/evergreen-ci/evergreen/archive"
     8  	"github.com/evergreen-ci/evergreen/model"
     9  	"github.com/evergreen-ci/evergreen/plugin"
    10  	"github.com/mitchellh/mapstructure"
    11  	"github.com/mongodb/grip"
    12  	"github.com/mongodb/grip/send"
    13  	"github.com/mongodb/grip/slogger"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // Plugin command responsible for creating a tgz archive.
    18  type TarGzPackCommand struct {
    19  	// the tgz file that will be created
    20  	Target string `mapstructure:"target" plugin:"expand"`
    21  
    22  	// the directory to compress
    23  	SourceDir string `mapstructure:"source_dir" plugin:"expand"`
    24  
    25  	// a list of filename blobs to include,
    26  	// e.g. "*.tgz", "file.txt", "test_*"
    27  	Include []string `mapstructure:"include" plugin:"expand"`
    28  
    29  	// a list of filename blobs to exclude,
    30  	// e.g. "*.zip", "results.out", "ignore/**"
    31  	ExcludeFiles []string `mapstructure:"exclude_files" plugin:"expand"`
    32  }
    33  
    34  func (self *TarGzPackCommand) Name() string {
    35  	return TarGzPackCmdName
    36  }
    37  
    38  func (self *TarGzPackCommand) Plugin() string {
    39  	return ArchivePluginName
    40  }
    41  
    42  // ParseParams reads in the given parameters for the command.
    43  func (self *TarGzPackCommand) ParseParams(params map[string]interface{}) error {
    44  	if err := mapstructure.Decode(params, self); err != nil {
    45  		return errors.Wrapf(err, "error parsing '%v' params", self.Name())
    46  	}
    47  	if err := self.validateParams(); err != nil {
    48  		return errors.Wrapf(err, "error validating '%v' params", self.Name())
    49  	}
    50  	return nil
    51  }
    52  
    53  // Make sure a target and source dir are set, and files are specified to be
    54  // included.
    55  func (self *TarGzPackCommand) validateParams() error {
    56  	if self.Target == "" {
    57  		return errors.New("target cannot be blank")
    58  	}
    59  	if self.SourceDir == "" {
    60  		return errors.New("source_dir cannot be blank")
    61  	}
    62  	if len(self.Include) == 0 {
    63  		return errors.New("include cannot be empty")
    64  	}
    65  
    66  	return nil
    67  }
    68  
    69  // Execute builds the archive.
    70  func (self *TarGzPackCommand) Execute(pluginLogger plugin.Logger,
    71  	pluginCom plugin.PluginCommunicator,
    72  	conf *model.TaskConfig,
    73  	stop chan bool) error {
    74  
    75  	if err := plugin.ExpandValues(self, conf.Expansions); err != nil {
    76  		return errors.Wrap(err, "error expanding params")
    77  	}
    78  
    79  	// if the source dir is a relative path, join it to the working dir
    80  	if !filepath.IsAbs(self.SourceDir) {
    81  		self.SourceDir = filepath.Join(conf.WorkDir, self.SourceDir)
    82  	}
    83  
    84  	// if the target is a relative path, join it to the working dir
    85  	if !filepath.IsAbs(self.Target) {
    86  		self.Target = filepath.Join(conf.WorkDir, self.Target)
    87  	}
    88  
    89  	errChan := make(chan error)
    90  	filesArchived := -1
    91  	go func() {
    92  		var err error
    93  		filesArchived, err = self.BuildArchive(pluginLogger)
    94  		errChan <- errors.WithStack(err)
    95  	}()
    96  
    97  	select {
    98  	case err := <-errChan:
    99  		if err != nil {
   100  			return errors.WithStack(err)
   101  		}
   102  		if filesArchived == 0 {
   103  			deleteErr := os.Remove(self.Target)
   104  			if deleteErr != nil {
   105  				pluginLogger.LogExecution(slogger.INFO, "Error deleting empty archive: %v", deleteErr)
   106  			}
   107  		}
   108  		return nil
   109  	case <-stop:
   110  		pluginLogger.LogExecution(slogger.INFO, "Received signal to terminate"+
   111  			" execution of targz pack command")
   112  		return nil
   113  	}
   114  
   115  }
   116  
   117  // since archive.BuildArchive takes in a slogger.Logger
   118  type agentAppender struct {
   119  	pluginLogger plugin.Logger
   120  }
   121  
   122  // satisfy the slogger.Appender interface
   123  func (self *agentAppender) Append(log *slogger.Log) error {
   124  	self.pluginLogger.LogExecution(log.Level, slogger.FormatLog(log))
   125  	return nil
   126  }
   127  
   128  // Build the archive.
   129  // Returns the number of files included in the archive (0 means empty archive).
   130  func (self *TarGzPackCommand) BuildArchive(pluginLogger plugin.Logger) (int, error) {
   131  	// create a logger to pass into the BuildArchive command
   132  	appender := &agentAppender{
   133  		pluginLogger: pluginLogger,
   134  	}
   135  
   136  	log := &slogger.Logger{
   137  		Name:      "",
   138  		Appenders: []send.Sender{slogger.WrapAppender(appender)},
   139  	}
   140  
   141  	// create a targz writer for the target file
   142  	f, gz, tarWriter, err := archive.TarGzWriter(self.Target)
   143  	if err != nil {
   144  		return -1, errors.Wrapf(err, "error opening target archive file %s", self.Target)
   145  	}
   146  	defer func() {
   147  		grip.CatchError(tarWriter.Close())
   148  		grip.CatchError(gz.Close())
   149  		grip.CatchError(f.Close())
   150  	}()
   151  
   152  	// Build the archive
   153  	out, err := archive.BuildArchive(tarWriter, self.SourceDir, self.Include,
   154  		self.ExcludeFiles, log)
   155  	return out, errors.WithStack(err)
   156  }