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 }