blob: 38c60ce6c738873e6a2dd72801e537858aa4a953 [file] [log] [blame]
package main
/*
Read and validate an AutoRoll config file.
*/
import (
"context"
"flag"
"fmt"
"io"
"io/ioutil"
"path/filepath"
"go.skia.org/infra/autoroll/go/config"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/sklog"
"go.skia.org/infra/go/util"
"go.skia.org/infra/task_driver/go/lib/os_steps"
"go.skia.org/infra/task_driver/go/td"
"google.golang.org/protobuf/encoding/prototext"
)
var (
// Required properties for this task.
projectId = flag.String("project_id", "", "ID of the Google Cloud project.")
taskId = flag.String("task_id", "", "ID of this task.")
taskName = flag.String("task_name", "", "Name of the task.")
workdir = flag.String("workdir", ".", "Working directory")
configFlag = flag.String("config", "", "Config file or dir of config files to validate.")
// Optional flags.
local = flag.Bool("local", false, "True if running locally (as opposed to on the bots)")
output = flag.String("o", "", "If provided, dump a JSON blob of step data to the given file. Prints to stdout if '-' is given.")
)
var (
// "constants"
chromiumServiceAccounts = []string{
"chromium-autoroll@skia-public.iam.gserviceaccount.com",
"chromium-release-autoroll@skia-public.iam.gserviceaccount.com",
"chromium-internal-autoroll@skia-corp.google.com.iam.gserviceaccount.com",
}
)
func validateConfig(ctx context.Context, f string) (string, error) {
var rollerName string
return rollerName, td.Do(ctx, td.Props(fmt.Sprintf("Validate %s", f)), func(ctx context.Context) error {
// Decode the config.
var cfg config.Config
if err := util.WithReadFile(f, func(f io.Reader) error {
b, err := ioutil.ReadAll(f)
if err != nil {
return skerr.Wrap(err)
}
if err := prototext.Unmarshal(b, &cfg); err != nil {
return skerr.Wrap(err)
}
return nil
}); err != nil {
td.Fatalf(ctx, "%s failed validation: %s", f, err)
}
// Validate the config.
if err := cfg.Validate(); err != nil {
return skerr.Wrap(err)
}
if util.In(cfg.ServiceAccount, chromiumServiceAccounts) {
gerrit := cfg.GetGerrit()
if gerrit != nil && gerrit.Config == config.GerritConfig_CHROMIUM {
if gerrit.Project != "emscripten-releases" && gerrit.Project != "infra/infra" {
return skerr.Fmt("Chromium rollers must use Gerrit config CHROMIUM_BOT_COMMIT")
}
}
}
rollerName = cfg.RollerName
return nil
})
}
func main() {
// Setup.
ctx := td.StartRun(projectId, taskId, taskName, output, local)
defer td.EndRun(ctx)
if *configFlag == "" {
td.Fatalf(ctx, "--config is required.")
}
// Gather files to validate.
configFiles := []string{}
f, err := os_steps.Stat(ctx, *configFlag)
if err != nil {
td.Fatal(ctx, err)
}
if f.IsDir() {
files, err := os_steps.ReadDir(ctx, *configFlag)
if err != nil {
td.Fatal(ctx, err)
}
for _, f := range files {
// Ignore subdirectories and file names starting with '.'
if !f.IsDir() && f.Name()[0] != '.' {
configFiles = append(configFiles, filepath.Join(*configFlag, f.Name()))
}
}
} else {
configFiles = append(configFiles, *configFlag)
}
// Validate the file(s).
rollers := make(map[string]string, len(configFiles))
for _, f := range configFiles {
rollerName, err := validateConfig(ctx, f)
if err != nil {
td.Fatalf(ctx, "%s failed validation: %s", f, err)
}
if otherFile, ok := rollers[rollerName]; ok {
td.Fatalf(ctx, "Roller %q is defined in both %s and %s", rollerName, f, otherFile)
}
rollers[rollerName] = f
}
sklog.Infof("Validated %d files.", len(configFiles))
}