| package gcs | 
 |  | 
 | import ( | 
 | 	"context" | 
 | 	"crypto/sha1" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"os" | 
 | 	"path" | 
 | 	"runtime" | 
 |  | 
 | 	"cloud.google.com/go/storage" | 
 | 	"go.skia.org/infra/go/skerr" | 
 | 	"go.skia.org/infra/go/sklog" | 
 | 	"go.skia.org/infra/go/util" | 
 | ) | 
 |  | 
 | // DownloadHelper provides convenience methods for downloading binaries by SHA1 | 
 | // sum. | 
 | type DownloadHelper struct { | 
 | 	bucket  string | 
 | 	s       *storage.Client | 
 | 	subdir  string | 
 | 	workdir string | 
 | } | 
 |  | 
 | // NewDownloadHelper returns a DownloadHelper instance. | 
 | func NewDownloadHelper(s *storage.Client, gsBucket, gsSubdir, workdir string) *DownloadHelper { | 
 | 	return &DownloadHelper{ | 
 | 		bucket:  gsBucket, | 
 | 		s:       s, | 
 | 		subdir:  gsSubdir, | 
 | 		workdir: workdir, | 
 | 	} | 
 | } | 
 |  | 
 | // Download downloads the given binary from Google Storage. | 
 | func (d *DownloadHelper) Download(name, hash string) error { | 
 | 	sklog.Infof("Downloading new binary for %s...", name) | 
 | 	filepath := path.Join(d.workdir, name) | 
 | 	object := hash | 
 | 	if d.subdir != "" { | 
 | 		object = d.subdir + "/" + object | 
 | 	} | 
 | 	resp, err := d.s.Bucket(d.bucket).Object(object).NewReader(context.Background()) | 
 | 	if err != nil { | 
 | 		return skerr.Wrapf(err, "Download helper can't get reader for %s", name) | 
 | 	} | 
 | 	if err := util.WithWriteFile(filepath, func(w io.Writer) error { | 
 | 		_, err := io.Copy(w, resp) | 
 | 		return skerr.Wrap(err) | 
 | 	}); err != nil { | 
 | 		return skerr.Wrapf(err, "Download helper failed to download %s", name) | 
 | 	} | 
 | 	if runtime.GOOS != "windows" { | 
 | 		return skerr.Wrap(os.Chmod(filepath, 0755)) | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // MaybeDownload downloads the given binary from Google Storage if necessary. | 
 | func (d *DownloadHelper) MaybeDownload(name, hash string) error { | 
 | 	filepath := path.Join(d.workdir, name) | 
 | 	info, err := os.Stat(filepath) | 
 | 	if err != nil { | 
 | 		if os.IsNotExist(err) { | 
 | 			return d.Download(name, hash) | 
 | 		} else { | 
 | 			return skerr.Wrapf(err, "Failed to stat %s", filepath) | 
 | 		} | 
 | 	} | 
 | 	info, err = os.Stat(filepath) | 
 | 	if err != nil { | 
 | 		return skerr.Wrapf(err, "Failed to stat %s", filepath) | 
 | 	} | 
 | 	if info.Mode() != 0755 { | 
 | 		sklog.Infof("Binary %s is not executable.", filepath) | 
 | 		return d.Download(name, hash) | 
 | 	} | 
 |  | 
 | 	var contents []byte | 
 | 	contents, err = os.ReadFile(filepath) | 
 | 	if err != nil { | 
 | 		return skerr.Wrapf(err, "Failed to read %s", filepath) | 
 | 	} | 
 | 	sha1sum := sha1.Sum(contents) | 
 | 	sha1str := fmt.Sprintf("%x", sha1sum) | 
 | 	if sha1str != hash { | 
 | 		sklog.Infof("Binary %s is out of date:\nExpect: %s\nGot:    %s", filepath, hash, sha1str) | 
 | 		return d.Download(name, hash) | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // Close should be called when finished with the DownloadHelper. | 
 | func (d *DownloadHelper) Close() error { | 
 | 	return d.s.Close() | 
 | } |