| // Copyright 2017 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 static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.assertThrows; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSortedMap; |
| import com.google.common.hash.HashCode; |
| import com.google.devtools.build.lib.actions.ExecutionRequirements.WorkerProtocolFormat; |
| import com.google.devtools.build.lib.vfs.DigestHashFunction; |
| import com.google.devtools.build.lib.vfs.FileSystem; |
| import com.google.devtools.build.lib.vfs.Path; |
| import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; |
| import com.google.devtools.build.lib.worker.WorkerProcessStatus.Status; |
| import java.io.IOException; |
| import org.apache.commons.pool2.impl.DefaultPooledObject; |
| import org.junit.After; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests for {@link WorkerFactory}. */ |
| @RunWith(JUnit4.class) |
| public class WorkerFactoryTest { |
| |
| final FileSystem fs = new InMemoryFileSystem(DigestHashFunction.SHA256); |
| |
| @After |
| public void tearDown() { |
| WorkerMultiplexerManager.resetForTesting(); |
| } |
| |
| /** |
| * Regression test for b/64689608: The execroot of the sandboxed worker process must end with the |
| * workspace name, just like the normal execroot does. |
| */ |
| @Test |
| public void sandboxedWorkerPathEndsWithWorkspaceName() throws Exception { |
| Path workerBaseDir = fs.getPath("/outputbase/bazel-workers"); |
| WorkerFactory workerFactory = new WorkerFactory(workerBaseDir); |
| WorkerKey workerKey = createWorkerKey(/* mustBeSandboxed= */ true, /* multiplex= */ false); |
| Path sandboxedWorkerPath = workerFactory.getSandboxedWorkerPath(workerKey, 1); |
| |
| assertThat(sandboxedWorkerPath.getBaseName()).isEqualTo("workspace"); |
| } |
| |
| /** WorkerFactory should create correct worker type based on WorkerKey. */ |
| @Test |
| public void workerCreationTypeCheck() throws Exception { |
| Path workerBaseDir = fs.getPath("/outputbase/bazel-workers"); |
| WorkerFactory workerFactory = new WorkerFactory(workerBaseDir); |
| WorkerKey sandboxedWorkerKey = |
| createWorkerKey(/* mustBeSandboxed= */ true, /* multiplex= */ false); |
| Worker sandboxedWorker = workerFactory.create(sandboxedWorkerKey); |
| assertThat(sandboxedWorker.getClass()).isEqualTo(SandboxedWorker.class); |
| |
| WorkerKey nonProxiedWorkerKey = |
| createWorkerKey(/* mustBeSandboxed= */ false, /* multiplex= */ false); |
| Worker nonProxiedWorker = workerFactory.create(nonProxiedWorkerKey); |
| assertThat(nonProxiedWorker.getClass()).isEqualTo(SingleplexWorker.class); |
| |
| WorkerKey proxiedWorkerKey = |
| createWorkerKey(/* mustBeSandboxed= */ false, /* multiplex= */ true); |
| Worker proxiedWorker = workerFactory.create(proxiedWorkerKey); |
| // If proxied = true, WorkerProxy is created along with a WorkerMultiplexer. |
| // Destroy WorkerMultiplexer to avoid unexpected behavior in WorkerMultiplexerManagerTest. |
| WorkerMultiplexerManager.removeInstance(proxiedWorkerKey); |
| assertThat(proxiedWorker.getClass()).isEqualTo(WorkerProxy.class); |
| } |
| |
| /** Proxied workers with the same WorkerKey should share the log file. */ |
| @Test |
| public void testMultiplexWorkersShareLogfiles() throws Exception { |
| Path workerBaseDir = fs.getPath("/outputbase/bazel-workers"); |
| WorkerFactory workerFactory = new WorkerFactory(workerBaseDir); |
| |
| WorkerKey workerKey1 = |
| createWorkerKey(/* mustBeSandboxed= */ false, /* multiplex= */ true, "arg1"); |
| Worker proxiedWorker1a = workerFactory.create(workerKey1); |
| Worker proxiedWorker1b = workerFactory.create(workerKey1); |
| WorkerKey workerKey2 = |
| createWorkerKey(/* mustBeSandboxed= */ false, /* multiplex= */ true, "arg2"); |
| Worker proxiedWorker2 = workerFactory.create(workerKey2); |
| assertThat(proxiedWorker1a.getLogFile()).isEqualTo(proxiedWorker1b.getLogFile()); |
| assertThat(proxiedWorker1a.getLogFile()).isNotEqualTo(proxiedWorker2.getLogFile()); |
| } |
| |
| /** WorkerFactory should create the base dir if needed and fail if that's impossible. */ |
| @Test |
| public void testCreate_createsWorkerDirectory() throws Exception { |
| Path workerBaseDir = fs.getPath("/outputbase/bazel-workers"); |
| WorkerFactory workerFactory = new WorkerFactory(workerBaseDir); |
| WorkerKey sandboxedWorkerKey = createWorkerKey(/* mustBeSandboxed */ true, /* proxied */ false); |
| assertThat(workerBaseDir.isDirectory()).isFalse(); |
| workerFactory.create(sandboxedWorkerKey); |
| assertThat(workerBaseDir.isDirectory()).isTrue(); |
| |
| workerBaseDir.delete(); |
| workerBaseDir.createSymbolicLink(workerBaseDir.getRelative("whatevs")); |
| assertThat(workerBaseDir.isDirectory()).isFalse(); |
| assertThrows(IOException.class, () -> workerFactory.create(sandboxedWorkerKey)); |
| } |
| |
| @Test |
| public void testDoomedWorkerValidation() throws Exception { |
| Path workerBaseDir = fs.getPath("/outputbase/bazel-workers"); |
| WorkerFactory workerFactory = new WorkerFactory(workerBaseDir); |
| |
| WorkerKey workerKey = |
| createWorkerKey(/* mustBeSandboxed= */ false, /* multiplex= */ false, "arg1"); |
| Worker worker = workerFactory.create(workerKey); |
| |
| assertThat(workerFactory.validateObject(workerKey, new DefaultPooledObject<>(worker))).isTrue(); |
| |
| worker.getStatus().maybeUpdateStatus(Status.KILLED_DUE_TO_MEMORY_PRESSURE); |
| |
| assertThat(workerFactory.validateObject(workerKey, new DefaultPooledObject<>(worker))) |
| .isFalse(); |
| } |
| |
| protected WorkerKey createWorkerKey(boolean mustBeSandboxed, boolean multiplex, String... args) { |
| return new WorkerKey( |
| /* args= */ ImmutableList.copyOf(args), |
| /* env= */ ImmutableMap.of(), |
| /* execRoot= */ fs.getPath("/outputbase/execroot/workspace"), |
| /* mnemonic= */ "dummy", |
| /* workerFilesCombinedHash= */ HashCode.fromInt(0), |
| /* workerFilesWithDigests= */ ImmutableSortedMap.of(), |
| /* sandboxed= */ mustBeSandboxed, |
| /* multiplex= */ multiplex, |
| /* cancellable= */ false, |
| WorkerProtocolFormat.PROTO); |
| } |
| } |