| // Copyright 2021 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.devtools.build.lib.worker; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| import com.google.common.flogger.GoogleLogger; |
| import com.google.devtools.build.lib.sandbox.SandboxHelpers; |
| import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; |
| import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs; |
| import com.google.devtools.build.lib.vfs.Path; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; |
| import java.io.IOException; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.Set; |
| |
| /** |
| * A multiplex worker proxy with sandboxing. The multiplexer process runs in {@code workDir}, while |
| * each proxy has a fixed subdir where it sets up its files. The subdir is then passed to the worker |
| * in {@link WorkRequest#sandbox_dir}. The worker implementation is responsible for reading from and |
| * writing to that subdir only. |
| */ |
| public class SandboxedWorkerProxy extends WorkerProxy { |
| private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); |
| /** The sandbox directory for the current request, inside {@code workDir}. */ |
| private final Path sandboxDir; |
| |
| private final PathFragment sandboxName; |
| |
| SandboxedWorkerProxy( |
| WorkerKey workerKey, |
| int workerId, |
| Path logFile, |
| WorkerMultiplexer workerMultiplexer, |
| Path workDir) { |
| super(workerKey, workerId, logFile, workerMultiplexer, workDir); |
| sandboxName = |
| PathFragment.create( |
| Joiner.on(PathFragment.SEPARATOR_CHAR) |
| .join( |
| "__sandbox", |
| Integer.toString(workerId), |
| workerKey.getExecRoot().getBaseName())); |
| sandboxDir = this.workDir.getRelative(sandboxName); |
| } |
| |
| @Override |
| public boolean isSandboxed() { |
| return true; |
| } |
| |
| @Override |
| public void prepareExecution( |
| SandboxInputs inputFiles, SandboxOutputs outputs, Set<PathFragment> workerFiles) |
| throws IOException, InterruptedException { |
| workerMultiplexer.createSandboxedProcess(workDir, workerFiles, inputFiles); |
| |
| sandboxDir.createDirectoryAndParents(); |
| LinkedHashSet<PathFragment> dirsToCreate = new LinkedHashSet<>(); |
| Set<PathFragment> inputsToCreate = new HashSet<>(); |
| |
| SandboxHelpers.populateInputsAndDirsToCreate( |
| ImmutableSet.of(), |
| inputsToCreate, |
| dirsToCreate, |
| Iterables.concat(inputFiles.getFiles().keySet(), inputFiles.getSymlinks().keySet()), |
| outputs); |
| SandboxHelpers.cleanExisting( |
| sandboxDir.getParentDirectory(), inputFiles, inputsToCreate, dirsToCreate, sandboxDir); |
| // Finally, create anything that is still missing. This is non-strict only for historical |
| // reasons, we haven't seen what would break if we make it strict. |
| SandboxHelpers.createDirectories(dirsToCreate, sandboxDir, /* strict=*/ false); |
| WorkerExecRoot.createInputs(inputsToCreate, inputFiles, sandboxDir); |
| } |
| |
| /** Send the WorkRequest to multiplexer. */ |
| @Override |
| protected void putRequest(WorkRequest request) throws IOException { |
| // Modifying the request on the way out is not great. The alternatives are having the |
| // spawn runner ask the worker for the dir or making the spawn runner understand the sandbox, |
| // dir structure, neither of which are nice either. |
| workerMultiplexer.putRequest( |
| request.toBuilder().setSandboxDir(sandboxName.getPathString()).build()); |
| } |
| |
| @Override |
| public void finishExecution(Path execRoot, SandboxOutputs outputs) throws IOException { |
| SandboxHelpers.moveOutputs(outputs, sandboxDir, execRoot); |
| } |
| |
| @Override |
| synchronized void destroy() { |
| super.destroy(); |
| try { |
| workDir.deleteTree(); |
| } catch (IOException e) { |
| logger.atWarning().withCause(e).log("Caught IOException while deleting workdir."); |
| } |
| } |
| } |