github.com/bazelbuild/remote-apis-sdks@v0.0.0-20240425170053-8a36686a6350/go/cmd/remotetool/main.go (about)

     1  // Main package for the remotetool binary.
     2  //
     3  // This tool supports common debugging operations concerning remotely executed
     4  // actions:
     5  // 1. Download/upload a file or directory from/to remote cache by its digest.
     6  // 2. Display details of a remotely executed action.
     7  // 3. Download action results by the action digest.
     8  // 4. Re-execute remote action (with optional inputs override).
     9  //
    10  // Example (download an action result from remote action cache):
    11  //
    12  //	bazelisk run //go/cmd/remotetool -- \
    13  //	 --operation=download_action_result \
    14  //		--instance=$INSTANCE \
    15  //		--service remotebuildexecution.googleapis.com:443 \
    16  //		--alsologtostderr --v 1 \
    17  //		--credential_file $CRED_FILE \
    18  //		--digest=52a54724e6b3dff3bc44ef5dceb3aab5892f2fc7e37fce5aa6e16a7a266fbed6/147 \
    19  //		--path=`pwd`/tmp
    20  package main
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"flag"
    26  	"fmt"
    27  	"os"
    28  	"path"
    29  
    30  	"github.com/bazelbuild/remote-apis-sdks/go/pkg/outerr"
    31  	"github.com/bazelbuild/remote-apis-sdks/go/pkg/tool"
    32  
    33  	rflags "github.com/bazelbuild/remote-apis-sdks/go/pkg/flags"
    34  	log "github.com/golang/glog"
    35  )
    36  
    37  // OpType denotes the type of operation to perform.
    38  type OpType string
    39  
    40  const (
    41  	downloadActionResult OpType = "download_action_result"
    42  	showAction           OpType = "show_action"
    43  	downloadAction       OpType = "download_action"
    44  	downloadBlob         OpType = "download_blob"
    45  	downloadDir          OpType = "download_dir"
    46  	executeAction        OpType = "execute_action"
    47  	checkDeterminism     OpType = "check_determinism"
    48  	uploadBlob           OpType = "upload_blob"
    49  	uploadBlobV2         OpType = "upload_blob_v2"
    50  	uploadDir            OpType = "upload_dir"
    51  )
    52  
    53  var supportedOps = []OpType{
    54  	downloadActionResult,
    55  	showAction,
    56  	downloadAction,
    57  	downloadBlob,
    58  	downloadDir,
    59  	executeAction,
    60  	checkDeterminism,
    61  	uploadBlob,
    62  	uploadDir,
    63  }
    64  
    65  var (
    66  	operation    = flag.String("operation", "", fmt.Sprintf("Specifies the operation to perform. Supported values: %v", supportedOps))
    67  	digest       = flag.String("digest", "", "Digest in <digest/size_bytes> format.")
    68  	pathPrefix   = flag.String("path", "", "Path to which outputs should be downloaded to.")
    69  	overwrite    = flag.Bool("overwrite", false, "Overwrite the output path if it already exist.")
    70  	actionRoot   = flag.String("action_root", "", "For execute_action: the root of the action spec, containing ac.textproto (Action proto), cmd.textproto (Command proto), and input/ (root of the input tree).")
    71  	execAttempts = flag.Int("exec_attempts", 10, "For check_determinism: the number of times to remotely execute the action and check for mismatches.")
    72  	jsonOutput   = flag.String("json", "", "Path to output operation result as JSON. Currently supported for \"upload_dir\", and includes various upload metadata (see UploadStats).")
    73  	_            = flag.String("input_root", "", "Deprecated. Use action root instead.")
    74  )
    75  
    76  func main() {
    77  	flag.Usage = func() {
    78  		fmt.Fprintf(flag.CommandLine.Output(), "Usage: %v [-flags] -- --operation <op> arguments ...\n", path.Base(os.Args[0]))
    79  		flag.PrintDefaults()
    80  	}
    81  	flag.Parse()
    82  	if *operation == "" {
    83  		log.Exitf("--operation must be specified.")
    84  	}
    85  	if *execAttempts <= 0 {
    86  		log.Exitf("--exec_attempts must be >= 1.")
    87  	}
    88  
    89  	ctx := context.Background()
    90  	grpcClient, err := rflags.NewClientFromFlags(ctx)
    91  	if err != nil {
    92  		log.Exitf("error connecting to remote execution client: %v", err)
    93  	}
    94  	defer grpcClient.Close()
    95  	c := &tool.Client{GrpcClient: grpcClient}
    96  
    97  	switch OpType(*operation) {
    98  	case downloadActionResult:
    99  		if err := c.DownloadActionResult(ctx, getDigestFlag(), getPathFlag()); err != nil {
   100  			log.Exitf("error downloading action result for digest %v: %v", getDigestFlag(), err)
   101  		}
   102  
   103  	case downloadBlob:
   104  		res, err := c.DownloadBlob(ctx, getDigestFlag(), getPathFlag())
   105  		if err != nil {
   106  			log.Exitf("error downloading blob for digest %v: %v", getDigestFlag(), err)
   107  		}
   108  		os.Stdout.Write([]byte(res))
   109  
   110  	case downloadDir:
   111  		if err := c.DownloadDirectory(ctx, getDigestFlag(), getPathFlag()); err != nil {
   112  			log.Exitf("error downloading directory for digest %v: %v", getDigestFlag(), err)
   113  		}
   114  
   115  	case showAction:
   116  		res, err := c.ShowAction(ctx, getDigestFlag())
   117  		if err != nil {
   118  			log.Exitf("error fetching action %v: %v", getDigestFlag(), err)
   119  		}
   120  		os.Stdout.Write([]byte(res))
   121  
   122  	case downloadAction:
   123  		err := c.DownloadAction(ctx, getDigestFlag(), getPathFlag(), *overwrite)
   124  		if err != nil {
   125  			log.Exitf("error fetching action %v: %v", getDigestFlag(), err)
   126  		}
   127  		fmt.Printf("Action downloaded to %v\n", getPathFlag())
   128  
   129  	case executeAction:
   130  		if _, err := c.ExecuteAction(ctx, *digest, *actionRoot, getPathFlag(), outerr.SystemOutErr); err != nil {
   131  			log.Exitf("error executing action: %v", err)
   132  		}
   133  
   134  	case checkDeterminism:
   135  		if err := c.CheckDeterminism(ctx, *digest, *actionRoot, *execAttempts); err != nil {
   136  			log.Exitf("error checking determinism: %v", err)
   137  		}
   138  
   139  	case uploadBlob:
   140  		if err := c.UploadBlob(ctx, getPathFlag()); err != nil {
   141  			log.Exitf("error uploading blob for digest %v: %v", getDigestFlag(), err)
   142  		}
   143  
   144  	case uploadBlobV2:
   145  		if err := c.UploadBlobV2(ctx, getPathFlag()); err != nil {
   146  			log.Exitf("error uploading blob for digest %v: %v", getDigestFlag(), err)
   147  		}
   148  
   149  	case uploadDir:
   150  		us, err := c.UploadDirectory(ctx, getPathFlag())
   151  		if *jsonOutput != "" {
   152  			js, _ := json.MarshalIndent(us, "", "  ")
   153  			if *jsonOutput == "-" {
   154  				fmt.Printf("%s\n", js)
   155  			} else {
   156  				log.Infof("Outputting JSON results to %s", *jsonOutput)
   157  				if err := os.WriteFile(*jsonOutput, []byte(js), 0o666); err != nil {
   158  					log.Exitf("Error writing JSON output to file: %v", err)
   159  				}
   160  			}
   161  		}
   162  		if err != nil {
   163  			log.Exitf("error uploading directory for path %s: %v", getPathFlag(), err)
   164  		}
   165  
   166  	default:
   167  		log.Exitf("unsupported operation %v. Supported operations:\n%v", *operation, supportedOps)
   168  	}
   169  }
   170  
   171  func getDigestFlag() string {
   172  	if *digest == "" {
   173  		log.Exitf("--digest must be specified.")
   174  	}
   175  	return *digest
   176  }
   177  
   178  func getPathFlag() string {
   179  	if *pathPrefix == "" {
   180  		log.Exitf("--path must be specified.")
   181  	}
   182  	return *pathPrefix
   183  }