blob: a9f6a3ea1bbc7cae8464a28f67f6e9a62464af4f [file] [log] [blame]
package cos
import (
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"cloud.google.com/go/compute/metadata"
log "github.com/golang/glog"
"github.com/pkg/errors"
"cos.googlesource.com/cos/tools.git/src/pkg/utils"
)
const (
cosToolsGCS = "cos-tools"
cosToolsGCSAsia = "cos-tools-asia"
cosToolsGCSEU = "cos-tools-eu"
kernelInfo = "kernel_info"
kernelSrcArchive = "kernel-src.tar.gz"
kernelHeaders = "kernel-headers.tgz"
toolchainURL = "toolchain_url"
toolchainArchive = "toolchain.tar.xz"
toolchainEnv = "toolchain_env"
crosKernelRepo = "https://chromium.googlesource.com/chromiumos/third_party/kernel"
)
// Map VM zone prefix to specific cos-tools bucket for geo-redundancy.
var cosToolsPrefixMap = map[string]string{
"us": cosToolsGCS,
"northamerica": cosToolsGCS,
"southamerica": cosToolsGCS,
"europe": cosToolsGCSEU,
"asia": cosToolsGCSAsia,
"australia": cosToolsGCSAsia,
}
// ArtifactsDownloader defines the interface to download COS artifacts.
type ArtifactsDownloader interface {
DownloadKernelSrc(destDir string) error
DownloadToolchainEnv(destDir string) error
DownloadToolchain(destDir string) error
DownloadKernelHeaders(destDir string) error
DownloadArtifact(destDir, artifact string) error
GetArtifact(artifact string) ([]byte, error)
}
// GCSDownloader is the struct downloading COS artifacts from GCS bucket.
type GCSDownloader struct {
envReader *EnvReader
gcsDownloadBucket string
gcsDownloadPrefix string
}
// NewGCSDownloader creates a GCSDownloader instance.
func NewGCSDownloader(e *EnvReader, bucket, prefix string) *GCSDownloader {
// If bucket is not set, use cos-tools, cos-tools-asia or cos-tools-eu
// according to the zone the VM is running in for geo-redundancy.
// If cannot fetch zone from metadata or get an unknown zone prefix,
// use cos-tools as the default GCS bucket.
if bucket == "" {
zone, err := metadata.Zone()
if err != nil {
log.Warningf("failed to get zone from metadata, will use 'gs://cos-tools' as artifact bucket, err: %v", err)
bucket = cosToolsGCS
} else {
zonePrefix := strings.Split(zone, "-")[0]
if geoBucket, found := cosToolsPrefixMap[zonePrefix]; found {
bucket = geoBucket
} else {
bucket = cosToolsGCS
}
}
}
// Use build number as the default GCS download prefix.
if prefix == "" {
prefix = e.BuildNumber()
}
return &GCSDownloader{e, bucket, prefix}
}
// DownloadKernelSrc downloads COS kernel sources to destination directory.
func (d *GCSDownloader) DownloadKernelSrc(destDir string) error {
return d.DownloadArtifact(destDir, kernelSrcArchive)
}
// DownloadToolchainEnv downloads toolchain compilation environment variables to destination directory.
func (d *GCSDownloader) DownloadToolchainEnv(destDir string) error {
return d.DownloadArtifact(destDir, toolchainEnv)
}
// DownloadToolchain downloads toolchain package to destination directory.
func (d *GCSDownloader) DownloadToolchain(destDir string) error {
return d.DownloadArtifact(destDir, toolchainArchive)
}
// DownloadKernelHeaders downloads COS kernel headers to destination directory.
func (d *GCSDownloader) DownloadKernelHeaders(destDir string) error {
return d.DownloadArtifact(destDir, kernelHeaders)
}
// GetArtifact gets an artifact from GCS buckets and returns its content.
func (d *GCSDownloader) GetArtifact(artifactPath string) ([]byte, error) {
tmpDir, err := ioutil.TempDir("", "tmp")
if err != nil {
return nil, errors.Wrap(err, "failed to create temp dir")
}
defer os.RemoveAll(tmpDir)
if err = d.DownloadArtifact(tmpDir, artifactPath); err != nil {
return nil, errors.Wrapf(err, "failed to download artifact %s", artifactPath)
}
content, err := ioutil.ReadFile(filepath.Join(tmpDir, filepath.Base(artifactPath)))
if err != nil {
return nil, errors.Wrapf(err, "failed to read file %s", filepath.Join(tmpDir, artifactPath))
}
return content, nil
}
// DownloadArtifact downloads an artifact from the GCS prefix configured in GCSDownloader.
func (d *GCSDownloader) DownloadArtifact(destDir, artifactPath string) error {
gcsPath := path.Join(d.gcsDownloadPrefix, artifactPath)
if err := utils.DownloadFromGCS(destDir, d.gcsDownloadBucket, gcsPath); err != nil {
return errors.Errorf("failed to download %s from gs://%s/%s : %s", artifactPath, d.gcsDownloadBucket, gcsPath, err)
}
return nil
}