blob: 4af3c5d243ad902569a977801125135955cc2afe [file] [log] [blame]
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +00001// Copyright 2016 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.
14
15package com.google.devtools.build.lib.sandbox;
16
17import com.google.common.collect.ImmutableSet;
18import com.google.common.collect.ImmutableSet.Builder;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000019import com.google.devtools.build.lib.actions.ActionExecutionContext;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000020import com.google.devtools.build.lib.actions.EnvironmentalExecException;
21import com.google.devtools.build.lib.actions.ExecException;
Philipp Wollermannf399a212016-09-23 09:59:26 +000022import com.google.devtools.build.lib.actions.SandboxedSpawnActionContext;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000023import com.google.devtools.build.lib.actions.Spawn;
Philipp Wollermann9e21b412016-09-26 14:08:28 +000024import com.google.devtools.build.lib.actions.SpawnActionContext;
25import com.google.devtools.build.lib.actions.Spawns;
Philipp Wollermann238839c2016-10-27 08:55:07 +000026import com.google.devtools.build.lib.actions.UserExecException;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000027import com.google.devtools.build.lib.analysis.BlazeDirectories;
Philipp Wollermann9e21b412016-09-26 14:08:28 +000028import com.google.devtools.build.lib.buildtool.BuildRequest;
29import com.google.devtools.build.lib.events.Event;
Philipp Wollermanna7827c82016-09-26 16:21:22 +000030import com.google.devtools.build.lib.events.EventHandler;
Yue Gan400f82e2017-01-12 05:08:02 +000031import com.google.devtools.build.lib.util.io.OutErr;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000032import com.google.devtools.build.lib.vfs.Path;
33import com.google.devtools.build.lib.vfs.PathFragment;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000034import java.io.IOException;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000035import java.util.Map;
Philipp Wollermann9e21b412016-09-26 14:08:28 +000036import java.util.Set;
Philipp Wollermann9e21b412016-09-26 14:08:28 +000037import java.util.concurrent.atomic.AtomicReference;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000038
39/** Abstract common ancestor for sandbox strategies implementing the common parts. */
Philipp Wollermannf399a212016-09-23 09:59:26 +000040abstract class SandboxStrategy implements SandboxedSpawnActionContext {
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000041
Philipp Wollermann9e21b412016-09-26 14:08:28 +000042 private final BuildRequest buildRequest;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000043 private final BlazeDirectories blazeDirs;
Philipp Wollermann9e21b412016-09-26 14:08:28 +000044 private final Path execRoot;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000045 private final boolean verboseFailures;
46 private final SandboxOptions sandboxOptions;
Philipp Wollermann809df532016-09-08 12:53:39 +000047 private final SpawnHelpers spawnHelpers;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000048
49 public SandboxStrategy(
Philipp Wollermann9e21b412016-09-26 14:08:28 +000050 BuildRequest buildRequest,
51 BlazeDirectories blazeDirs,
Philipp Wollermann9e21b412016-09-26 14:08:28 +000052 boolean verboseFailures,
53 SandboxOptions sandboxOptions) {
54 this.buildRequest = buildRequest;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000055 this.blazeDirs = blazeDirs;
Philipp Wollermann9e21b412016-09-26 14:08:28 +000056 this.execRoot = blazeDirs.getExecRoot();
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000057 this.verboseFailures = verboseFailures;
58 this.sandboxOptions = sandboxOptions;
Philipp Wollermann809df532016-09-08 12:53:39 +000059 this.spawnHelpers = new SpawnHelpers(blazeDirs.getExecRoot());
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000060 }
61
Philipp Wollermann9e21b412016-09-26 14:08:28 +000062 protected void runSpawn(
63 Spawn spawn,
64 ActionExecutionContext actionExecutionContext,
65 Map<String, String> spawnEnvironment,
66 SandboxExecRoot sandboxExecRoot,
67 Set<PathFragment> outputs,
68 SandboxRunner runner,
69 AtomicReference<Class<? extends SpawnActionContext>> writeOutputFiles)
70 throws ExecException, InterruptedException {
Philipp Wollermanna7827c82016-09-26 16:21:22 +000071 EventHandler eventHandler = actionExecutionContext.getExecutor().getEventHandler();
Philipp Wollermann238839c2016-10-27 08:55:07 +000072 ExecException execException = null;
Yue Gan400f82e2017-01-12 05:08:02 +000073 OutErr outErr = actionExecutionContext.getFileOutErr();
Philipp Wollermann9e21b412016-09-26 14:08:28 +000074 try {
75 runner.run(
76 spawn.getArguments(),
77 spawnEnvironment,
Yue Gan400f82e2017-01-12 05:08:02 +000078 outErr,
Philipp Wollermann9e21b412016-09-26 14:08:28 +000079 Spawns.getTimeoutSeconds(spawn),
Yue Gan400f82e2017-01-12 05:08:02 +000080 SandboxHelpers.shouldAllowNetwork(buildRequest, spawn),
Philipp Wollermann28d9bd32017-02-01 16:05:02 +000081 sandboxOptions.sandboxDebug,
82 sandboxOptions.sandboxFakeHostname);
Philipp Wollermann238839c2016-10-27 08:55:07 +000083 } catch (ExecException e) {
84 execException = e;
85 }
86
87 if (writeOutputFiles != null && !writeOutputFiles.compareAndSet(null, SandboxStrategy.class)) {
88 throw new InterruptedException();
89 }
90
91 try {
92 // We copy the outputs even when the command failed, otherwise StandaloneTestStrategy
93 // won't be able to get the test logs of a failed test. (We should probably do this in
94 // some better way.)
95 sandboxExecRoot.copyOutputs(execRoot, outputs);
96 } catch (IOException e) {
97 if (execException == null) {
98 throw new UserExecException("Could not move output artifacts from sandboxed execution", e);
Philipp Wollermann9e21b412016-09-26 14:08:28 +000099 } else {
Philipp Wollermann238839c2016-10-27 08:55:07 +0000100 // Catch the IOException and turn it into an error message, otherwise this might hide an
101 // exception thrown during runner.run earlier.
102 eventHandler.handle(
103 Event.error(
104 "I/O exception while extracting output artifacts from sandboxed execution: " + e));
Philipp Wollermann9e21b412016-09-26 14:08:28 +0000105 }
Philipp Wollermann9e21b412016-09-26 14:08:28 +0000106 }
107
Philipp Wollermann238839c2016-10-27 08:55:07 +0000108 if (execException != null) {
Yue Gan400f82e2017-01-12 05:08:02 +0000109 outErr.printErr(
110 "Use --strategy="
111 + spawn.getMnemonic()
112 + "=standalone to disable sandboxing for the failing actions.\n");
Philipp Wollermann238839c2016-10-27 08:55:07 +0000113 throw execException;
Philipp Wollermann9e21b412016-09-26 14:08:28 +0000114 }
115 }
116
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +0000117 /** Gets the list of directories that the spawn will assume to be writable. */
Kristina Chodorow4b73e972017-02-16 17:00:53 +0000118 protected ImmutableSet<Path> getWritableDirs(Path sandboxExecRoot, Map<String, String> env,
119 ImmutableSet<PathFragment> outputs) {
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +0000120 Builder<Path> writableDirs = ImmutableSet.builder();
121 // We have to make the TEST_TMPDIR directory writable if it is specified.
122 if (env.containsKey("TEST_TMPDIR")) {
123 writableDirs.add(sandboxExecRoot.getRelative(env.get("TEST_TMPDIR")));
124 }
Kristina Chodorow4b73e972017-02-16 17:00:53 +0000125 for (PathFragment output : outputs) {
126 writableDirs.add(sandboxExecRoot.getRelative(output).getParentDirectory());
127 }
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +0000128 return writableDirs.build();
129 }
130
131 protected ImmutableSet<Path> getInaccessiblePaths() {
132 ImmutableSet.Builder<Path> inaccessiblePaths = ImmutableSet.builder();
133 for (String path : sandboxOptions.sandboxBlockPath) {
134 inaccessiblePaths.add(blazeDirs.getFileSystem().getPath(path));
135 }
136 return inaccessiblePaths.build();
137 }
138
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +0000139 @Override
140 public boolean willExecuteRemotely(boolean remotable) {
141 return false;
142 }
143
144 @Override
145 public String toString() {
146 return "sandboxed";
147 }
148
149 @Override
150 public boolean shouldPropagateExecException() {
151 return verboseFailures && sandboxOptions.sandboxDebug;
152 }
Philipp Wollermann809df532016-09-08 12:53:39 +0000153
154 public Map<PathFragment, Path> getMounts(Spawn spawn, ActionExecutionContext executionContext)
155 throws ExecException {
156 try {
157 return spawnHelpers.getMounts(spawn, executionContext);
158 } catch (IOException e) {
159 throw new EnvironmentalExecException("Could not prepare mounts for sandbox execution", e);
160 }
161 }
162
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +0000163}