blob: bf7aa76ae0d54219b8c45507c504d404b7d3f7f7 [file] [log] [blame]
buchgr9f7edd72017-07-14 12:58:50 +02001// Copyright 2017 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.remote;
15
16import static com.google.common.truth.Truth.assertThat;
tomlu09fe0622018-06-19 12:55:39 -070017import static java.nio.charset.StandardCharsets.ISO_8859_1;
michajlo660d17f2020-03-27 09:01:57 -070018import static org.junit.Assert.assertThrows;
Googler00cd2622019-05-29 07:47:32 -070019import static org.mockito.ArgumentMatchers.any;
buchgrd480c5f2019-04-03 00:53:34 -070020import static org.mockito.ArgumentMatchers.anyCollection;
Googlerf11ea6f2020-02-04 09:43:01 -080021import static org.mockito.ArgumentMatchers.eq;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020022import static org.mockito.Mockito.doNothing;
23import static org.mockito.Mockito.doThrow;
Chi Wange8d356d2020-08-14 00:27:19 -070024import static org.mockito.Mockito.inOrder;
25import static org.mockito.Mockito.mock;
buchgr9f7edd72017-07-14 12:58:50 +020026import static org.mockito.Mockito.never;
Jakob Buchgraber562fcf92017-07-27 12:51:13 +020027import static org.mockito.Mockito.spy;
George Gensureda146d22019-03-26 03:17:32 -070028import static org.mockito.Mockito.times;
buchgr9f7edd72017-07-14 12:58:50 +020029import static org.mockito.Mockito.verify;
Benjamin Petersonf1570532019-01-24 07:14:18 -080030import static org.mockito.Mockito.verifyNoMoreInteractions;
buchgr9f7edd72017-07-14 12:58:50 +020031import static org.mockito.Mockito.when;
32
olaolaf0aa55d2018-08-16 08:51:06 -070033import build.bazel.remote.execution.v2.ActionResult;
olaolaf0aa55d2018-08-16 08:51:06 -070034import build.bazel.remote.execution.v2.Digest;
Chi Wange8d356d2020-08-14 00:27:19 -070035import build.bazel.remote.execution.v2.ExecuteOperationMetadata;
olaolaf0aa55d2018-08-16 08:51:06 -070036import build.bazel.remote.execution.v2.ExecuteRequest;
37import build.bazel.remote.execution.v2.ExecuteResponse;
George Gensure7aa74982020-04-01 00:54:15 -070038import build.bazel.remote.execution.v2.ExecutedActionMetadata;
Chi Wange8d356d2020-08-14 00:27:19 -070039import build.bazel.remote.execution.v2.ExecutionStage.Value;
olaolaf0aa55d2018-08-16 08:51:06 -070040import build.bazel.remote.execution.v2.LogFile;
schmittde9b9552020-01-13 10:44:29 -080041import com.google.common.collect.ClassToInstanceMap;
42import com.google.common.collect.ImmutableClassToInstanceMap;
buchgr9f7edd72017-07-14 12:58:50 +020043import com.google.common.collect.ImmutableList;
44import com.google.common.collect.ImmutableMap;
Jakob Buchgraber50c10042019-04-11 02:11:19 -070045import com.google.common.collect.ImmutableSet;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020046import com.google.common.eventbus.EventBus;
tomlu09fe0622018-06-19 12:55:39 -070047import com.google.common.io.ByteStreams;
George Gensuref600b692018-07-10 14:13:50 -070048import com.google.common.util.concurrent.ListeningScheduledExecutorService;
49import com.google.common.util.concurrent.MoreExecutors;
buchgrff008f42018-06-02 14:13:43 -070050import com.google.common.util.concurrent.SettableFuture;
schmittde9b9552020-01-13 10:44:29 -080051import com.google.devtools.build.lib.actions.ActionContext;
buchgr9f7edd72017-07-14 12:58:50 +020052import com.google.devtools.build.lib.actions.ActionInput;
buchgrd480c5f2019-04-03 00:53:34 -070053import com.google.devtools.build.lib.actions.Artifact;
Jakob Buchgraber50c10042019-04-11 02:11:19 -070054import com.google.devtools.build.lib.actions.ArtifactRoot;
tomlu09fe0622018-06-19 12:55:39 -070055import com.google.devtools.build.lib.actions.CommandLines.ParamFileActionInput;
buchgr9f7edd72017-07-14 12:58:50 +020056import com.google.devtools.build.lib.actions.ExecutionRequirements;
tomlu09fe0622018-06-19 12:55:39 -070057import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
buchgr9f7edd72017-07-14 12:58:50 +020058import com.google.devtools.build.lib.actions.ResourceSet;
59import com.google.devtools.build.lib.actions.SimpleSpawn;
60import com.google.devtools.build.lib.actions.Spawn;
George Gensure7aa74982020-04-01 00:54:15 -070061import com.google.devtools.build.lib.actions.SpawnMetrics;
rupertsda40fbf2017-09-22 05:59:42 +020062import com.google.devtools.build.lib.actions.SpawnResult;
63import com.google.devtools.build.lib.actions.SpawnResult.Status;
janakraea05602019-05-22 15:41:29 -070064import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
buchgr559a07d2017-11-30 11:09:35 -080065import com.google.devtools.build.lib.clock.JavaClock;
ulfjacke4cca142020-01-08 04:44:40 -080066import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
67import com.google.devtools.build.lib.collect.nestedset.Order;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020068import com.google.devtools.build.lib.events.Event;
69import com.google.devtools.build.lib.events.EventKind;
70import com.google.devtools.build.lib.events.Reporter;
71import com.google.devtools.build.lib.events.StoredEventHandler;
schmittde9b9552020-01-13 10:44:29 -080072import com.google.devtools.build.lib.exec.AbstractSpawnStrategy;
tomlu09fe0622018-06-19 12:55:39 -070073import com.google.devtools.build.lib.exec.ExecutionOptions;
schmittde9b9552020-01-13 10:44:29 -080074import com.google.devtools.build.lib.exec.RemoteLocalFallbackRegistry;
buchgr9f7edd72017-07-14 12:58:50 +020075import com.google.devtools.build.lib.exec.SpawnRunner;
Chi Wange8d356d2020-08-14 00:27:19 -070076import com.google.devtools.build.lib.exec.SpawnRunner.ProgressStatus;
tomlu29e306d2018-04-19 05:41:44 -070077import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext;
buchgr9f7edd72017-07-14 12:58:50 +020078import com.google.devtools.build.lib.exec.util.FakeOwner;
Jakob Buchgraber18462692019-11-06 04:34:16 -080079import com.google.devtools.build.lib.remote.common.CacheNotFoundException;
Chi Wang09d8f142020-10-30 01:03:16 -070080import com.google.devtools.build.lib.remote.common.OperationObserver;
Jakob Buchgraber60566092019-11-11 06:28:22 -080081import com.google.devtools.build.lib.remote.common.RemoteCacheClient.ActionKey;
Chi Wang09d8f142020-10-30 01:03:16 -070082import com.google.devtools.build.lib.remote.common.RemoteExecutionClient;
Jakob Buchgraber75b7ed42019-03-27 10:27:13 -070083import com.google.devtools.build.lib.remote.options.RemoteOptions;
buchgrd480c5f2019-04-03 00:53:34 -070084import com.google.devtools.build.lib.remote.options.RemoteOutputsMode;
Googler922d1e62018-03-05 14:49:00 -080085import com.google.devtools.build.lib.remote.util.DigestUtil;
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -070086import com.google.devtools.build.lib.remote.util.FakeSpawnExecutionContext;
michajlo0f99c3c2020-03-09 16:07:50 -070087import com.google.devtools.build.lib.testutil.TestUtils;
buchgra6591352017-09-01 13:14:19 +020088import com.google.devtools.build.lib.util.ExitCode;
buchgr9f7edd72017-07-14 12:58:50 +020089import com.google.devtools.build.lib.util.io.FileOutErr;
ccalvarinbda12a12018-06-21 18:57:26 -070090import com.google.devtools.build.lib.vfs.DigestHashFunction;
buchgr9f7edd72017-07-14 12:58:50 +020091import com.google.devtools.build.lib.vfs.FileSystem;
92import com.google.devtools.build.lib.vfs.FileSystemUtils;
93import com.google.devtools.build.lib.vfs.Path;
94import com.google.devtools.build.lib.vfs.PathFragment;
95import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
96import com.google.devtools.common.options.Options;
Chi Wange8d356d2020-08-14 00:27:19 -070097import com.google.longrunning.Operation;
98import com.google.protobuf.Any;
George Gensure7aa74982020-04-01 00:54:15 -070099import com.google.protobuf.Timestamp;
100import com.google.protobuf.util.Durations;
101import com.google.protobuf.util.Timestamps;
olaola2732df02018-03-16 08:48:13 -0700102import com.google.rpc.Code;
buchgr9f7edd72017-07-14 12:58:50 +0200103import java.io.IOException;
tomlu09fe0622018-06-19 12:55:39 -0700104import java.io.InputStream;
105import java.nio.charset.StandardCharsets;
George Gensure7aa74982020-04-01 00:54:15 -0700106import java.time.Duration;
George Gensureda146d22019-03-26 03:17:32 -0700107import java.util.List;
George Gensuref600b692018-07-10 14:13:50 -0700108import java.util.concurrent.Executors;
michajlo0f99c3c2020-03-09 16:07:50 -0700109import java.util.concurrent.TimeUnit;
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700110import javax.annotation.Nullable;
michajlo0f99c3c2020-03-09 16:07:50 -0700111import org.junit.After;
buchgr9f7edd72017-07-14 12:58:50 +0200112import org.junit.Before;
113import org.junit.Test;
114import org.junit.runner.RunWith;
115import org.junit.runners.JUnit4;
116import org.mockito.ArgumentCaptor;
Chi Wange8d356d2020-08-14 00:27:19 -0700117import org.mockito.InOrder;
buchgr9f7edd72017-07-14 12:58:50 +0200118import org.mockito.Mock;
Jakob Buchgraber562fcf92017-07-27 12:51:13 +0200119import org.mockito.Mockito;
buchgr9f7edd72017-07-14 12:58:50 +0200120import org.mockito.MockitoAnnotations;
121
122/** Tests for {@link com.google.devtools.build.lib.remote.RemoteSpawnRunner} */
123@RunWith(JUnit4.class)
124public class RemoteSpawnRunnerTest {
125
126 private static final ImmutableMap<String, String> NO_CACHE =
127 ImmutableMap.of(ExecutionRequirements.NO_CACHE, "");
michajlo0f99c3c2020-03-09 16:07:50 -0700128 private ListeningScheduledExecutorService retryService;
buchgr9f7edd72017-07-14 12:58:50 +0200129
130 private Path execRoot;
olaolabf326fa2018-03-21 11:22:12 -0700131 private Path logDir;
buchgr559a07d2017-11-30 11:09:35 -0800132 private DigestUtil digestUtil;
buchgr9f7edd72017-07-14 12:58:50 +0200133 private FakeActionInputFileCache fakeFileCache;
134 private FileOutErr outErr;
135
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700136 private RemoteOptions remoteOptions;
George Gensuref600b692018-07-10 14:13:50 -0700137
Jakob Buchgraber60566092019-11-11 06:28:22 -0800138 @Mock private RemoteExecutionCache cache;
buchgr9f7edd72017-07-14 12:58:50 +0200139
Chi Wang09d8f142020-10-30 01:03:16 -0700140 @Mock private RemoteExecutionClient executor;
buchgr9f7edd72017-07-14 12:58:50 +0200141
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700142 @Mock private SpawnRunner localRunner;
buchgr9f7edd72017-07-14 12:58:50 +0200143
olaolabf326fa2018-03-21 11:22:12 -0700144 // The action key of the Spawn returned by newSimpleSpawn().
145 private final String simpleActionId =
146 "eb45b20cc979d504f96b9efc9a08c48103c6f017afa09c0df5c70a5f92a98ea8";
147
buchgr9f7edd72017-07-14 12:58:50 +0200148 @Before
149 public final void setUp() throws Exception {
150 MockitoAnnotations.initMocks(this);
ccalvarinbda12a12018-06-21 18:57:26 -0700151 digestUtil = new DigestUtil(DigestHashFunction.SHA256);
152 FileSystem fs = new InMemoryFileSystem(new JavaClock(), DigestHashFunction.SHA256);
buchgr9f7edd72017-07-14 12:58:50 +0200153 execRoot = fs.getPath("/exec/root");
olaolabf326fa2018-03-21 11:22:12 -0700154 logDir = fs.getPath("/server-logs");
buchgr9f7edd72017-07-14 12:58:50 +0200155 FileSystemUtils.createDirectoryAndParents(execRoot);
156 fakeFileCache = new FakeActionInputFileCache(execRoot);
157
158 Path stdout = fs.getPath("/tmp/stdout");
159 Path stderr = fs.getPath("/tmp/stderr");
160 FileSystemUtils.createDirectoryAndParents(stdout.getParentDirectory());
161 FileSystemUtils.createDirectoryAndParents(stderr.getParentDirectory());
162 outErr = new FileOutErr(stdout, stderr);
George Gensuref600b692018-07-10 14:13:50 -0700163
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700164 remoteOptions = Options.getDefaults(RemoteOptions.class);
michajlo0f99c3c2020-03-09 16:07:50 -0700165
166 retryService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(1));
George Gensuref600b692018-07-10 14:13:50 -0700167 }
168
michajlo0f99c3c2020-03-09 16:07:50 -0700169 @After
170 public void afterEverything() throws InterruptedException {
George Gensuref600b692018-07-10 14:13:50 -0700171 retryService.shutdownNow();
michajlo0f99c3c2020-03-09 16:07:50 -0700172 retryService.awaitTermination(TestUtils.WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
buchgr9f7edd72017-07-14 12:58:50 +0200173 }
174
175 @Test
buchgr9f7edd72017-07-14 12:58:50 +0200176 public void nonCachableSpawnsShouldNotBeCached_remote() throws Exception {
olaolaa22d0e92017-12-11 07:53:15 -0800177 // Test that if a spawn is marked "NO_CACHE" then it's not fetched from a remote cache.
178 // It should be executed remotely, but marked non-cacheable to remote execution, so that
179 // the action result is not saved in the remote cache.
buchgr9f7edd72017-07-14 12:58:50 +0200180
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700181 remoteOptions.remoteAcceptCached = true;
182 remoteOptions.remoteLocalFallback = false;
183 remoteOptions.remoteUploadLocalResults = true;
184 remoteOptions.remoteResultCachePriority = 1;
185 remoteOptions.remoteExecutionPriority = 2;
buchgr9f7edd72017-07-14 12:58:50 +0200186
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700187 RemoteSpawnRunner runner = newSpawnRunner();
buchgr9f7edd72017-07-14 12:58:50 +0200188
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700189 ExecuteResponse succeeded =
190 ExecuteResponse.newBuilder()
191 .setResult(ActionResult.newBuilder().setExitCode(0).build())
192 .build();
Chi Wang09d8f142020-10-30 01:03:16 -0700193 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700194 .thenReturn(succeeded);
buchgr9f7edd72017-07-14 12:58:50 +0200195
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700196 Spawn spawn = simpleSpawnWithExecutionInfo(NO_CACHE);
schmittde9b9552020-01-13 10:44:29 -0800197 SpawnExecutionContext policy = getSpawnContext(spawn);
buchgr9f7edd72017-07-14 12:58:50 +0200198
199 runner.exec(spawn, policy);
200
201 ArgumentCaptor<ExecuteRequest> requestCaptor = ArgumentCaptor.forClass(ExecuteRequest.class);
Chi Wang09d8f142020-10-30 01:03:16 -0700202 verify(executor).executeRemotely(requestCaptor.capture(), any(OperationObserver.class));
buchgr9f7edd72017-07-14 12:58:50 +0200203 assertThat(requestCaptor.getValue().getSkipCacheLookup()).isTrue();
olaolafea6ab42018-12-20 08:47:00 -0800204 assertThat(requestCaptor.getValue().getResultsCachePolicy().getPriority()).isEqualTo(1);
olaola5c97dcb2018-12-21 10:40:43 -0800205 assertThat(requestCaptor.getValue().getExecutionPolicy().getPriority()).isEqualTo(2);
olaolaf0aa55d2018-08-16 08:51:06 -0700206 // TODO(olaola): verify that the uploaded action has the doNotCache set.
buchgr9f7edd72017-07-14 12:58:50 +0200207
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700208 verify(cache, never())
209 .downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false));
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700210 verify(cache, never()).upload(any(), any(), any(), any(), any(), any());
George Gensure7aa74982020-04-01 00:54:15 -0700211 verifyNoMoreInteractions(localRunner);
buchgr9f7edd72017-07-14 12:58:50 +0200212 }
213
schmittde9b9552020-01-13 10:44:29 -0800214 private FakeSpawnExecutionContext getSpawnContext(Spawn spawn) {
janakr619dcf82020-09-22 14:08:59 -0700215 AbstractSpawnStrategy fakeLocalStrategy =
216 new AbstractSpawnStrategy(execRoot, localRunner, /*verboseFailures=*/ true) {};
schmittde9b9552020-01-13 10:44:29 -0800217 ClassToInstanceMap<ActionContext> actionContextRegistry =
218 ImmutableClassToInstanceMap.of(RemoteLocalFallbackRegistry.class, () -> fakeLocalStrategy);
219 return new FakeSpawnExecutionContext(
220 spawn, fakeFileCache, execRoot, outErr, actionContextRegistry);
221 }
222
buchgr9f7edd72017-07-14 12:58:50 +0200223 @Test
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700224 public void nonCachableSpawnsShouldNotBeCached_localFallback() throws Exception {
225 // Test that if a non-cachable spawn is executed locally due to the local fallback,
226 // that its result is not uploaded to the remote cache.
buchgr9f7edd72017-07-14 12:58:50 +0200227
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700228 remoteOptions.remoteAcceptCached = true;
229 remoteOptions.remoteLocalFallback = true;
230 remoteOptions.remoteUploadLocalResults = true;
buchgr9f7edd72017-07-14 12:58:50 +0200231
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700232 RemoteSpawnRunner runner = newSpawnRunner();
buchgr9f7edd72017-07-14 12:58:50 +0200233
234 // Throw an IOException to trigger the local fallback.
Chi Wang09d8f142020-10-30 01:03:16 -0700235 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700236 .thenThrow(IOException.class);
buchgr9f7edd72017-07-14 12:58:50 +0200237
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700238 Spawn spawn = simpleSpawnWithExecutionInfo(NO_CACHE);
schmittde9b9552020-01-13 10:44:29 -0800239 SpawnExecutionContext policy = getSpawnContext(spawn);
buchgr9f7edd72017-07-14 12:58:50 +0200240
241 runner.exec(spawn, policy);
242
243 verify(localRunner).exec(spawn, policy);
Jakob Buchgraber5f4d6992019-11-14 07:00:02 -0800244 verify(cache).ensureInputsPresent(any(), any());
Benjamin Petersonf1570532019-01-24 07:14:18 -0800245 verifyNoMoreInteractions(cache);
buchgr9f7edd72017-07-14 12:58:50 +0200246 }
247
Jakob Buchgraber562fcf92017-07-27 12:51:13 +0200248 @Test
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700249 public void cachableSpawnsShouldBeCached_localFallback() throws Exception {
250 // Test that if a cachable spawn is executed locally due to the local fallback,
251 // that its result is uploaded to the remote cache.
252
253 remoteOptions.remoteAcceptCached = true;
254 remoteOptions.remoteLocalFallback = true;
255 remoteOptions.remoteUploadLocalResults = true;
256
257 RemoteSpawnRunner runner = spy(newSpawnRunner());
258
259 // Throw an IOException to trigger the local fallback.
Chi Wang09d8f142020-10-30 01:03:16 -0700260 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700261 .thenThrow(IOException.class);
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700262
263 SpawnResult res =
264 new SpawnResult.Builder()
265 .setStatus(Status.SUCCESS)
266 .setExitCode(0)
267 .setRunnerName("test")
268 .build();
269 when(localRunner.exec(any(Spawn.class), any(SpawnExecutionContext.class))).thenReturn(res);
270
271 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800272 SpawnExecutionContext policy = getSpawnContext(spawn);
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700273
274 SpawnResult result = runner.exec(spawn, policy);
275 assertThat(result.exitCode()).isEqualTo(0);
276 assertThat(result.status()).isEqualTo(Status.SUCCESS);
277 verify(localRunner).exec(eq(spawn), eq(policy));
278 verify(runner)
279 .execLocallyAndUpload(
280 eq(spawn), eq(policy), any(), any(), any(), any(), /* uploadLocalResults= */ eq(true));
281 verify(cache).upload(any(), any(), any(), any(), any(), any());
282 }
283
284 @Test
ishikhman21c313c2019-09-26 07:14:09 -0700285 public void failedLocalActionShouldNotBeUploaded() throws Exception {
286 // Test that the outputs of a locally executed action that failed are not uploaded.
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700287
ishikhman21c313c2019-09-26 07:14:09 -0700288 remoteOptions.remoteLocalFallback = true;
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -0700289 remoteOptions.remoteUploadLocalResults = true;
290
291 RemoteSpawnRunner runner = spy(newSpawnRunner());
292
ishikhman21c313c2019-09-26 07:14:09 -0700293 // Throw an IOException to trigger the local fallback.
Chi Wang09d8f142020-10-30 01:03:16 -0700294 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700295 .thenThrow(IOException.class);
Jakob Buchgraber562fcf92017-07-27 12:51:13 +0200296
buchgra6591352017-09-01 13:14:19 +0200297 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800298 SpawnExecutionContext policy = getSpawnContext(spawn);
Jakob Buchgraber562fcf92017-07-27 12:51:13 +0200299
300 SpawnResult res = Mockito.mock(SpawnResult.class);
301 when(res.exitCode()).thenReturn(1);
302 when(res.status()).thenReturn(Status.EXECUTION_FAILED);
303 when(localRunner.exec(eq(spawn), eq(policy))).thenReturn(res);
304
cpovirka4d3da62019-05-02 14:27:33 -0700305 assertThat(runner.exec(spawn, policy)).isSameInstanceAs(res);
Jakob Buchgraber562fcf92017-07-27 12:51:13 +0200306
307 verify(localRunner).exec(eq(spawn), eq(policy));
olaolaf0aa55d2018-08-16 08:51:06 -0700308 verify(runner)
309 .execLocallyAndUpload(
Googlerd9aa8ae2020-05-20 00:56:28 -0700310 eq(spawn), eq(policy), any(), any(), any(), any(), /* uploadLocalResults= */ eq(true));
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700311 verify(cache, never()).upload(any(), any(), any(), any(), any(), any());
Jakob Buchgraber562fcf92017-07-27 12:51:13 +0200312 }
313
buchgr2efea9d2017-08-11 16:19:00 +0200314 @Test
ishikhman62f54582019-03-18 03:42:42 -0700315 public void treatFailedCachedActionAsCacheMiss_local() throws Exception {
316 // Test that bazel treats failed cache action as a cache miss and attempts to execute action
317 // locally
buchgr2efea9d2017-08-11 16:19:00 +0200318
ishikhman21c313c2019-09-26 07:14:09 -0700319 remoteOptions.remoteLocalFallback = true;
320 remoteOptions.remoteUploadLocalResults = true;
321
buchgr2efea9d2017-08-11 16:19:00 +0200322 ActionResult failedAction = ActionResult.newBuilder().setExitCode(1).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700323 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
324 .thenReturn(failedAction);
buchgr2efea9d2017-08-11 16:19:00 +0200325
ishikhman21c313c2019-09-26 07:14:09 -0700326 RemoteSpawnRunner runner = spy(newSpawnRunner());
327 // Throw an IOException to trigger the local fallback.
Chi Wang09d8f142020-10-30 01:03:16 -0700328 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700329 .thenThrow(IOException.class);
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700330
ishikhman62f54582019-03-18 03:42:42 -0700331 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800332 SpawnExecutionContext policy = getSpawnContext(spawn);
buchgr2efea9d2017-08-11 16:19:00 +0200333
ishikhman62f54582019-03-18 03:42:42 -0700334 SpawnResult succeeded =
335 new SpawnResult.Builder()
336 .setStatus(Status.SUCCESS)
337 .setExitCode(0)
338 .setRunnerName("test")
339 .build();
340 when(localRunner.exec(eq(spawn), eq(policy))).thenReturn(succeeded);
341
342 runner.exec(spawn, policy);
343
344 verify(localRunner).exec(eq(spawn), eq(policy));
345 verify(runner)
346 .execLocallyAndUpload(
Googlerd9aa8ae2020-05-20 00:56:28 -0700347 eq(spawn), eq(policy), any(), any(), any(), any(), /* uploadLocalResults= */ eq(true));
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700348 verify(cache).upload(any(), any(), any(), any(), any(), any());
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700349 verify(cache, never()).download(any(ActionResult.class), any(Path.class), eq(outErr), any());
ishikhman62f54582019-03-18 03:42:42 -0700350 }
351
352 @Test
353 public void treatFailedCachedActionAsCacheMiss_remote() throws Exception {
354 // Test that bazel treats failed cache action as a cache miss and attempts to execute action
355 // remotely
356
357 ActionResult failedAction = ActionResult.newBuilder().setExitCode(1).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700358 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
359 .thenReturn(failedAction);
ishikhman62f54582019-03-18 03:42:42 -0700360
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700361 RemoteSpawnRunner runner = newSpawnRunner();
ishikhman62f54582019-03-18 03:42:42 -0700362
363 ExecuteResponse succeeded =
364 ExecuteResponse.newBuilder()
365 .setResult(ActionResult.newBuilder().setExitCode(0).build())
366 .build();
Chi Wang09d8f142020-10-30 01:03:16 -0700367 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700368 .thenReturn(succeeded);
ishikhman62f54582019-03-18 03:42:42 -0700369 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800370 SpawnExecutionContext policy = getSpawnContext(spawn);
ishikhman62f54582019-03-18 03:42:42 -0700371
372 runner.exec(spawn, policy);
373
374 ArgumentCaptor<ExecuteRequest> requestCaptor = ArgumentCaptor.forClass(ExecuteRequest.class);
Chi Wang09d8f142020-10-30 01:03:16 -0700375 verify(executor).executeRemotely(requestCaptor.capture(), any(OperationObserver.class));
ishikhman62f54582019-03-18 03:42:42 -0700376 assertThat(requestCaptor.getValue().getSkipCacheLookup()).isTrue();
buchgr2efea9d2017-08-11 16:19:00 +0200377 }
378
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200379 @Test
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200380 public void printWarningIfCacheIsDown() throws Exception {
381 // If we try to upload to a local cache, that is down a warning should be printed.
382
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700383 remoteOptions.remoteUploadLocalResults = true;
384 remoteOptions.remoteLocalFallback = true;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200385
386 Reporter reporter = new Reporter(new EventBus());
387 StoredEventHandler eventHandler = new StoredEventHandler();
388 reporter.addHandler(eventHandler);
389
ishikhman21c313c2019-09-26 07:14:09 -0700390 RemoteSpawnRunner runner = newSpawnRunner(reporter);
391 // Trigger local fallback
Chi Wang09d8f142020-10-30 01:03:16 -0700392 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700393 .thenThrow(new IOException());
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200394
buchgra6591352017-09-01 13:14:19 +0200395 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800396 SpawnExecutionContext policy = getSpawnContext(spawn);
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200397
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700398 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
399 .thenThrow(new IOException("cache down"));
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200400
olaola7744b862017-09-18 23:04:33 +0200401 doThrow(new IOException("cache down"))
402 .when(cache)
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700403 .upload(any(), any(), any(), any(), any(), any());
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200404
Googler4dd6f002018-03-27 08:15:39 -0700405 SpawnResult res =
406 new SpawnResult.Builder()
407 .setStatus(Status.SUCCESS)
408 .setExitCode(0)
409 .setRunnerName("test")
410 .build();
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200411 when(localRunner.exec(eq(spawn), eq(policy))).thenReturn(res);
412
413 assertThat(runner.exec(spawn, policy)).isEqualTo(res);
414
415 verify(localRunner).exec(eq(spawn), eq(policy));
416
417 assertThat(eventHandler.getEvents()).hasSize(1);
418
419 Event evt = eventHandler.getEvents().get(0);
420 assertThat(evt.getKind()).isEqualTo(EventKind.WARNING);
421 assertThat(evt.getMessage()).contains("fail");
422 assertThat(evt.getMessage()).contains("upload");
423 }
424
425 @Test
olaola90622712017-10-17 02:55:18 +0200426 public void noRemoteExecutorFallbackFails() throws Exception {
ishikhman21c313c2019-09-26 07:14:09 -0700427 // Errors from the fallback runner should be propagated out of the remote runner.
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200428
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700429 remoteOptions.remoteUploadLocalResults = true;
430 remoteOptions.remoteLocalFallback = true;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200431
ishikhman21c313c2019-09-26 07:14:09 -0700432 RemoteSpawnRunner runner = newSpawnRunner();
433 // Trigger local fallback
Chi Wang09d8f142020-10-30 01:03:16 -0700434 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700435 .thenThrow(new IOException());
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200436
buchgra6591352017-09-01 13:14:19 +0200437 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800438 SpawnExecutionContext policy = getSpawnContext(spawn);
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200439
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700440 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
441 .thenReturn(null);
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200442
443 IOException err = new IOException("local execution error");
444 when(localRunner.exec(eq(spawn), eq(policy))).thenThrow(err);
445
jcaterb9226772019-04-29 12:04:52 -0700446 IOException e = assertThrows(IOException.class, () -> runner.exec(spawn, policy));
cpovirka4d3da62019-05-02 14:27:33 -0700447 assertThat(e).isSameInstanceAs(err);
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200448
449 verify(localRunner).exec(eq(spawn), eq(policy));
450 }
451
452 @Test
olaola90622712017-10-17 02:55:18 +0200453 public void remoteCacheErrorFallbackFails() throws Exception {
ishikhman21c313c2019-09-26 07:14:09 -0700454 // Errors from the fallback runner should be propagated out of the remote runner.
olaola90622712017-10-17 02:55:18 +0200455
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700456 remoteOptions.remoteUploadLocalResults = true;
457 remoteOptions.remoteLocalFallback = true;
olaola90622712017-10-17 02:55:18 +0200458
ishikhman21c313c2019-09-26 07:14:09 -0700459 RemoteSpawnRunner runner = newSpawnRunner();
460 // Trigger local fallback
Chi Wang09d8f142020-10-30 01:03:16 -0700461 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700462 .thenThrow(new IOException());
olaola90622712017-10-17 02:55:18 +0200463
464 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800465 SpawnExecutionContext policy = getSpawnContext(spawn);
olaola90622712017-10-17 02:55:18 +0200466
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700467 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
468 .thenThrow(new IOException());
olaola90622712017-10-17 02:55:18 +0200469
470 IOException err = new IOException("local execution error");
471 when(localRunner.exec(eq(spawn), eq(policy))).thenThrow(err);
472
jcaterb9226772019-04-29 12:04:52 -0700473 IOException e = assertThrows(IOException.class, () -> runner.exec(spawn, policy));
cpovirka4d3da62019-05-02 14:27:33 -0700474 assertThat(e).isSameInstanceAs(err);
olaola90622712017-10-17 02:55:18 +0200475
476 verify(localRunner).exec(eq(spawn), eq(policy));
477 }
478
479 @Test
480 public void testLocalFallbackFailureRemoteExecutorFailure() throws Exception {
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700481 remoteOptions.remoteLocalFallback = true;
olaola90622712017-10-17 02:55:18 +0200482
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700483 RemoteSpawnRunner runner = newSpawnRunner();
olaola90622712017-10-17 02:55:18 +0200484
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700485 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
486 .thenReturn(null);
Chi Wang09d8f142020-10-30 01:03:16 -0700487 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700488 .thenThrow(new IOException());
olaola90622712017-10-17 02:55:18 +0200489
490 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800491 SpawnExecutionContext policy = getSpawnContext(spawn);
olaola90622712017-10-17 02:55:18 +0200492
493 IOException err = new IOException("local execution error");
494 when(localRunner.exec(eq(spawn), eq(policy))).thenThrow(err);
495
jcaterb9226772019-04-29 12:04:52 -0700496 IOException e = assertThrows(IOException.class, () -> runner.exec(spawn, policy));
cpovirka4d3da62019-05-02 14:27:33 -0700497 assertThat(e).isSameInstanceAs(err);
olaola90622712017-10-17 02:55:18 +0200498
499 verify(localRunner).exec(eq(spawn), eq(policy));
500 }
501
502 @Test
olaolabf326fa2018-03-21 11:22:12 -0700503 public void testHumanReadableServerLogsSavedForFailingAction() throws Exception {
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700504 RemoteSpawnRunner runner = newSpawnRunner();
olaolabf326fa2018-03-21 11:22:12 -0700505 Digest logDigest = digestUtil.computeAsUtf8("bla");
buchgrff008f42018-06-02 14:13:43 -0700506 Path logPath = logDir.getRelative(simpleActionId).getRelative("logname");
Chi Wang09d8f142020-10-30 01:03:16 -0700507 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
olaolabf326fa2018-03-21 11:22:12 -0700508 .thenReturn(
509 ExecuteResponse.newBuilder()
510 .putServerLogs(
511 "logname",
512 LogFile.newBuilder().setHumanReadable(true).setDigest(logDigest).build())
513 .setResult(ActionResult.newBuilder().setExitCode(31).build())
514 .build());
buchgrff008f42018-06-02 14:13:43 -0700515 SettableFuture<Void> completed = SettableFuture.create();
516 completed.set(null);
olaolaf0aa55d2018-08-16 08:51:06 -0700517 when(cache.downloadFile(eq(logPath), eq(logDigest))).thenReturn(completed);
olaolabf326fa2018-03-21 11:22:12 -0700518
519 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800520 SpawnExecutionContext policy = getSpawnContext(spawn);
olaolabf326fa2018-03-21 11:22:12 -0700521
522 SpawnResult res = runner.exec(spawn, policy);
523 assertThat(res.status()).isEqualTo(Status.NON_ZERO_EXIT);
524
Chi Wang09d8f142020-10-30 01:03:16 -0700525 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
olaolaf0aa55d2018-08-16 08:51:06 -0700526 verify(cache).downloadFile(eq(logPath), eq(logDigest));
olaolabf326fa2018-03-21 11:22:12 -0700527 }
528
529 @Test
530 public void testHumanReadableServerLogsSavedForFailingActionWithStatus() throws Exception {
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700531 RemoteSpawnRunner runner = newSpawnRunner();
olaolabf326fa2018-03-21 11:22:12 -0700532 Digest logDigest = digestUtil.computeAsUtf8("bla");
buchgrff008f42018-06-02 14:13:43 -0700533 Path logPath = logDir.getRelative(simpleActionId).getRelative("logname");
olaolabf326fa2018-03-21 11:22:12 -0700534 com.google.rpc.Status timeoutStatus =
535 com.google.rpc.Status.newBuilder().setCode(Code.DEADLINE_EXCEEDED.getNumber()).build();
536 ExecuteResponse resp =
537 ExecuteResponse.newBuilder()
538 .putServerLogs(
539 "logname", LogFile.newBuilder().setHumanReadable(true).setDigest(logDigest).build())
540 .setStatus(timeoutStatus)
541 .build();
Chi Wang09d8f142020-10-30 01:03:16 -0700542 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Benjamin Peterson1532df02019-01-24 08:45:44 -0800543 .thenThrow(new IOException(new ExecutionStatusException(resp.getStatus(), resp)));
buchgrff008f42018-06-02 14:13:43 -0700544 SettableFuture<Void> completed = SettableFuture.create();
545 completed.set(null);
olaolaf0aa55d2018-08-16 08:51:06 -0700546 when(cache.downloadFile(eq(logPath), eq(logDigest))).thenReturn(completed);
olaolabf326fa2018-03-21 11:22:12 -0700547
548 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800549 SpawnExecutionContext policy = getSpawnContext(spawn);
olaolabf326fa2018-03-21 11:22:12 -0700550
551 SpawnResult res = runner.exec(spawn, policy);
552 assertThat(res.status()).isEqualTo(Status.TIMEOUT);
553
Chi Wang09d8f142020-10-30 01:03:16 -0700554 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
olaolaf0aa55d2018-08-16 08:51:06 -0700555 verify(cache).downloadFile(eq(logPath), eq(logDigest));
olaolabf326fa2018-03-21 11:22:12 -0700556 }
557
558 @Test
559 public void testNonHumanReadableServerLogsNotSaved() throws Exception {
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700560 RemoteSpawnRunner runner = newSpawnRunner();
olaolabf326fa2018-03-21 11:22:12 -0700561
562 Digest logDigest = digestUtil.computeAsUtf8("bla");
563 ActionResult result = ActionResult.newBuilder().setExitCode(31).build();
Chi Wang09d8f142020-10-30 01:03:16 -0700564 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
olaolabf326fa2018-03-21 11:22:12 -0700565 .thenReturn(
566 ExecuteResponse.newBuilder()
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700567 .putServerLogs("logname", LogFile.newBuilder().setDigest(logDigest).build())
olaolabf326fa2018-03-21 11:22:12 -0700568 .setResult(result)
569 .build());
570
571 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800572 FakeSpawnExecutionContext policy = getSpawnContext(spawn);
olaolabf326fa2018-03-21 11:22:12 -0700573 SpawnResult res = runner.exec(spawn, policy);
574 assertThat(res.status()).isEqualTo(Status.NON_ZERO_EXIT);
575
Chi Wang09d8f142020-10-30 01:03:16 -0700576 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700577 verify(cache).download(eq(result), eq(execRoot), any(FileOutErr.class), any());
olaolaf0aa55d2018-08-16 08:51:06 -0700578 verify(cache, never()).downloadFile(any(Path.class), any(Digest.class));
olaolabf326fa2018-03-21 11:22:12 -0700579 }
580
581 @Test
582 public void testServerLogsNotSavedForSuccessfulAction() throws Exception {
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700583 RemoteSpawnRunner runner = newSpawnRunner();
olaolabf326fa2018-03-21 11:22:12 -0700584
585 Digest logDigest = digestUtil.computeAsUtf8("bla");
586 ActionResult result = ActionResult.newBuilder().setExitCode(0).build();
Chi Wang09d8f142020-10-30 01:03:16 -0700587 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
olaolabf326fa2018-03-21 11:22:12 -0700588 .thenReturn(
589 ExecuteResponse.newBuilder()
590 .putServerLogs(
591 "logname",
592 LogFile.newBuilder().setHumanReadable(true).setDigest(logDigest).build())
593 .setResult(result)
594 .build());
595
596 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800597 FakeSpawnExecutionContext policy = getSpawnContext(spawn);
olaolabf326fa2018-03-21 11:22:12 -0700598
599 SpawnResult res = runner.exec(spawn, policy);
600 assertThat(res.status()).isEqualTo(Status.SUCCESS);
601
Chi Wang09d8f142020-10-30 01:03:16 -0700602 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700603 verify(cache).download(eq(result), eq(execRoot), any(FileOutErr.class), any());
olaolaf0aa55d2018-08-16 08:51:06 -0700604 verify(cache, never()).downloadFile(any(Path.class), any(Digest.class));
olaolabf326fa2018-03-21 11:22:12 -0700605 }
606
607 @Test
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200608 public void cacheDownloadFailureTriggersRemoteExecution() throws Exception {
609 // If downloading a cached action fails, remote execution should be tried.
610
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700611 RemoteSpawnRunner runner = newSpawnRunner();
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200612
613 ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700614 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
615 .thenReturn(cachedResult);
George Gensureaeee3e02020-04-15 04:43:45 -0700616 Exception downloadFailure =
617 new BulkTransferException(new CacheNotFoundException(Digest.getDefaultInstance()));
George Gensurea168a822018-08-21 12:17:07 -0700618 doThrow(downloadFailure)
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200619 .when(cache)
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700620 .download(eq(cachedResult), any(Path.class), any(FileOutErr.class), any());
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200621 ActionResult execResult = ActionResult.newBuilder().setExitCode(31).build();
622 ExecuteResponse succeeded = ExecuteResponse.newBuilder().setResult(execResult).build();
Chi Wang09d8f142020-10-30 01:03:16 -0700623 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700624 .thenReturn(succeeded);
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700625 doNothing().when(cache).download(eq(execResult), any(Path.class), any(FileOutErr.class), any());
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200626
buchgra6591352017-09-01 13:14:19 +0200627 Spawn spawn = newSimpleSpawn();
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200628
schmittde9b9552020-01-13 10:44:29 -0800629 SpawnExecutionContext policy = getSpawnContext(spawn);
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200630
631 SpawnResult res = runner.exec(spawn, policy);
ulfjack32e7a1c2017-11-28 01:14:34 -0800632 assertThat(res.status()).isEqualTo(Status.NON_ZERO_EXIT);
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200633 assertThat(res.exitCode()).isEqualTo(31);
634
Chi Wang09d8f142020-10-30 01:03:16 -0700635 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200636 }
637
buchgr4763abc2017-08-30 14:37:21 +0200638 @Test
George Gensureda146d22019-03-26 03:17:32 -0700639 public void resultsDownloadFailureTriggersRemoteExecutionWithSkipCacheLookup() throws Exception {
640 // If downloading an action result fails, remote execution should be retried
641 // with skip cache lookup enabled
642
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700643 RemoteSpawnRunner runner = newSpawnRunner();
George Gensureda146d22019-03-26 03:17:32 -0700644
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700645 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
646 .thenReturn(null);
George Gensureda146d22019-03-26 03:17:32 -0700647 ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
648 ActionResult execResult = ActionResult.newBuilder().setExitCode(31).build();
649 ExecuteResponse cachedResponse =
650 ExecuteResponse.newBuilder().setResult(cachedResult).setCachedResult(true).build();
651 ExecuteResponse executedResponse = ExecuteResponse.newBuilder().setResult(execResult).build();
Chi Wang09d8f142020-10-30 01:03:16 -0700652 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
George Gensureda146d22019-03-26 03:17:32 -0700653 .thenReturn(cachedResponse)
654 .thenReturn(executedResponse);
George Gensureaeee3e02020-04-15 04:43:45 -0700655 Exception downloadFailure =
656 new BulkTransferException(new CacheNotFoundException(Digest.getDefaultInstance()));
George Gensureda146d22019-03-26 03:17:32 -0700657 doThrow(downloadFailure)
658 .when(cache)
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700659 .download(eq(cachedResult), any(Path.class), any(FileOutErr.class), any());
660 doNothing().when(cache).download(eq(execResult), any(Path.class), any(FileOutErr.class), any());
George Gensureda146d22019-03-26 03:17:32 -0700661
662 Spawn spawn = newSimpleSpawn();
663
schmittde9b9552020-01-13 10:44:29 -0800664 SpawnExecutionContext policy = getSpawnContext(spawn);
George Gensureda146d22019-03-26 03:17:32 -0700665
666 SpawnResult res = runner.exec(spawn, policy);
667 assertThat(res.status()).isEqualTo(Status.NON_ZERO_EXIT);
668 assertThat(res.exitCode()).isEqualTo(31);
669
670 ArgumentCaptor<ExecuteRequest> requestCaptor = ArgumentCaptor.forClass(ExecuteRequest.class);
Chi Wange8d356d2020-08-14 00:27:19 -0700671 verify(executor, times(2))
Chi Wang09d8f142020-10-30 01:03:16 -0700672 .executeRemotely(requestCaptor.capture(), any(OperationObserver.class));
George Gensureda146d22019-03-26 03:17:32 -0700673 List<ExecuteRequest> requests = requestCaptor.getAllValues();
674 // first request should have been executed without skip cache lookup
675 assertThat(requests.get(0).getSkipCacheLookup()).isFalse();
676 // second should have been executed with skip cache lookup
677 assertThat(requests.get(1).getSkipCacheLookup()).isTrue();
678 }
679
680 @Test
buchgr4763abc2017-08-30 14:37:21 +0200681 public void testRemoteExecutionTimeout() throws Exception {
682 // If remote execution times out the SpawnResult status should be TIMEOUT.
683
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700684 remoteOptions.remoteLocalFallback = false;
buchgr4763abc2017-08-30 14:37:21 +0200685
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700686 RemoteSpawnRunner runner = newSpawnRunner();
buchgr4763abc2017-08-30 14:37:21 +0200687
688 ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700689 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
690 .thenReturn(null);
olaola2732df02018-03-16 08:48:13 -0700691 ExecuteResponse resp =
692 ExecuteResponse.newBuilder()
693 .setResult(cachedResult)
694 .setStatus(
695 com.google.rpc.Status.newBuilder()
696 .setCode(Code.DEADLINE_EXCEEDED.getNumber())
697 .build())
698 .build();
Chi Wang09d8f142020-10-30 01:03:16 -0700699 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Benjamin Peterson1532df02019-01-24 08:45:44 -0800700 .thenThrow(new IOException(new ExecutionStatusException(resp.getStatus(), resp)));
buchgr4763abc2017-08-30 14:37:21 +0200701
buchgra6591352017-09-01 13:14:19 +0200702 Spawn spawn = newSimpleSpawn();
buchgr4763abc2017-08-30 14:37:21 +0200703
schmittde9b9552020-01-13 10:44:29 -0800704 SpawnExecutionContext policy = getSpawnContext(spawn);
buchgr4763abc2017-08-30 14:37:21 +0200705
706 SpawnResult res = runner.exec(spawn, policy);
707 assertThat(res.status()).isEqualTo(Status.TIMEOUT);
708
Chi Wang09d8f142020-10-30 01:03:16 -0700709 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700710 verify(cache).download(eq(cachedResult), eq(execRoot), any(FileOutErr.class), any());
buchgr4763abc2017-08-30 14:37:21 +0200711 }
712
buchgra6591352017-09-01 13:14:19 +0200713 @Test
olaola90622712017-10-17 02:55:18 +0200714 public void testRemoteExecutionTimeoutDoesNotTriggerFallback() throws Exception {
715 // If remote execution times out the SpawnResult status should be TIMEOUT, regardess of local
716 // fallback option.
717
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700718 remoteOptions.remoteLocalFallback = true;
olaola90622712017-10-17 02:55:18 +0200719
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700720 RemoteSpawnRunner runner = newSpawnRunner();
olaola90622712017-10-17 02:55:18 +0200721
722 ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700723 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
724 .thenReturn(null);
olaola2732df02018-03-16 08:48:13 -0700725 ExecuteResponse resp =
726 ExecuteResponse.newBuilder()
727 .setResult(cachedResult)
728 .setStatus(
729 com.google.rpc.Status.newBuilder()
730 .setCode(Code.DEADLINE_EXCEEDED.getNumber())
731 .build())
732 .build();
Chi Wang09d8f142020-10-30 01:03:16 -0700733 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Benjamin Peterson1532df02019-01-24 08:45:44 -0800734 .thenThrow(new IOException(new ExecutionStatusException(resp.getStatus(), resp)));
olaola90622712017-10-17 02:55:18 +0200735
736 Spawn spawn = newSimpleSpawn();
737
schmittde9b9552020-01-13 10:44:29 -0800738 SpawnExecutionContext policy = getSpawnContext(spawn);
olaola90622712017-10-17 02:55:18 +0200739
740 SpawnResult res = runner.exec(spawn, policy);
741 assertThat(res.status()).isEqualTo(Status.TIMEOUT);
742
Chi Wang09d8f142020-10-30 01:03:16 -0700743 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700744 verify(cache).download(eq(cachedResult), eq(execRoot), any(FileOutErr.class), any());
olaola90622712017-10-17 02:55:18 +0200745 verify(localRunner, never()).exec(eq(spawn), eq(policy));
746 }
747
748 @Test
749 public void testRemoteExecutionCommandFailureDoesNotTriggerFallback() throws Exception {
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700750 remoteOptions.remoteLocalFallback = true;
olaola90622712017-10-17 02:55:18 +0200751
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700752 RemoteSpawnRunner runner = newSpawnRunner();
olaola90622712017-10-17 02:55:18 +0200753
754 ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700755 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
756 .thenReturn(null);
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700757 ExecuteResponse failed =
758 ExecuteResponse.newBuilder()
759 .setResult(ActionResult.newBuilder().setExitCode(33).build())
760 .build();
Chi Wang09d8f142020-10-30 01:03:16 -0700761 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700762 .thenReturn(failed);
olaola90622712017-10-17 02:55:18 +0200763
764 Spawn spawn = newSimpleSpawn();
765
schmittde9b9552020-01-13 10:44:29 -0800766 SpawnExecutionContext policy = getSpawnContext(spawn);
olaola90622712017-10-17 02:55:18 +0200767
768 SpawnResult res = runner.exec(spawn, policy);
ulfjack32e7a1c2017-11-28 01:14:34 -0800769 assertThat(res.status()).isEqualTo(Status.NON_ZERO_EXIT);
olaola90622712017-10-17 02:55:18 +0200770 assertThat(res.exitCode()).isEqualTo(33);
771
Chi Wang09d8f142020-10-30 01:03:16 -0700772 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700773 verify(cache, never()).download(eq(cachedResult), eq(execRoot), any(FileOutErr.class), any());
olaola90622712017-10-17 02:55:18 +0200774 verify(localRunner, never()).exec(eq(spawn), eq(policy));
775 }
776
777 @Test
buchgra6591352017-09-01 13:14:19 +0200778 public void testExitCode_executorfailure() throws Exception {
779 // If we get a failure due to the remote cache not working, the exit code should be
780 // ExitCode.REMOTE_ERROR.
781
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700782 remoteOptions.remoteLocalFallback = false;
buchgra6591352017-09-01 13:14:19 +0200783
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700784 RemoteSpawnRunner runner = newSpawnRunner();
buchgra6591352017-09-01 13:14:19 +0200785
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700786 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
787 .thenReturn(null);
Chi Wang09d8f142020-10-30 01:03:16 -0700788 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700789 .thenThrow(new IOException("reasons"));
buchgra6591352017-09-01 13:14:19 +0200790
791 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800792 SpawnExecutionContext policy = getSpawnContext(spawn);
buchgra6591352017-09-01 13:14:19 +0200793
Jakob Buchgrabera79a4b62019-06-23 02:06:20 -0700794 SpawnResult result = runner.exec(spawn, policy);
795 assertThat(result.exitCode()).isEqualTo(ExitCode.REMOTE_ERROR.getNumericExitCode());
janakrdf701352020-10-13 08:20:35 -0700796 assertThat(result.getDetailMessage("", false, false)).contains("reasons");
buchgra6591352017-09-01 13:14:19 +0200797 }
798
799 @Test
800 public void testExitCode_executionfailure() throws Exception {
801 // If we get a failure due to the remote executor not working, the exit code should be
802 // ExitCode.REMOTE_ERROR.
803
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700804 remoteOptions.remoteLocalFallback = false;
buchgra6591352017-09-01 13:14:19 +0200805
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700806 RemoteSpawnRunner runner = newSpawnRunner();
buchgra6591352017-09-01 13:14:19 +0200807
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700808 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
809 .thenThrow(new IOException("reasons"));
buchgra6591352017-09-01 13:14:19 +0200810
811 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800812 SpawnExecutionContext policy = getSpawnContext(spawn);
buchgra6591352017-09-01 13:14:19 +0200813
Jakob Buchgrabera79a4b62019-06-23 02:06:20 -0700814 SpawnResult result = runner.exec(spawn, policy);
815 assertThat(result.exitCode()).isEqualTo(ExitCode.REMOTE_ERROR.getNumericExitCode());
janakrdf701352020-10-13 08:20:35 -0700816 assertThat(result.getDetailMessage("", false, false)).contains("reasons");
buchgra6591352017-09-01 13:14:19 +0200817 }
818
tomlu09fe0622018-06-19 12:55:39 -0700819 @Test
820 public void testMaterializeParamFiles() throws Exception {
tomlub5a727a2019-08-03 23:06:35 -0700821 testParamFilesAreMaterializedForFlag("--materialize_param_files");
822 }
823
824 @Test
825 public void testMaterializeParamFilesIsImpliedBySubcommands() throws Exception {
826 testParamFilesAreMaterializedForFlag("--subcommands");
827 }
828
tomlub5a727a2019-08-03 23:06:35 -0700829 private void testParamFilesAreMaterializedForFlag(String flag) throws Exception {
830 ExecutionOptions executionOptions = Options.parse(ExecutionOptions.class, flag).getOptions();
tomlu09fe0622018-06-19 12:55:39 -0700831 executionOptions.materializeParamFiles = true;
832 RemoteSpawnRunner runner =
833 new RemoteSpawnRunner(
834 execRoot,
835 Options.getDefaults(RemoteOptions.class),
836 executionOptions,
janakr61072c12020-07-07 12:28:56 -0700837 true,
tomlu09fe0622018-06-19 12:55:39 -0700838 /*cmdlineReporter=*/ null,
839 "build-req-id",
840 "command-id",
841 cache,
842 executor,
Jakob Buchgraber20342c72019-11-12 04:56:16 -0800843 retryService,
tomlu09fe0622018-06-19 12:55:39 -0700844 digestUtil,
Jakob Buchgraber50c10042019-04-11 02:11:19 -0700845 logDir,
cushone79dfd42019-09-18 18:58:06 -0700846 /* filesToDownload= */ ImmutableSet.of());
tomlu09fe0622018-06-19 12:55:39 -0700847
848 ExecuteResponse succeeded =
849 ExecuteResponse.newBuilder()
850 .setResult(ActionResult.newBuilder().setExitCode(0).build())
851 .build();
Chi Wang09d8f142020-10-30 01:03:16 -0700852 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700853 .thenReturn(succeeded);
tomlu09fe0622018-06-19 12:55:39 -0700854
855 ImmutableList<String> args = ImmutableList.of("--foo", "--bar");
856 ParamFileActionInput input =
857 new ParamFileActionInput(
858 PathFragment.create("out/param_file"), args, ParameterFileType.UNQUOTED, ISO_8859_1);
859 Spawn spawn =
860 new SimpleSpawn(
janakr94ff7602020-05-19 12:59:28 -0700861 new FakeOwner("foo", "bar", "//dummy:label"),
tomlu09fe0622018-06-19 12:55:39 -0700862 /*arguments=*/ ImmutableList.of(),
863 /*environment=*/ ImmutableMap.of(),
864 /*executionInfo=*/ ImmutableMap.of(),
ulfjacke4cca142020-01-08 04:44:40 -0800865 /*inputs=*/ NestedSetBuilder.create(Order.STABLE_ORDER, input),
866 /*outputs=*/ ImmutableSet.<ActionInput>of(),
tomlu09fe0622018-06-19 12:55:39 -0700867 ResourceSet.ZERO);
schmittde9b9552020-01-13 10:44:29 -0800868 SpawnExecutionContext policy = getSpawnContext(spawn);
tomlu09fe0622018-06-19 12:55:39 -0700869 SpawnResult res = runner.exec(spawn, policy);
870 assertThat(res.status()).isEqualTo(Status.SUCCESS);
871 Path paramFile = execRoot.getRelative("out/param_file");
872 assertThat(paramFile.exists()).isTrue();
873 try (InputStream inputStream = paramFile.getInputStream()) {
874 assertThat(
875 new String(ByteStreams.toByteArray(inputStream), StandardCharsets.UTF_8).split("\n"))
876 .asList()
877 .containsExactly("--foo", "--bar");
878 }
879 }
880
olaola8afd0fb2018-10-30 08:36:29 -0700881 @Test
buchgrd480c5f2019-04-03 00:53:34 -0700882 public void testDownloadMinimalOnCacheHit() throws Exception {
883 // arrange
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700884 remoteOptions.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
buchgrd480c5f2019-04-03 00:53:34 -0700885
886 ActionResult succeededAction = ActionResult.newBuilder().setExitCode(0).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700887 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
888 .thenReturn(succeededAction);
buchgrd480c5f2019-04-03 00:53:34 -0700889
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700890 RemoteSpawnRunner runner = newSpawnRunner();
buchgrd480c5f2019-04-03 00:53:34 -0700891
892 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800893 SpawnExecutionContext policy = getSpawnContext(spawn);
buchgrd480c5f2019-04-03 00:53:34 -0700894
895 // act
896 SpawnResult result = runner.exec(spawn, policy);
897 assertThat(result.exitCode()).isEqualTo(0);
898 assertThat(result.status()).isEqualTo(Status.SUCCESS);
899
900 // assert
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700901 verify(cache)
George Gensure3ef8fb92020-05-06 09:49:48 -0700902 .downloadMinimal(
903 any(), eq(succeededAction), anyCollection(), any(), any(), any(), any(), any());
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700904 verify(cache, never()).download(any(ActionResult.class), any(Path.class), eq(outErr), any());
buchgrd480c5f2019-04-03 00:53:34 -0700905 }
906
907 @Test
908 public void testDownloadMinimalOnCacheMiss() throws Exception {
909 // arrange
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700910 remoteOptions.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
buchgrd480c5f2019-04-03 00:53:34 -0700911
912 ActionResult succeededAction = ActionResult.newBuilder().setExitCode(0).build();
913 ExecuteResponse succeeded = ExecuteResponse.newBuilder().setResult(succeededAction).build();
Chi Wang09d8f142020-10-30 01:03:16 -0700914 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -0700915 .thenReturn(succeeded);
buchgrd480c5f2019-04-03 00:53:34 -0700916
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700917 RemoteSpawnRunner runner = newSpawnRunner();
buchgrd480c5f2019-04-03 00:53:34 -0700918
919 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800920 FakeSpawnExecutionContext policy = getSpawnContext(spawn);
buchgrd480c5f2019-04-03 00:53:34 -0700921
922 // act
923 SpawnResult result = runner.exec(spawn, policy);
924 assertThat(result.exitCode()).isEqualTo(0);
925 assertThat(result.status()).isEqualTo(Status.SUCCESS);
926
927 // assert
Chi Wang09d8f142020-10-30 01:03:16 -0700928 verify(executor).executeRemotely(any(), any(OperationObserver.class));
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700929 verify(cache)
George Gensure3ef8fb92020-05-06 09:49:48 -0700930 .downloadMinimal(
931 any(), eq(succeededAction), anyCollection(), any(), any(), any(), any(), any());
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700932 verify(cache, never()).download(any(ActionResult.class), any(Path.class), eq(outErr), any());
buchgrd480c5f2019-04-03 00:53:34 -0700933 }
934
935 @Test
936 public void testDownloadMinimalIoError() throws Exception {
937 // arrange
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700938 remoteOptions.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
buchgrd480c5f2019-04-03 00:53:34 -0700939
940 ActionResult succeededAction = ActionResult.newBuilder().setExitCode(0).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700941 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
942 .thenReturn(succeededAction);
buchgrd480c5f2019-04-03 00:53:34 -0700943 IOException downloadFailure = new IOException("downloadMinimal failed");
George Gensure3ef8fb92020-05-06 09:49:48 -0700944 when(cache.downloadMinimal(any(), any(), anyCollection(), any(), any(), any(), any(), any()))
buchgrd480c5f2019-04-03 00:53:34 -0700945 .thenThrow(downloadFailure);
946
Jakob Buchgraberc6c30302019-04-11 00:35:42 -0700947 RemoteSpawnRunner runner = newSpawnRunner();
buchgrd480c5f2019-04-03 00:53:34 -0700948
949 Spawn spawn = newSimpleSpawn();
schmittde9b9552020-01-13 10:44:29 -0800950 FakeSpawnExecutionContext policy = getSpawnContext(spawn);
buchgrd480c5f2019-04-03 00:53:34 -0700951
952 // act
Jakob Buchgrabera79a4b62019-06-23 02:06:20 -0700953 SpawnResult result = runner.exec(spawn, policy);
954 assertThat(result.getFailureMessage()).isEqualTo(downloadFailure.getMessage());
buchgrd480c5f2019-04-03 00:53:34 -0700955
956 // assert
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700957 verify(cache)
George Gensure3ef8fb92020-05-06 09:49:48 -0700958 .downloadMinimal(
959 any(), eq(succeededAction), anyCollection(), any(), any(), any(), any(), any());
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700960 verify(cache, never()).download(any(ActionResult.class), any(Path.class), eq(outErr), any());
buchgrd480c5f2019-04-03 00:53:34 -0700961 }
962
Jakob Buchgraber50c10042019-04-11 02:11:19 -0700963 @Test
964 public void testDownloadTopLevel() throws Exception {
965 // arrange
966 RemoteOptions options = Options.getDefaults(RemoteOptions.class);
967 options.remoteOutputsMode = RemoteOutputsMode.TOPLEVEL;
968
janakr448f1cf2020-03-30 09:12:44 -0700969 ArtifactRoot outputRoot = ArtifactRoot.asDerivedRoot(execRoot, "outs");
janakraea05602019-05-22 15:41:29 -0700970 Artifact topLevelOutput =
971 ActionsTestUtil.createArtifact(outputRoot, outputRoot.getRoot().getRelative("foo.bin"));
Jakob Buchgraber50c10042019-04-11 02:11:19 -0700972
973 ActionResult succeededAction = ActionResult.newBuilder().setExitCode(0).build();
Jakob Buchgraber21577f22020-03-23 03:11:29 -0700974 when(cache.downloadActionResult(any(ActionKey.class), /* inlineOutErr= */ eq(false)))
975 .thenReturn(succeededAction);
Jakob Buchgraber50c10042019-04-11 02:11:19 -0700976
977 RemoteSpawnRunner runner = newSpawnRunner(ImmutableSet.of(topLevelOutput));
978
979 Spawn spawn = newSimpleSpawn(topLevelOutput);
schmittde9b9552020-01-13 10:44:29 -0800980 FakeSpawnExecutionContext policy = getSpawnContext(spawn);
Jakob Buchgraber50c10042019-04-11 02:11:19 -0700981
982 // act
983 SpawnResult result = runner.exec(spawn, policy);
984 assertThat(result.exitCode()).isEqualTo(0);
985 assertThat(result.status()).isEqualTo(Status.SUCCESS);
986
987 // assert
Jakob Buchgraberd75b6cf2019-06-19 08:12:49 -0700988 verify(cache).download(eq(succeededAction), any(Path.class), eq(outErr), any());
Jakob Buchgraber50c10042019-04-11 02:11:19 -0700989 verify(cache, never())
George Gensure3ef8fb92020-05-06 09:49:48 -0700990 .downloadMinimal(
991 any(), eq(succeededAction), anyCollection(), any(), any(), any(), any(), any());
Jakob Buchgraber50c10042019-04-11 02:11:19 -0700992 }
993
George Gensure7aa74982020-04-01 00:54:15 -0700994 @Test
995 public void accountingDisabledWithoutWorker() {
996 SpawnMetrics.Builder spawnMetrics = Mockito.mock(SpawnMetrics.Builder.class);
997 RemoteSpawnRunner.spawnMetricsAccounting(
998 spawnMetrics, ExecutedActionMetadata.getDefaultInstance());
999 verifyNoMoreInteractions(spawnMetrics);
1000 }
1001
1002 @Test
1003 public void accountingAddsDurationsForStages() {
1004 SpawnMetrics.Builder builder =
Googler2b7e71a2020-06-04 01:32:01 -07001005 SpawnMetrics.Builder.forRemoteExec()
Googlerd9aa8ae2020-05-20 00:56:28 -07001006 .setQueueTime(Duration.ofSeconds(1))
George Gensure7aa74982020-04-01 00:54:15 -07001007 .setSetupTime(Duration.ofSeconds(2))
1008 .setExecutionWallTime(Duration.ofSeconds(2))
Googlerd9aa8ae2020-05-20 00:56:28 -07001009 .setProcessOutputsTime(Duration.ofSeconds(2));
George Gensure7aa74982020-04-01 00:54:15 -07001010 Timestamp queued = Timestamp.getDefaultInstance();
1011 com.google.protobuf.Duration oneSecond = Durations.fromMillis(1000);
1012 Timestamp workerStart = Timestamps.add(queued, oneSecond);
1013 Timestamp executionStart = Timestamps.add(workerStart, oneSecond);
1014 Timestamp executionCompleted = Timestamps.add(executionStart, oneSecond);
1015 Timestamp outputUploadStart = Timestamps.add(executionCompleted, oneSecond);
1016 Timestamp outputUploadComplete = Timestamps.add(outputUploadStart, oneSecond);
1017 ExecutedActionMetadata executedMetadata =
1018 ExecutedActionMetadata.newBuilder()
1019 .setWorker("test worker")
1020 .setQueuedTimestamp(queued)
1021 .setWorkerStartTimestamp(workerStart)
1022 .setExecutionStartTimestamp(executionStart)
1023 .setExecutionCompletedTimestamp(executionCompleted)
1024 .setOutputUploadStartTimestamp(outputUploadStart)
1025 .setOutputUploadCompletedTimestamp(outputUploadComplete)
1026 .build();
1027 RemoteSpawnRunner.spawnMetricsAccounting(builder, executedMetadata);
1028 SpawnMetrics spawnMetrics = builder.build();
1029 // remote queue time is accumulated
Googlerd9aa8ae2020-05-20 00:56:28 -07001030 assertThat(spawnMetrics.queueTime()).isEqualTo(Duration.ofSeconds(2));
George Gensure7aa74982020-04-01 00:54:15 -07001031 // setup time is substituted
1032 assertThat(spawnMetrics.setupTime()).isEqualTo(Duration.ofSeconds(1));
1033 // execution time is unspecified, assume substituted
1034 assertThat(spawnMetrics.executionWallTime()).isEqualTo(Duration.ofSeconds(1));
Googlerd9aa8ae2020-05-20 00:56:28 -07001035 // ProcessOutputs time is unspecified, assume substituted
1036 assertThat(spawnMetrics.processOutputsTime()).isEqualTo(Duration.ofSeconds(1));
George Gensure7aa74982020-04-01 00:54:15 -07001037 }
1038
Chi Wange8d356d2020-08-14 00:27:19 -07001039 @Test
1040 public void shouldReportExecutingStatusWithoutMetadata() throws Exception {
1041 RemoteSpawnRunner runner = newSpawnRunner();
1042 ExecuteResponse succeeded =
1043 ExecuteResponse.newBuilder()
1044 .setResult(ActionResult.newBuilder().setExitCode(0).build())
1045 .build();
1046
1047 Spawn spawn = newSimpleSpawn();
1048 SpawnExecutionContext policy = mock(SpawnExecutionContext.class);
1049 when(policy.getTimeout()).thenReturn(Duration.ZERO);
1050
Chi Wang09d8f142020-10-30 01:03:16 -07001051 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -07001052 .thenAnswer(
1053 invocationOnMock -> {
Chi Wang09d8f142020-10-30 01:03:16 -07001054 OperationObserver receiver = invocationOnMock.getArgument(1);
Chi Wange8d356d2020-08-14 00:27:19 -07001055 verify(policy, never()).report(eq(ProgressStatus.EXECUTING), any(String.class));
Chi Wang09d8f142020-10-30 01:03:16 -07001056 receiver.onNext(Operation.getDefaultInstance());
Chi Wange8d356d2020-08-14 00:27:19 -07001057 return succeeded;
1058 });
1059
1060 SpawnResult res = runner.exec(spawn, policy);
1061 assertThat(res.status()).isEqualTo(Status.SUCCESS);
1062
Chi Wang09d8f142020-10-30 01:03:16 -07001063 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Chi Wange8d356d2020-08-14 00:27:19 -07001064 InOrder reportOrder = inOrder(policy);
1065 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.SCHEDULING), any(String.class));
1066 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.EXECUTING), any(String.class));
1067 }
1068
1069 @Test
1070 public void shouldReportExecutingStatusAfterGotExecutingStageFromMetadata() throws Exception {
1071 RemoteSpawnRunner runner = newSpawnRunner();
1072 ExecuteResponse succeeded =
1073 ExecuteResponse.newBuilder()
1074 .setResult(ActionResult.newBuilder().setExitCode(0).build())
1075 .build();
1076
1077 Spawn spawn = newSimpleSpawn();
1078 SpawnExecutionContext policy = mock(SpawnExecutionContext.class);
1079 when(policy.getTimeout()).thenReturn(Duration.ZERO);
1080
Chi Wang09d8f142020-10-30 01:03:16 -07001081 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -07001082 .thenAnswer(
1083 invocationOnMock -> {
Chi Wang09d8f142020-10-30 01:03:16 -07001084 OperationObserver receiver = invocationOnMock.getArgument(1);
Chi Wange8d356d2020-08-14 00:27:19 -07001085 Operation queued =
1086 Operation.newBuilder()
1087 .setMetadata(
1088 Any.pack(
1089 ExecuteOperationMetadata.newBuilder().setStage(Value.QUEUED).build()))
1090 .build();
Chi Wang09d8f142020-10-30 01:03:16 -07001091 receiver.onNext(queued);
Chi Wange8d356d2020-08-14 00:27:19 -07001092 verify(policy, never()).report(eq(ProgressStatus.EXECUTING), any(String.class));
1093
1094 Operation executing =
1095 Operation.newBuilder()
1096 .setMetadata(
1097 Any.pack(
1098 ExecuteOperationMetadata.newBuilder()
1099 .setStage(Value.EXECUTING)
1100 .build()))
1101 .build();
Chi Wang09d8f142020-10-30 01:03:16 -07001102 receiver.onNext(executing);
Chi Wange8d356d2020-08-14 00:27:19 -07001103
1104 return succeeded;
1105 });
1106
1107 SpawnResult res = runner.exec(spawn, policy);
1108 assertThat(res.status()).isEqualTo(Status.SUCCESS);
1109
Chi Wang09d8f142020-10-30 01:03:16 -07001110 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Chi Wange8d356d2020-08-14 00:27:19 -07001111 InOrder reportOrder = inOrder(policy);
1112 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.SCHEDULING), any(String.class));
1113 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.EXECUTING), any(String.class));
1114 }
1115
1116 @Test
Chi Wang0f26b172020-08-20 07:19:17 -07001117 public void shouldIgnoreInvalidMetadata() throws Exception {
1118 RemoteSpawnRunner runner = newSpawnRunner();
1119 ExecuteResponse succeeded =
1120 ExecuteResponse.newBuilder()
1121 .setResult(ActionResult.newBuilder().setExitCode(0).build())
1122 .build();
1123
1124 Spawn spawn = newSimpleSpawn();
1125 SpawnExecutionContext policy = mock(SpawnExecutionContext.class);
1126 when(policy.getTimeout()).thenReturn(Duration.ZERO);
1127
Chi Wang09d8f142020-10-30 01:03:16 -07001128 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wang0f26b172020-08-20 07:19:17 -07001129 .thenAnswer(
1130 invocationOnMock -> {
Chi Wang09d8f142020-10-30 01:03:16 -07001131 OperationObserver receiver = invocationOnMock.getArgument(1);
Chi Wang0f26b172020-08-20 07:19:17 -07001132 Operation operation =
1133 Operation.newBuilder()
1134 .setMetadata(
1135 // Anything that is not ExecutionOperationMetadata
1136 Any.pack(Operation.getDefaultInstance()))
1137 .build();
Chi Wang09d8f142020-10-30 01:03:16 -07001138 receiver.onNext(operation);
Chi Wang0f26b172020-08-20 07:19:17 -07001139 return succeeded;
1140 });
1141
1142 SpawnResult res = runner.exec(spawn, policy);
1143 assertThat(res.status()).isEqualTo(Status.SUCCESS);
1144
Chi Wang09d8f142020-10-30 01:03:16 -07001145 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Chi Wang0f26b172020-08-20 07:19:17 -07001146 InOrder reportOrder = inOrder(policy);
1147 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.SCHEDULING), any(String.class));
1148 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.EXECUTING), any(String.class));
1149 }
1150
1151 @Test
Chi Wange8d356d2020-08-14 00:27:19 -07001152 public void shouldReportExecutingStatusIfNoExecutingStatusFromMetadata() throws Exception {
1153 RemoteSpawnRunner runner = newSpawnRunner();
1154 ExecuteResponse succeeded =
1155 ExecuteResponse.newBuilder()
1156 .setResult(ActionResult.newBuilder().setExitCode(0).build())
1157 .build();
1158
1159 Spawn spawn = newSimpleSpawn();
1160 SpawnExecutionContext policy = mock(SpawnExecutionContext.class);
1161 when(policy.getTimeout()).thenReturn(Duration.ZERO);
1162
Chi Wang09d8f142020-10-30 01:03:16 -07001163 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -07001164 .thenAnswer(
1165 invocationOnMock -> {
Chi Wang09d8f142020-10-30 01:03:16 -07001166 OperationObserver receiver = invocationOnMock.getArgument(1);
Chi Wange8d356d2020-08-14 00:27:19 -07001167 Operation completed =
1168 Operation.newBuilder()
1169 .setMetadata(
1170 Any.pack(
1171 ExecuteOperationMetadata.newBuilder()
1172 .setStage(Value.COMPLETED)
1173 .build()))
1174 .build();
Chi Wang09d8f142020-10-30 01:03:16 -07001175 receiver.onNext(completed);
Chi Wange8d356d2020-08-14 00:27:19 -07001176 return succeeded;
1177 });
1178
1179 SpawnResult res = runner.exec(spawn, policy);
1180 assertThat(res.status()).isEqualTo(Status.SUCCESS);
1181
Chi Wang09d8f142020-10-30 01:03:16 -07001182 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Chi Wange8d356d2020-08-14 00:27:19 -07001183 InOrder reportOrder = inOrder(policy);
1184 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.SCHEDULING), any(String.class));
1185 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.EXECUTING), any(String.class));
1186 }
1187
1188 @Test
1189 public void shouldReportExecutingStatusEvenNoOperationFromServer() throws Exception {
1190 RemoteSpawnRunner runner = newSpawnRunner();
1191 ExecuteResponse succeeded =
1192 ExecuteResponse.newBuilder()
1193 .setResult(ActionResult.newBuilder().setExitCode(0).build())
1194 .build();
1195
1196 Spawn spawn = newSimpleSpawn();
1197 SpawnExecutionContext policy = mock(SpawnExecutionContext.class);
1198 when(policy.getTimeout()).thenReturn(Duration.ZERO);
1199
Chi Wang09d8f142020-10-30 01:03:16 -07001200 when(executor.executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class)))
Chi Wange8d356d2020-08-14 00:27:19 -07001201 .thenReturn(succeeded);
1202
1203 SpawnResult res = runner.exec(spawn, policy);
1204 assertThat(res.status()).isEqualTo(Status.SUCCESS);
1205
Chi Wang09d8f142020-10-30 01:03:16 -07001206 verify(executor).executeRemotely(any(ExecuteRequest.class), any(OperationObserver.class));
Chi Wange8d356d2020-08-14 00:27:19 -07001207 InOrder reportOrder = inOrder(policy);
1208 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.SCHEDULING), any(String.class));
1209 reportOrder.verify(policy, times(1)).report(eq(ProgressStatus.EXECUTING), any(String.class));
1210 }
1211
Jakob Buchgraber50c10042019-04-11 02:11:19 -07001212 private static Spawn newSimpleSpawn(Artifact... outputs) {
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -07001213 return simpleSpawnWithExecutionInfo(ImmutableMap.of(), outputs);
1214 }
1215
1216 private static SimpleSpawn simpleSpawnWithExecutionInfo(
1217 ImmutableMap<String, String> executionInfo, Artifact... outputs) {
buchgra6591352017-09-01 13:14:19 +02001218 return new SimpleSpawn(
janakr94ff7602020-05-19 12:59:28 -07001219 new FakeOwner("foo", "bar", "//dummy:label"),
Jakob Buchgraberc6c30302019-04-11 00:35:42 -07001220 /*arguments=*/ ImmutableList.of(),
1221 /*environment=*/ ImmutableMap.of(),
Sergio Rodriguez Orellana8860c3e2019-07-25 01:12:58 -07001222 /*executionInfo=*/ executionInfo,
ulfjacke4cca142020-01-08 04:44:40 -08001223 /*inputs=*/ NestedSetBuilder.emptySet(Order.STABLE_ORDER),
1224 /*outputs=*/ ImmutableSet.copyOf(outputs),
buchgra6591352017-09-01 13:14:19 +02001225 ResourceSet.ZERO);
1226 }
1227
Jakob Buchgraberc6c30302019-04-11 00:35:42 -07001228 private RemoteSpawnRunner newSpawnRunner() {
janakr61072c12020-07-07 12:28:56 -07001229 return newSpawnRunner(
1230 /* verboseFailures= */ false,
1231 executor,
1232 /* reporter= */ null,
1233 /* topLevelOutputs= */ ImmutableSet.of());
Jakob Buchgraber50c10042019-04-11 02:11:19 -07001234 }
1235
ishikhman21c313c2019-09-26 07:14:09 -07001236 private RemoteSpawnRunner newSpawnRunner(Reporter reporter) {
janakr61072c12020-07-07 12:28:56 -07001237 return newSpawnRunner(
1238 /* verboseFailures= */ false, executor, reporter, /* topLevelOutputs= */ ImmutableSet.of());
ishikhman21c313c2019-09-26 07:14:09 -07001239 }
1240
Jakob Buchgraber34784c32019-07-22 07:40:14 -07001241 private RemoteSpawnRunner newSpawnRunner(ImmutableSet<ActionInput> topLevelOutputs) {
janakr61072c12020-07-07 12:28:56 -07001242 return newSpawnRunner(
1243 /* verboseFailures= */ false, executor, /* reporter= */ null, topLevelOutputs);
Jakob Buchgraberc6c30302019-04-11 00:35:42 -07001244 }
1245
1246 private RemoteSpawnRunner newSpawnRunner(
janakr61072c12020-07-07 12:28:56 -07001247 boolean verboseFailures,
Chi Wang09d8f142020-10-30 01:03:16 -07001248 @Nullable RemoteExecutionClient executor,
Jakob Buchgraber50c10042019-04-11 02:11:19 -07001249 @Nullable Reporter reporter,
Jakob Buchgraber34784c32019-07-22 07:40:14 -07001250 ImmutableSet<ActionInput> topLevelOutputs) {
Jakob Buchgraberc6c30302019-04-11 00:35:42 -07001251 return new RemoteSpawnRunner(
1252 execRoot,
1253 remoteOptions,
1254 Options.getDefaults(ExecutionOptions.class),
janakr61072c12020-07-07 12:28:56 -07001255 verboseFailures,
Jakob Buchgraberc6c30302019-04-11 00:35:42 -07001256 reporter,
1257 "build-req-id",
1258 "command-id",
1259 cache,
1260 executor,
Jakob Buchgraber20342c72019-11-12 04:56:16 -08001261 retryService,
Jakob Buchgraberc6c30302019-04-11 00:35:42 -07001262 digestUtil,
Jakob Buchgraber50c10042019-04-11 02:11:19 -07001263 logDir,
1264 topLevelOutputs);
Jakob Buchgraberc6c30302019-04-11 00:35:42 -07001265 }
buchgr9f7edd72017-07-14 12:58:50 +02001266}