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 }