github.com/bazelbuild/remote-apis-sdks@v0.0.0-20240425170053-8a36686a6350/go/cmd/rexec/main.go (about) 1 // Main package for the rexec binary. 2 // 3 // This tool executes a command remotely, downloading the command outputs and propagating its 4 // stdout and stderr. 5 // 6 // Example usage: 7 // 8 // rexec --alsologtostderr --v 1 \ 9 // --service remotebuildexecution.googleapis.com:443 \ 10 // --instance $INSTANCE \ 11 // --credential_file $CRED_FILE \ 12 // --exec_root $HOME/example \ 13 // --platform container-image $CONTAINER \ 14 // --inputs a/hello,a/goodbye 15 // --working_directory foo/bar 16 // --output_files foo/bar/out 17 // -- /bin/bash -c 'cat hello goodbye > out' 18 package main 19 20 import ( 21 "context" 22 "flag" 23 "fmt" 24 "os" 25 "path" 26 27 "github.com/bazelbuild/remote-apis-sdks/go/pkg/command" 28 "github.com/bazelbuild/remote-apis-sdks/go/pkg/filemetadata" 29 "github.com/bazelbuild/remote-apis-sdks/go/pkg/moreflag" 30 "github.com/bazelbuild/remote-apis-sdks/go/pkg/outerr" 31 "github.com/bazelbuild/remote-apis-sdks/go/pkg/rexec" 32 33 rflags "github.com/bazelbuild/remote-apis-sdks/go/pkg/flags" 34 log "github.com/golang/glog" 35 ) 36 37 func initFlags(cmd *command.Command, opt *command.ExecutionOptions) { 38 flag.StringVar(&cmd.Identifiers.CommandID, "command_id", "", "An identifier for the command for debugging.") 39 flag.StringVar(&cmd.Identifiers.InvocationID, "invocation_id", "", "An identifier for a group of commands for debugging.") 40 flag.StringVar(&cmd.Identifiers.ToolName, "tool_name", "", "The name of the tool to associate with executed commands.") 41 flag.StringVar(&cmd.ExecRoot, "exec_root", "", "The exec root of the command. The path from which all inputs and outputs are defined relatively.") 42 flag.StringVar(&cmd.WorkingDir, "working_directory", "", "The working directory, relative to the exec root, for the command to run in. It must be a directory which exists in the input tree. If it is left empty, then the action is run in the exec root.") 43 flag.Var((*moreflag.StringListValue)(&cmd.InputSpec.Inputs), "inputs", "Comma-separated command input paths, relative to exec root.") 44 flag.Var((*moreflag.StringListValue)(&cmd.OutputFiles), "output_files", "Comma-separated command output file paths, relative to exec root.") 45 flag.Var((*moreflag.StringListValue)(&cmd.OutputDirs), "output_directories", "Comma-separated command output directory paths, relative to exec root.") 46 flag.DurationVar(&cmd.Timeout, "exec_timeout", 0, "Timeout for the command. Value of 0 means no timeout.") 47 flag.Var((*moreflag.StringMapValue)(&cmd.Platform), "platform", "Comma-separated key value pairs in the form key=value. This is used to identify remote platform settings like the docker image to use to run the command.") 48 flag.Var((*moreflag.StringMapValue)(&cmd.InputSpec.EnvironmentVariables), "environment_variables", "Environment variables to pass through to remote execution, as comma-separated key value pairs in the form key=value.") 49 flag.BoolVar(&opt.AcceptCached, "accept_cached", true, "Boolean indicating whether to accept remote cache hits.") 50 flag.BoolVar(&opt.DoNotCache, "do_not_cache", false, "Boolean indicating whether to skip caching the command result remotely.") 51 flag.BoolVar(&opt.DownloadOutputs, "download_outputs", true, "Boolean indicating whether to download outputs after the command is executed.") 52 flag.BoolVar(&opt.DownloadOutErr, "download_outerr", true, "Boolean indicating whether to download stdout and stderr after the command is executed.") 53 } 54 55 func main() { 56 cmd := &command.Command{InputSpec: &command.InputSpec{}, Identifiers: &command.Identifiers{}} 57 opt := &command.ExecutionOptions{} 58 initFlags(cmd, opt) 59 flag.Usage = func() { 60 fmt.Fprintf(flag.CommandLine.Output(), "Usage: %v [-flags] -- command arguments ...\n", path.Base(os.Args[0])) 61 flag.PrintDefaults() 62 } 63 flag.Parse() 64 cmd.Args = flag.Args() 65 if err := cmd.Validate(); err != nil { 66 flag.Usage() 67 log.Exitf("Invalid command provided: %v", err) 68 } 69 70 ctx := context.Background() 71 grpcClient, err := rflags.NewClientFromFlags(ctx) 72 if err != nil { 73 log.Exitf("error connecting to remote execution client: %v", err) 74 } 75 defer grpcClient.Close() 76 c := &rexec.Client{ 77 FileMetadataCache: filemetadata.NewNoopCache(), 78 GrpcClient: grpcClient, 79 } 80 res, _ := c.Run(ctx, cmd, opt, outerr.SystemOutErr) 81 switch res.Status { 82 case command.NonZeroExitResultStatus: 83 fmt.Fprintf(os.Stderr, "Remote action FAILED with exit code %d.\n", res.ExitCode) 84 case command.TimeoutResultStatus: 85 fmt.Fprintf(os.Stderr, "Remote action TIMED OUT after %0f seconds.\n", cmd.Timeout.Seconds()) 86 case command.InterruptedResultStatus: 87 fmt.Fprintf(os.Stderr, "Remote execution was interrupted.\n") 88 case command.RemoteErrorResultStatus: 89 fmt.Fprintf(os.Stderr, "Remote execution error: %v.\n", res.Err) 90 case command.LocalErrorResultStatus: 91 fmt.Fprintf(os.Stderr, "Local error: %v.\n", res.Err) 92 } 93 }