github.com/secure-build/gitlab-runner@v12.5.0+incompatible/commands/helpers/cache_extractor.go (about)

     1  package helpers
     2  
     3  import (
     4  	"io"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"os"
     8  	"path/filepath"
     9  	"time"
    10  
    11  	"github.com/sirupsen/logrus"
    12  	"github.com/urfave/cli"
    13  
    14  	"gitlab.com/gitlab-org/gitlab-runner/common"
    15  	"gitlab.com/gitlab-org/gitlab-runner/helpers/archives"
    16  	url_helpers "gitlab.com/gitlab-org/gitlab-runner/helpers/url"
    17  	"gitlab.com/gitlab-org/gitlab-runner/log"
    18  )
    19  
    20  type CacheExtractorCommand struct {
    21  	retryHelper
    22  	File    string `long:"file" description:"The file containing your cache artifacts"`
    23  	URL     string `long:"url" description:"URL of remote cache resource"`
    24  	Timeout int    `long:"timeout" description:"Overall timeout for cache downloading request (in minutes)"`
    25  
    26  	client *CacheClient
    27  }
    28  
    29  func (c *CacheExtractorCommand) getClient() *CacheClient {
    30  	if c.client == nil {
    31  		c.client = NewCacheClient(c.Timeout)
    32  	}
    33  
    34  	return c.client
    35  }
    36  
    37  func checkIfUpToDate(path string, resp *http.Response) (bool, time.Time) {
    38  	fi, _ := os.Lstat(path)
    39  	date, _ := time.Parse(http.TimeFormat, resp.Header.Get("Last-Modified"))
    40  	return fi != nil && !date.After(fi.ModTime()), date
    41  }
    42  
    43  func (c *CacheExtractorCommand) download() error {
    44  	err := os.MkdirAll(filepath.Dir(c.File), 0700)
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	resp, err := c.getCache()
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	defer resp.Body.Close()
    55  
    56  	upToDate, date := checkIfUpToDate(c.File, resp)
    57  	if upToDate {
    58  		logrus.Infoln(filepath.Base(c.File), "is up to date")
    59  		return nil
    60  	}
    61  
    62  	file, err := ioutil.TempFile(filepath.Dir(c.File), "cache")
    63  	if err != nil {
    64  		return err
    65  	}
    66  	defer os.Remove(file.Name())
    67  	defer file.Close()
    68  
    69  	logrus.Infoln("Downloading", filepath.Base(c.File), "from", url_helpers.CleanURL(c.URL))
    70  	_, err = io.Copy(file, resp.Body)
    71  	if err != nil {
    72  		return retryableErr{err: err}
    73  	}
    74  	err = os.Chtimes(file.Name(), time.Now(), date)
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	err = file.Close()
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	err = os.Rename(file.Name(), c.File)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  func (c *CacheExtractorCommand) getCache() (*http.Response, error) {
    93  	resp, err := c.getClient().Get(c.URL)
    94  	if err != nil {
    95  		return nil, retryableErr{err: err}
    96  	}
    97  
    98  	if resp.StatusCode == http.StatusNotFound {
    99  		resp.Body.Close()
   100  		return nil, os.ErrNotExist
   101  	}
   102  
   103  	return resp, retryOnServerError(resp)
   104  }
   105  
   106  func (c *CacheExtractorCommand) Execute(context *cli.Context) {
   107  	log.SetRunnerFormatter()
   108  
   109  	if len(c.File) == 0 {
   110  		logrus.Fatalln("Missing cache file")
   111  	}
   112  
   113  	if c.URL != "" {
   114  		err := c.doRetry(c.download)
   115  		if err != nil {
   116  			logrus.Fatalln(err)
   117  		}
   118  	} else {
   119  		logrus.Infoln("No URL provided, cache will not be downloaded from shared cache server. Instead a local version of cache will be extracted.")
   120  	}
   121  
   122  	err := archives.ExtractZipFile(c.File)
   123  	if err != nil && !os.IsNotExist(err) {
   124  		logrus.Fatalln(err)
   125  	}
   126  }
   127  
   128  func init() {
   129  	common.RegisterCommand2("cache-extractor", "download and extract cache artifacts (internal)", &CacheExtractorCommand{
   130  		retryHelper: retryHelper{
   131  			Retry:     2,
   132  			RetryTime: time.Second,
   133  		},
   134  	})
   135  }