blob: 86a4b28090a598d105f20e2302489c7b6565c9f0 [file] [log] [blame]
// Copyright 2020 The LUCI Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package updater
import (
"context"
"io/ioutil"
"path/filepath"
"cloud.google.com/go/storage"
"google.golang.org/protobuf/encoding/protojson"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/common/sync/parallel"
"infra/tools/dirmd"
dirmdpb "infra/tools/dirmd/proto"
)
var includedSubRepos = []string{
"native_client",
"third_party/angle",
"third_party/catapult",
"third_party/cros_system_api",
"third_party/dawn",
"third_party/depot_tools",
"third_party/devtools-frontend",
"third_party/devtools-frontend/src/front_end/third_party/axe-core",
"third_party/devtools-frontend/src/third_party/pyjson5",
"third_party/ffmpeg",
"third_party/icu",
"third_party/libjpeg_turbo",
"third_party/libsrtp",
"third_party/libyuv",
"third_party/lss",
"third_party/nasm",
"third_party/pdfium",
"third_party/perfetto",
"third_party/skia",
"third_party/swiftshader",
"third_party/vulkan_memory_allocator",
"third_party/vulkan-deps",
"third_party/webrtc",
"v8",
}
// Updater computed metadata from a Chromium checkout and uploads it to GCS.
type Updater struct {
// ChromiumCheckout is a path to chromium/src.git checkout.
ChromiumCheckout string
// GCSBucket is the destination bucket for metadata.
GCSBucket *storage.BucketHandle
// LegacyGSBucket is the destination bucket for metadata in legacy format.
GCSBucketLegacy *storage.BucketHandle
// OutDir is a path to the directory where to write output files.
OutDir string
}
// Run updates the metadata stored in GCS.
func (u *Updater) Run(ctx context.Context) error {
return parallel.FanOutIn(func(work chan<- func() error) {
work <- func() error {
return u.run(ctx)
}
work <- func() error {
return u.runLegacyFull(ctx)
}
})
}
func (u *Updater) run(ctx context.Context) error {
mapping, err := u.readMapping(ctx, dirmdpb.MappingForm_COMPUTED)
if err != nil {
return err
}
// Write in computed form.
err = parallel.FanOutIn(func(work chan<- func() error) {
// Write in legacy format.
work <- func() error {
return u.writeMapping(ctx, "component_map.json", mapping, false)
}
// Write in new format.
work <- func() error {
return u.writeMapping(ctx, "metadata_computed.json", mapping, true)
}
})
if err != nil {
return err
}
// Write in reduced form.
if err := mapping.Reduce(); err != nil {
return err
}
return u.writeMapping(ctx, "metadata_reduced.json", mapping, true)
}
func (u *Updater) runLegacyFull(ctx context.Context) error {
mapping, err := u.readMapping(ctx, dirmdpb.MappingForm_FULL)
if err != nil {
return err
}
return u.writeMapping(ctx, "component_map_subdirs.json", mapping, false)
}
func (u *Updater) readMapping(ctx context.Context, form dirmdpb.MappingForm) (*dirmd.Mapping, error) {
dirs := make([]string, 1, 1+len(includedSubRepos))
dirs[0] = u.ChromiumCheckout
for _, subRepo := range includedSubRepos {
dirs = append(dirs, filepath.Join(u.ChromiumCheckout, filepath.FromSlash(subRepo)))
}
return dirmd.ReadMapping(ctx, form, dirs...)
}
func (u *Updater) writeMapping(ctx context.Context, name string, mapping *dirmd.Mapping, modernFormat bool) error {
var contents []byte
var bucket *storage.BucketHandle
if modernFormat {
bucket = u.GCSBucket
var err error
if contents, err = protojson.Marshal(mapping.Proto()); err != nil {
return err
}
} else {
bucket = u.GCSBucketLegacy
contents = toLegacyFormat(mapping)
}
if u.OutDir != "" {
if err := u.writeOutputFile(ctx, name, contents); err != nil {
return err
}
}
if bucket != nil {
if err := upload(ctx, bucket.Object(name), contents); err != nil {
return errors.Annotate(err, "failed to upload to GCS").Err()
}
}
return nil
}
func (u *Updater) writeOutputFile(ctx context.Context, name string, data []byte) error {
fullPath := filepath.Join(u.OutDir, name)
if err := ioutil.WriteFile(fullPath, data, 0666); err != nil {
return err
}
logging.Infof(ctx, "wrote %s", fullPath)
return nil
}
func upload(ctx context.Context, obj *storage.ObjectHandle, data []byte) error {
ctx, cancel := context.WithCancel(ctx)
w := obj.NewWriter(ctx)
defer func() {
w.Close()
cancel()
}()
w.PredefinedACL = "publicRead"
if _, err := w.Write(data); err != nil {
cancel() // cancel writing before closing
return err
}
logging.Infof(ctx, "wrote gs://%s/%s", obj.BucketName(), obj.ObjectName())
return nil
}