blob: ed22a8f82fbf495a319ce1d9a0802ef018bac853 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Command luciexe implements the cros_test_platform build logic.
//
// This is a luciexe binary as defined at
// https://godoc.org/go.chromium.org/luci/luciexe
//
// This binary is intended to slowly replace all the sub-commands of the
// cros_test_platform binary, eventually replacing the cros_test_platform
// recipe and Go binary.
package main
import (
"compress/zlib"
"context"
"flag"
"fmt"
"os"
"infra/cmd/cros_test_platform/luciexe/execute"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/luciexe/exe"
bbpb "go.chromium.org/luci/buildbucket/proto"
)
func main() {
exe.Run(luciEXEMain, exe.WithZlibCompression(zlib.BestCompression))
}
// The exe.MainFn entry point for this luciexe binary.
//
// At the moment, this binary replaces only the skylab-execute step
// of cros_test_platform.
func luciEXEMain(ctx context.Context, input *bbpb.Build, userArgs []string, send exe.BuildSender) (merr error) {
// crbug.com/1112514: The input Build Status is not currently specified in
// the luciexe protocol. bbagent sets it, but recipe_engine's sub_build()
// doesn't. Play safe.
input.Status = bbpb.Status_STARTED
send()
// All errors in skylab-execute step are interpreted as infrastructure
// failures by the recipe.
// Test failures and user errors are reported in other recipe steps.
//
// Thus,
// [1] All errors from this binary are tagged as INFRA_FAILURE
// [2] Non-INFRA_FAILUREs are suppressed (before they get to this top-level
// function)
defer func() {
merr = exe.InfraErrorTag.Apply(merr)
}()
ca, err := parseArgs(userArgs)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
flag.PrintDefaults()
return err
}
ctx = setupLogging(ctx)
if err := execute.Run(ctx, execute.Args{
InputPath: ca.InputPath,
OutputPath: ca.OutputPath,
SwarmingTaskID: os.Getenv("SWARMING_TASK_ID"),
Build: input,
Send: send,
}); err != nil {
logApplicationError(ctx, err)
return err
}
return nil
}
func setupLogging(ctx context.Context) context.Context {
return logging.SetLevel(ctx, logging.Info)
}
// logApplicationError logs the error returned to the entry function of an
// application.
func logApplicationError(ctx context.Context, err error) {
errors.Log(ctx, err)
// Also log to error stream, since logs are directed at the main output
// stream.
fmt.Fprintf(os.Stderr, "%s\n", err)
}
type cliArgs struct {
InputPath string
OutputPath string
}
func parseArgs(args []string) (cliArgs, error) {
ca := cliArgs{}
fs := flag.NewFlagSet("cros_test_platform", flag.ContinueOnError)
fs.StringVar(&ca.InputPath, "input_json", "", "Path to JSON ExecuteRequests to read.")
fs.StringVar(&ca.OutputPath, "output_json", "", "Path to JSON ExecuteResponses to write.")
if err := fs.Parse(args); err != nil {
return ca, err
}
errs := errors.NewMultiError()
if ca.InputPath == "" {
errs = append(errs, errors.Reason("-input_json not specified").Err())
}
if ca.OutputPath == "" {
errs = append(errs, errors.Reason("-output_json not specified").Err())
}
if errs.First() != nil {
return ca, errs
}
return ca, nil
}