Passing Bazel metadata in gRPC headers.

TESTED=unit tests
RELNOTES: none
PiperOrigin-RevId: 169395919
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
index 774a580..74a8afc 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
@@ -32,6 +32,7 @@
 import com.google.devtools.remoteexecution.v1test.ActionResult;
 import com.google.devtools.remoteexecution.v1test.Command;
 import com.google.devtools.remoteexecution.v1test.Platform;
+import io.grpc.Context;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.NoSuchElementException;
@@ -54,6 +55,8 @@
   private final Platform platform;
 
   private final RemoteActionCache remoteCache;
+  private final String buildRequestId;
+  private final String commandId;
   private final boolean verboseFailures;
 
   @Nullable private final Reporter cmdlineReporter;
@@ -61,14 +64,22 @@
   // Used to ensure that a warning is reported only once.
   private final AtomicBoolean warningReported = new AtomicBoolean();
 
-  RemoteSpawnCache(Path execRoot, RemoteOptions options, RemoteActionCache remoteCache,
-      boolean verboseFailures, @Nullable Reporter cmdlineReporter) {
+  RemoteSpawnCache(
+      Path execRoot,
+      RemoteOptions options,
+      RemoteActionCache remoteCache,
+      String buildRequestId,
+      String commandId,
+      boolean verboseFailures,
+      @Nullable Reporter cmdlineReporter) {
     this.execRoot = execRoot;
     this.options = options;
     this.platform = options.parseRemotePlatformOverride();
     this.remoteCache = remoteCache;
     this.verboseFailures = verboseFailures;
     this.cmdlineReporter = cmdlineReporter;
+    this.buildRequestId = buildRequestId;
+    this.commandId = commandId;
   }
 
   @Override
@@ -91,61 +102,70 @@
 
     // Look up action cache, and reuse the action output if it is found.
     final ActionKey actionKey = Digests.computeActionKey(action);
-    ActionResult result =
-        this.options.remoteAcceptCached ? remoteCache.getCachedActionResult(actionKey) : null;
-    if (result != null) {
-      // We don't cache failed actions, so we know the outputs exist.
-      // For now, download all outputs locally; in the future, we can reuse the digests to
-      // just update the TreeNodeRepository and continue the build.
-      try {
-        remoteCache.download(result, execRoot, policy.getFileOutErr());
-        SpawnResult spawnResult = new SpawnResult.Builder()
-            .setStatus(Status.SUCCESS)
-            .setExitCode(result.getExitCode())
-            .build();
-        return SpawnCache.success(spawnResult);
-      } catch (CacheNotFoundException e) {
-        // There's a cache miss. Fall back to local execution.
+    Context withMetadata =
+        TracingMetadataUtils.contextWithMetadata(buildRequestId, commandId, actionKey);
+    // Metadata will be available in context.current() until we detach.
+    // This is done via a thread-local variable.
+    Context previous = withMetadata.attach();
+    try {
+      ActionResult result =
+          this.options.remoteAcceptCached ? remoteCache.getCachedActionResult(actionKey) : null;
+      if (result != null) {
+        // We don't cache failed actions, so we know the outputs exist.
+        // For now, download all outputs locally; in the future, we can reuse the digests to
+        // just update the TreeNodeRepository and continue the build.
+        try {
+          remoteCache.download(result, execRoot, policy.getFileOutErr());
+          SpawnResult spawnResult =
+              new SpawnResult.Builder()
+                  .setStatus(Status.SUCCESS)
+                  .setExitCode(result.getExitCode())
+                  .build();
+          return SpawnCache.success(spawnResult);
+        } catch (CacheNotFoundException e) {
+          // There's a cache miss. Fall back to local execution.
+        }
       }
-    }
-    if (options.remoteUploadLocalResults) {
-      return new CacheHandle() {
-        @Override
-        public boolean hasResult() {
-          return false;
-        }
+      if (options.remoteUploadLocalResults) {
+        return new CacheHandle() {
+          @Override
+          public boolean hasResult() {
+            return false;
+          }
 
-        @Override
-        public SpawnResult getResult() {
-          throw new NoSuchElementException();
-        }
+          @Override
+          public SpawnResult getResult() {
+            throw new NoSuchElementException();
+          }
 
-        @Override
-        public boolean willStore() {
-          return true;
-        }
+          @Override
+          public boolean willStore() {
+            return true;
+          }
 
-        @Override
-        public void store(SpawnResult result, Collection<Path> files)
-            throws InterruptedException, IOException {
-          try {
+          @Override
+          public void store(SpawnResult result, Collection<Path> files)
+              throws InterruptedException, IOException {
             boolean uploadAction = Status.SUCCESS.equals(result.status()) && result.exitCode() == 0;
-            remoteCache.upload(actionKey, execRoot, files, policy.getFileOutErr(), uploadAction);
-          } catch (IOException e) {
-            if (verboseFailures) {
-              report(Event.debug("Upload to remote cache failed: " + e.getMessage()));
-            } else {
-              reportOnce(Event.warn("Some artifacts failed be uploaded to the remote cache."));
+            try {
+              remoteCache.upload(actionKey, execRoot, files, policy.getFileOutErr(), uploadAction);
+            } catch (IOException e) {
+              if (verboseFailures) {
+                report(Event.debug("Upload to remote cache failed: " + e.getMessage()));
+              } else {
+                reportOnce(Event.warn("Some artifacts failed be uploaded to the remote cache."));
+              }
             }
           }
-        }
 
-        @Override
-        public void close() {
-        }
-      };
-    } else {
-      return SpawnCache.NO_RESULT_NO_STORE;
+          @Override
+          public void close() {}
+        };
+      } else {
+        return SpawnCache.NO_RESULT_NO_STORE;
+      }
+    } finally {
+      withMetadata.detach(previous);
     }
   }