Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.0.1] Print interactive sandboxed shell command with --sandbox_debug #20734

Merged
merged 2 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package com.google.devtools.build.lib.sandbox;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -59,7 +60,8 @@
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import javax.annotation.Nullable;
import java.util.Optional;
import java.util.stream.Stream;

/** Abstract common ancestor for sandbox spawn runners implementing the common parts. */
abstract class AbstractSandboxSpawnRunner implements SpawnRunner {
Expand Down Expand Up @@ -224,7 +226,7 @@ private final SpawnResult run(
StringBuilder msg = new StringBuilder("Action failed to execute: java.io.IOException: ");
msg.append(exceptionMsg);
msg.append("\n");
if (sandboxDebugOutput != null) {
if (!sandboxDebugOutput.isEmpty()) {
msg.append("Sandbox debug output:\n");
msg.append(sandboxDebugOutput);
msg.append("\n");
Expand Down Expand Up @@ -294,12 +296,12 @@ private final SpawnResult run(
}

String sandboxDebugOutput = getSandboxDebugOutput(sandbox);
if (sandboxDebugOutput != null) {
if (!sandboxDebugOutput.isEmpty()) {
reporter.handle(
Event.of(
EventKind.DEBUG,
String.format(
"Sandbox debug output for %s %s: %s",
"Sandbox debug output for %s %s:\n%s",
originalSpawn.getMnemonic(),
originalSpawn.getTargetLabel(),
sandboxDebugOutput)));
Expand Down Expand Up @@ -334,18 +336,21 @@ private final SpawnResult run(
return spawnResultBuilder.build();
}

@Nullable
private String getSandboxDebugOutput(SandboxedSpawn sandbox) throws IOException {
Optional<String> sandboxDebugOutput = Optional.empty();
Path sandboxDebugPath = sandbox.getSandboxDebugPath();
if (sandboxDebugPath != null && sandboxDebugPath.exists()) {
try (InputStream inputStream = sandboxDebugPath.getInputStream()) {
String msg = new String(inputStream.readAllBytes(), UTF_8);
if (!msg.isEmpty()) {
return msg;
sandboxDebugOutput = Optional.of(msg);
}
}
}
return null;
Optional<String> interactiveDebugInstructions = sandbox.getInteractiveDebugInstructions();
return Stream.of(sandboxDebugOutput, interactiveDebugInstructions)
.flatMap(Optional::stream)
.collect(joining("\n"));
}

private boolean wasTimeout(Duration timeout, Duration wallTime) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/google/devtools/build/lib/sandbox/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ java_library(
":sandbox_helpers",
":sandbox_options",
"//src/main/java/com/google/devtools/build/lib/exec:tree_deleter",
"//src/main/java/com/google/devtools/build/lib/util:command",
"//src/main/java/com/google/devtools/build/lib/util:describable_execution_unit",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,29 +258,30 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context
allowNetwork
|| Spawns.requiresNetwork(spawn, getSandboxOptions().defaultSandboxAllowNetwork);

return new SymlinkedSandboxedSpawn(
sandboxPath,
sandboxExecRoot,
commandLine,
environment,
inputs,
outputs,
writableDirs,
treeDeleter,
/* sandboxDebugPath= */ null,
statisticsPath,
spawn.getMnemonic()) {
@Override
public void createFileSystem() throws IOException, InterruptedException {
super.createFileSystem();
writeConfig(
sandboxConfigPath,
writableDirs,
getInaccessiblePaths(),
allowNetworkForThisSpawn,
statisticsPath);
}
};
return new SymlinkedSandboxedSpawn(
sandboxPath,
sandboxExecRoot,
commandLine,
environment,
inputs,
outputs,
writableDirs,
treeDeleter,
/* sandboxDebugPath= */ null,
statisticsPath,
/* interactiveDebugArguments= */ null,
spawn.getMnemonic()) {
@Override
public void createFileSystem() throws IOException, InterruptedException {
super.createFileSystem();
writeConfig(
sandboxConfigPath,
writableDirs,
getInaccessiblePaths(),
allowNetworkForThisSpawn,
statisticsPath);
}
};
}

private void writeConfig(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ public static BindMount of(Path mountPoint, Path source) {
}

private final Path linuxSandboxPath;
private final List<String> commandArguments;
private Path hermeticSandboxPath;
private Path workingDirectory;
private Duration timeout;
Expand All @@ -72,15 +71,13 @@ public static BindMount of(Path mountPoint, Path source) {
private boolean sigintSendsSigterm = false;
private String cgroupsDir;

private LinuxSandboxCommandLineBuilder(Path linuxSandboxPath, List<String> commandArguments) {
private LinuxSandboxCommandLineBuilder(Path linuxSandboxPath) {
this.linuxSandboxPath = linuxSandboxPath;
this.commandArguments = commandArguments;
}

/** Returns a new command line builder for the {@code linux-sandbox} tool. */
public static LinuxSandboxCommandLineBuilder commandLineBuilder(
Path linuxSandboxPath, List<String> commandArguments) {
return new LinuxSandboxCommandLineBuilder(linuxSandboxPath, commandArguments);
public static LinuxSandboxCommandLineBuilder commandLineBuilder(Path linuxSandboxPath) {
return new LinuxSandboxCommandLineBuilder(linuxSandboxPath);
}

/**
Expand Down Expand Up @@ -247,7 +244,7 @@ public LinuxSandboxCommandLineBuilder addExecutionInfo(Map<String, String> execu
}

/** Builds the command line to invoke a specific command using the {@code linux-sandbox} tool. */
public ImmutableList<String> build() {
public ImmutableList<String> buildForCommand(List<String> commandArguments) {
Preconditions.checkState(
!(this.useFakeUsername && this.useFakeRoot),
"useFakeUsername and useFakeRoot are exclusive");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,9 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS
throws InterruptedException {
LocalExecutionOptions options = cmdEnv.getOptions().getOptions(LocalExecutionOptions.class);
ImmutableList<String> linuxSandboxArgv =
LinuxSandboxCommandLineBuilder.commandLineBuilder(
linuxSandbox, ImmutableList.of("/bin/true"))
LinuxSandboxCommandLineBuilder.commandLineBuilder(linuxSandbox)
.setTimeout(options.getLocalSigkillGraceSeconds())
.build();
.buildForCommand(ImmutableList.of("/bin/true"));
ImmutableMap<String, String> env = ImmutableMap.of();
Path execRoot = cmdEnv.getExecRoot();
File cwd = execRoot.getPathFile();
Expand Down Expand Up @@ -309,7 +308,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context
boolean createNetworkNamespace =
!(allowNetwork || Spawns.requiresNetwork(spawn, sandboxOptions.defaultSandboxAllowNetwork));
LinuxSandboxCommandLineBuilder commandLineBuilder =
LinuxSandboxCommandLineBuilder.commandLineBuilder(linuxSandbox, spawn.getArguments())
LinuxSandboxCommandLineBuilder.commandLineBuilder(linuxSandbox)
.addExecutionInfo(spawn.getExecutionInfo())
.setWritableFilesAndDirectories(writableDirs)
.setTmpfsDirectories(ImmutableSet.copyOf(getSandboxOptions().sandboxTmpfsPath))
Expand Down Expand Up @@ -354,7 +353,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context
return new HardlinkedSandboxedSpawn(
sandboxPath,
sandboxExecRoot,
commandLineBuilder.build(),
commandLineBuilder.buildForCommand(spawn.getArguments()),
environment,
inputs,
outputs,
Expand All @@ -368,14 +367,15 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context
return new SymlinkedSandboxedSpawn(
sandboxPath,
sandboxExecRoot,
commandLineBuilder.build(),
commandLineBuilder.buildForCommand(spawn.getArguments()),
environment,
inputs,
outputs,
writableDirs,
treeDeleter,
sandboxDebugPath,
statisticsPath,
makeInteractiveDebugArguments(commandLineBuilder, sandboxOptions),
spawn.getMnemonic());
}
}
Expand Down Expand Up @@ -538,4 +538,13 @@ public void cleanupSandboxBase(Path sandboxBase, TreeDeleter treeDeleter) throws

super.cleanupSandboxBase(sandboxBase, treeDeleter);
}

@Nullable
private ImmutableList<String> makeInteractiveDebugArguments(
LinuxSandboxCommandLineBuilder commandLineBuilder, SandboxOptions sandboxOptions) {
if (!sandboxOptions.sandboxDebug) {
return null;
}
return commandLineBuilder.buildForCommand(ImmutableList.of("/bin/sh", "-i"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,19 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context
null);
SandboxOutputs outputs = helpers.getOutputs(spawn);

return new SymlinkedSandboxedSpawn(
sandboxPath,
sandboxExecRoot,
commandLineBuilder.build(),
environment,
inputs,
outputs,
getWritableDirs(sandboxExecRoot, sandboxExecRoot, environment),
treeDeleter,
/* sandboxDebugPath= */ null,
statisticsPath,
spawn.getMnemonic());
return new SymlinkedSandboxedSpawn(
sandboxPath,
sandboxExecRoot,
commandLineBuilder.build(),
environment,
inputs,
outputs,
getWritableDirs(sandboxExecRoot, sandboxExecRoot, environment),
treeDeleter,
/* sandboxDebugPath= */ null,
statisticsPath,
/* interactiveDebugArguments= */ null,
spawn.getMnemonic());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.google.devtools.build.lib.util.DescribableExecutionUnit;
import com.google.devtools.build.lib.vfs.Path;
import java.io.IOException;
import java.util.Optional;
import javax.annotation.Nullable;

/**
Expand Down Expand Up @@ -61,4 +62,12 @@ default boolean useSubprocessTimeout() {

/** Deletes the sandbox directory. */
void delete();

/**
* Returns user-facing instructions for starting an interactive sandboxed environment identical to
* the one in which this spawn is executed.
*/
default Optional<String> getInteractiveDebugInstructions() {
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
import com.google.devtools.build.lib.exec.TreeDeleter;
import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs;
import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs;
import com.google.devtools.build.lib.util.CommandDescriptionForm;
import com.google.devtools.build.lib.util.CommandFailureUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;

Expand All @@ -37,6 +40,8 @@ public class SymlinkedSandboxedSpawn extends AbstractContainerizingSandboxedSpaw
/** Mnemonic of the action running in this spawn. */
private final String mnemonic;

@Nullable private final ImmutableList<String> interactiveDebugArguments;

public SymlinkedSandboxedSpawn(
Path sandboxPath,
Path sandboxExecRoot,
Expand All @@ -48,6 +53,7 @@ public SymlinkedSandboxedSpawn(
TreeDeleter treeDeleter,
@Nullable Path sandboxDebugPath,
@Nullable Path statisticsPath,
@Nullable ImmutableList<String> interactiveDebugArguments,
String mnemonic) {
super(
sandboxPath,
Expand All @@ -62,6 +68,7 @@ public SymlinkedSandboxedSpawn(
statisticsPath,
mnemonic);
this.mnemonic = isNullOrEmpty(mnemonic) ? "_NoMnemonic_" : mnemonic;
this.interactiveDebugArguments = interactiveDebugArguments;
}

@Override
Expand Down Expand Up @@ -96,4 +103,23 @@ public void delete() {
SandboxStash.stashSandbox(sandboxPath, mnemonic);
super.delete();
}

@Nullable
@Override
public Optional<String> getInteractiveDebugInstructions() {
if (interactiveDebugArguments == null) {
return Optional.empty();
}
return Optional.of(
"Run this command to start an interactive shell in an identical sandboxed environment:\n"
+ CommandFailureUtils.describeCommand(
CommandDescriptionForm.COMPLETE,
/* prettyPrintArgs= */ false,
interactiveDebugArguments,
getEnvironment(),
/* environmentVariablesToClear= */ null,
/* cwd= */ null,
/* configurationChecksum= */ null,
/* executionPlatformLabel= */ null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ protected Subprocess createProcess() throws IOException, UserExecException {
// Mostly tests require network, and some blaze run commands, but no workers.
LinuxSandboxCommandLineBuilder commandLineBuilder =
LinuxSandboxCommandLineBuilder.commandLineBuilder(
this.hardenedSandboxOptions.sandboxBinary(), args)
this.hardenedSandboxOptions.sandboxBinary())
.setWritableFilesAndDirectories(getWritableDirs(workDir))
.setTmpfsDirectories(ImmutableSet.copyOf(this.hardenedSandboxOptions.tmpfsPath()))
.setPersistentProcess(true)
Expand All @@ -202,7 +202,7 @@ protected Subprocess createProcess() throws IOException, UserExecException {
commandLineBuilder.setUseFakeUsername(true);
}

args = commandLineBuilder.build();
args = commandLineBuilder.buildForCommand(args);
}
return createProcessBuilder(args).start();
}
Expand Down
Loading
Loading