summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHÃ¥kon Hallingstad <hakon@verizonmedia.com>2021-10-15 16:54:45 +0200
committerGitHub <noreply@github.com>2021-10-15 16:54:45 +0200
commita0de877aed8c7303ebf835e889db7314dd9fbb4e (patch)
tree0795e758945c1826d4aa4453449f41c4c157069a
parente138d9f977423cda5a200eab82383d73c20ee05e (diff)
parenta2486bb73aadb9acccbca88670653b3a007afd19 (diff)
Merge pull request #19591 from vespa-engine/revert-19568-freva/uids
Revert "Use ContainerPath"
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerOperations.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java32
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java19
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java58
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java46
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/AbstractProducer.java6
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/Artifact.java22
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/ArtifactProducer.java7
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/JvmDumper.java32
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PerfReporter.java15
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PmapReporter.java9
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaLogDumper.java8
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java49
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/ContainerData.java27
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContext.java39
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java83
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java7
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespace.java48
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystem.java10
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemProvider.java41
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupService.java89
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/package-info.java5
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerFailTest.java3
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainerTest.java30
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java18
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java95
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java3
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImplTest.java25
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextManagerTest.java3
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java7
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespaceTest.java29
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemTest.java71
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerPathTest.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupServiceTest.java30
34 files changed, 472 insertions, 500 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerOperations.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerOperations.java
index fd38d38b381..acac2098d53 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerOperations.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerOperations.java
@@ -141,7 +141,7 @@ public class ContainerOperations {
}
private String executeNodeCtlInContainer(NodeAgentContext context, String program) {
- String[] command = new String[] {context.containerPathUnderVespaHome("bin/vespa-nodectl").pathInContainer(), program};
+ String[] command = new String[] {context.pathInNodeUnderVespaHome("bin/vespa-nodectl").toString(), program};
return executeCommandInContainerAsRoot(context, command).getOutput();
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
index 9328eb232a6..340f43b4671 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
@@ -22,7 +22,6 @@ import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentTask;
import com.yahoo.vespa.hosted.node.admin.task.util.file.DiskSize;
import com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder;
import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.Terminal;
import java.net.URI;
@@ -86,7 +85,7 @@ public class StorageMaintainer {
if (archiveUri.isEmpty()) return false;
ApplicationId owner = context.node().owner().orElseThrow();
- List<SyncFileInfo> syncFileInfos = FileFinder.files(context.containerPathUnderVespaHome("logs/vespa"))
+ List<SyncFileInfo> syncFileInfos = FileFinder.files(pathOnHostUnderContainerVespaHome(context, "logs/vespa"))
.maxDepth(2)
.stream()
.sorted(Comparator.comparing(FileFinder.FileAttributes::lastModifiedTime))
@@ -101,7 +100,7 @@ public class StorageMaintainer {
DiskSize cachedDiskUsage = diskUsage.getIfPresent(context.containerName());
if (cachedDiskUsage != null) return Optional.of(cachedDiskUsage);
- DiskSize diskUsageBytes = getDiskUsed(context, context.containerPath("/").pathOnHost());
+ DiskSize diskUsageBytes = getDiskUsed(context, context.pathOnHostFromPathInNode("/"));
diskUsage.put(context.containerName(), diskUsageBytes);
return Optional.of(diskUsageBytes);
} catch (Exception e) {
@@ -110,11 +109,11 @@ public class StorageMaintainer {
}
}
- DiskSize getDiskUsed(TaskContext context, Path pathOnHost) {
- if (!Files.exists(pathOnHost)) return DiskSize.ZERO;
+ DiskSize getDiskUsed(TaskContext context, Path path) {
+ if (!Files.exists(path)) return DiskSize.ZERO;
String output = terminal.newCommandLine(context)
- .add("du", "-xsk", pathOnHost.toString())
+ .add("du", "-xsk", path.toString())
.setTimeout(Duration.ofSeconds(60))
.executeSilently()
.getOutput();
@@ -150,18 +149,18 @@ public class StorageMaintainer {
Function<Instant, Double> monthNormalizer = instant -> Duration.between(instant, start).getSeconds() / oneMonthSeconds;
List<DiskCleanupRule> rules = new ArrayList<>();
- rules.add(CoredumpCleanupRule.forContainer(context.containerPathUnderVespaHome("var/crash")));
+ rules.add(CoredumpCleanupRule.forContainer(pathOnHostUnderContainerVespaHome(context, "var/crash")));
if (context.node().membership().map(m -> m.type().hasContainer()).orElse(false))
- rules.add(new LinearCleanupRule(() -> FileFinder.files(context.containerPathUnderVespaHome("logs/vespa/qrs")).list(),
+ rules.add(new LinearCleanupRule(() -> FileFinder.files(pathOnHostUnderContainerVespaHome(context, "logs/vespa/qrs")).list(),
fa -> monthNormalizer.apply(fa.lastModifiedTime()), Priority.LOWEST, Priority.HIGHEST));
if (context.nodeType() == NodeType.tenant && context.node().membership().map(m -> m.type().isAdmin()).orElse(false))
- rules.add(new LinearCleanupRule(() -> FileFinder.files(context.containerPathUnderVespaHome("logs/vespa/logarchive")).list(),
+ rules.add(new LinearCleanupRule(() -> FileFinder.files(pathOnHostUnderContainerVespaHome(context, "logs/vespa/logarchive")).list(),
fa -> monthNormalizer.apply(fa.lastModifiedTime()), Priority.LOWEST, Priority.HIGHEST));
if (context.nodeType() == NodeType.proxy)
- rules.add(new LinearCleanupRule(() -> FileFinder.files(context.containerPathUnderVespaHome("logs/nginx")).list(),
+ rules.add(new LinearCleanupRule(() -> FileFinder.files(pathOnHostUnderContainerVespaHome(context, "logs/nginx")).list(),
fa -> monthNormalizer.apply(fa.lastModifiedTime()), Priority.LOWEST, Priority.MEDIUM));
return rules;
@@ -203,17 +202,16 @@ public class StorageMaintainer {
* Removes old files, reports coredumps and archives container data, runs when container enters state "dirty"
*/
public void archiveNodeStorage(NodeAgentContext context) {
- ContainerPath logsDirInContainer = context.containerPathUnderVespaHome("logs");
+ Path logsDirInContainer = context.pathInNodeUnderVespaHome("logs");
Path containerLogsInArchiveDir = archiveContainerStoragePath
- .resolve(context.containerName().asString() + "_" + DATE_TIME_FORMATTER.format(clock.instant()) + logsDirInContainer.pathInContainer());
+ .resolve(context.containerName().asString() + "_" + DATE_TIME_FORMATTER.format(clock.instant()) + logsDirInContainer);
+ UnixPath containerLogsOnHost = new UnixPath(context.pathOnHostFromPathInNode(logsDirInContainer));
- // Files.move() does not support moving non-empty directories across providers, move using host paths
- UnixPath containerLogsOnHost = new UnixPath(logsDirInContainer.pathOnHost());
if (containerLogsOnHost.exists()) {
new UnixPath(containerLogsInArchiveDir).createParents();
containerLogsOnHost.moveIfExists(containerLogsInArchiveDir);
}
- new UnixPath(context.containerPath("/")).deleteRecursively();
+ new UnixPath(context.pathOnHostFromPathInNode("/")).deleteRecursively();
}
private String getMicrocodeVersion() {
@@ -237,4 +235,8 @@ public class StorageMaintainer {
.orElse("<none>")
);
}
+
+ private static Path pathOnHostUnderContainerVespaHome(NodeAgentContext context, String path) {
+ return context.pathOnHostFromPathInNode(context.pathInNodeUnderVespaHome(path));
+ }
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java
index c5c8d0e121d..2c0fa439000 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java
@@ -4,7 +4,6 @@ package com.yahoo.vespa.hosted.node.admin.maintenance.coredump;
import com.yahoo.vespa.hosted.node.admin.container.ContainerOperations;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.ConvergenceException;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.CommandResult;
import java.nio.file.Path;
@@ -57,8 +56,8 @@ public class CoreCollector {
return GDB_PATH_RHEL8;
}
- String readBinPathFallback(NodeAgentContext context, ContainerPath coredumpPath) {
- String command = getGdbPath(context) + " -n -batch -core " + coredumpPath.pathInContainer() + " | grep \'^Core was generated by\'";
+ String readBinPathFallback(NodeAgentContext context, Path coredumpPath) {
+ String command = getGdbPath(context) + " -n -batch -core " + coredumpPath + " | grep \'^Core was generated by\'";
String[] wrappedCommand = {"/bin/sh", "-c", command};
CommandResult result = docker.executeCommandInContainerAsRoot(context, wrappedCommand);
@@ -70,8 +69,8 @@ public class CoreCollector {
return matcher.group("path").split(" ")[0];
}
- String readBinPath(NodeAgentContext context, ContainerPath coredumpPath) {
- String[] command = {"file", coredumpPath.pathInContainer()};
+ String readBinPath(NodeAgentContext context, Path coredumpPath) {
+ String[] command = {"file", coredumpPath.toString()};
try {
CommandResult result = docker.executeCommandInContainerAsRoot(context, command);
if (result.getExitCode() != 0) {
@@ -95,9 +94,9 @@ public class CoreCollector {
return readBinPathFallback(context, coredumpPath);
}
- List<String> readBacktrace(NodeAgentContext context, ContainerPath coredumpPath, String binPath, boolean allThreads) {
+ List<String> readBacktrace(NodeAgentContext context, Path coredumpPath, String binPath, boolean allThreads) {
String threads = allThreads ? "thread apply all bt" : "bt";
- String[] command = {getGdbPath(context), "-n", "-ex", threads, "-batch", binPath, coredumpPath.pathInContainer()};
+ String[] command = {getGdbPath(context), "-n", "-ex", threads, "-batch", binPath, coredumpPath.toString()};
CommandResult result = docker.executeCommandInContainerAsRoot(context, command);
if (result.getExitCode() != 0)
@@ -106,8 +105,8 @@ public class CoreCollector {
return List.of(result.getOutput().split("\n"));
}
- List<String> readJstack(NodeAgentContext context, ContainerPath coredumpPath, String binPath) {
- String[] command = {"jhsdb", "jstack", "--exe", binPath, "--core", coredumpPath.pathInContainer()};
+ List<String> readJstack(NodeAgentContext context, Path coredumpPath, String binPath) {
+ String[] command = {"jhsdb", "jstack", "--exe", binPath, "--core", coredumpPath.toString()};
CommandResult result = docker.executeCommandInContainerAsRoot(context, command);
if (result.getExitCode() != 0)
@@ -122,7 +121,7 @@ public class CoreCollector {
* @param coredumpPath path to core dump file inside the container
* @return map of relevant metadata about the core dump
*/
- Map<String, Object> collect(NodeAgentContext context, ContainerPath coredumpPath) {
+ Map<String, Object> collect(NodeAgentContext context, Path coredumpPath) {
if (JAVA_HEAP_DUMP_PATTERN.matcher(coredumpPath.getFileName().toString()).find())
return JAVA_HEAP_DUMP_METADATA;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java
index 0594c5ee016..4c0ffa3dce9 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java
@@ -9,7 +9,6 @@ import com.yahoo.vespa.hosted.node.admin.nodeadmin.ConvergenceException;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder;
import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.Terminal;
import java.io.IOException;
@@ -87,13 +86,13 @@ public class CoredumpHandler {
public void converge(NodeAgentContext context, Supplier<Map<String, Object>> nodeAttributesSupplier, boolean throwIfCoreBeingWritten) {
- ContainerPath containerCrashPath = context.containerPath(crashPatchInContainer);
- ContainerPath containerProcessingPath = containerCrashPath.resolve(PROCESSING_DIRECTORY_NAME);
+ Path containerCrashPathOnHost = context.pathOnHostFromPathInNode(crashPatchInContainer);
+ Path containerProcessingPathOnHost = containerCrashPathOnHost.resolve(PROCESSING_DIRECTORY_NAME);
- updateMetrics(context, containerCrashPath);
+ updateMetrics(context, containerCrashPathOnHost);
if (throwIfCoreBeingWritten) {
- List<String> pendingCores = FileFinder.files(containerCrashPath)
+ List<String> pendingCores = FileFinder.files(containerCrashPathOnHost)
.match(fileAttributes -> !isReadyForProcessing(fileAttributes))
.maxDepth(1).stream()
.map(FileFinder.FileAttributes::filename)
@@ -104,17 +103,16 @@ public class CoredumpHandler {
}
// Check if we have already started to process a core dump or we can enqueue a new core one
- getCoredumpToProcess(containerCrashPath, containerProcessingPath)
+ getCoredumpToProcess(containerCrashPathOnHost, containerProcessingPathOnHost)
.ifPresent(path -> processAndReportSingleCoredump(context, path, nodeAttributesSupplier));
}
/** @return path to directory inside processing directory that contains a core dump file to process */
- Optional<ContainerPath> getCoredumpToProcess(ContainerPath containerCrashPath, ContainerPath containerProcessingPath) {
- return FileFinder.directories(containerProcessingPath).stream()
+ Optional<Path> getCoredumpToProcess(Path containerCrashPathOnHost, Path containerProcessingPathOnHost) {
+ return FileFinder.directories(containerProcessingPathOnHost).stream()
.map(FileFinder.FileAttributes::path)
.findAny()
- .map(ContainerPath.class::cast)
- .or(() -> enqueueCoredump(containerCrashPath, containerProcessingPath));
+ .or(() -> enqueueCoredump(containerCrashPathOnHost, containerProcessingPathOnHost));
}
/**
@@ -126,8 +124,8 @@ public class CoredumpHandler {
*
* @return path to directory inside processing directory which contains the enqueued core dump file
*/
- Optional<ContainerPath> enqueueCoredump(ContainerPath containerCrashPath, ContainerPath containerProcessingPath) {
- List<Path> toProcess = FileFinder.files(containerCrashPath)
+ Optional<Path> enqueueCoredump(Path containerCrashPathOnHost, Path containerProcessingPathOnHost) {
+ List<Path> toProcess = FileFinder.files(containerCrashPathOnHost)
.match(this::isReadyForProcessing)
.maxDepth(1)
.stream()
@@ -143,7 +141,7 @@ public class CoredumpHandler {
// Either there are no files in crash directory, or all the files are hs_err files.
if (coredumpIndex == -1) return Optional.empty();
- ContainerPath enqueuedDir = (ContainerPath) uncheck(() -> Files.createDirectories(containerProcessingPath.resolve(coredumpIdSupplier.get())));
+ Path enqueuedDir = uncheck(() -> Files.createDirectories(containerProcessingPathOnHost.resolve(coredumpIdSupplier.get())));
IntStream.range(0, coredumpIndex + 1)
.forEach(i -> {
Path path = toProcess.get(i);
@@ -153,7 +151,7 @@ public class CoredumpHandler {
return Optional.of(enqueuedDir);
}
- void processAndReportSingleCoredump(NodeAgentContext context, ContainerPath coredumpDirectory, Supplier<Map<String, Object>> nodeAttributesSupplier) {
+ void processAndReportSingleCoredump(NodeAgentContext context, Path coredumpDirectory, Supplier<Map<String, Object>> nodeAttributesSupplier) {
try {
String metadata = getMetadata(context, coredumpDirectory, nodeAttributesSupplier);
String coredumpId = coredumpDirectory.getFileName().toString();
@@ -169,16 +167,17 @@ public class CoredumpHandler {
* @return coredump metadata from metadata.json if present, otherwise attempts to get metadata using
* {@link CoreCollector} and stores it to metadata.json
*/
- String getMetadata(NodeAgentContext context, ContainerPath coredumpDirectory, Supplier<Map<String, Object>> nodeAttributesSupplier) throws IOException {
+ String getMetadata(NodeAgentContext context, Path coredumpDirectory, Supplier<Map<String, Object>> nodeAttributesSupplier) throws IOException {
UnixPath metadataPath = new UnixPath(coredumpDirectory.resolve(METADATA_FILE_NAME));
if (!Files.exists(metadataPath.toPath())) {
- ContainerPath coredumpFile = findCoredumpFileInProcessingDirectory(coredumpDirectory);
- Map<String, Object> metadata = new HashMap<>(coreCollector.collect(context, coredumpFile));
+ Path coredumpFilePathOnHost = findCoredumpFileInProcessingDirectory(coredumpDirectory);
+ Path coredumpFilePathInContainer = context.pathInNodeFromPathOnHost(coredumpFilePathOnHost);
+ Map<String, Object> metadata = new HashMap<>(coreCollector.collect(context, coredumpFilePathInContainer));
metadata.putAll(nodeAttributesSupplier.get());
metadata.put("coredump_path", doneCoredumpsPath
.resolve(context.containerName().asString())
- .resolve(coredumpDirectory.getFileName().toString())
- .resolve(coredumpFile.getFileName().toString()).toString());
+ .resolve(coredumpDirectory.getFileName())
+ .resolve(coredumpFilePathOnHost.getFileName()).toString());
String metadataFields = objectMapper.writeValueAsString(Map.of("fields", metadata));
metadataPath.writeUtf8File(metadataFields);
@@ -192,24 +191,23 @@ public class CoredumpHandler {
* Compresses core file (and deletes the uncompressed core), then moves the entire core dump processing
* directory to {@link #doneCoredumpsPath} for archive
*/
- private void finishProcessing(NodeAgentContext context, ContainerPath coredumpDirectory) throws IOException {
- ContainerPath coreFile = findCoredumpFileInProcessingDirectory(coredumpDirectory);
- ContainerPath compressedCoreFile = coreFile.resolveSibling(coreFile.getFileName() + ".lz4");
+ private void finishProcessing(NodeAgentContext context, Path coredumpDirectory) throws IOException {
+ Path coreFile = findCoredumpFileInProcessingDirectory(coredumpDirectory);
+ Path compressedCoreFile = coreFile.getParent().resolve(coreFile.getFileName() + ".lz4");
terminal.newCommandLine(context)
- .add(LZ4_PATH, "-f", coreFile.pathOnHost().toString(), compressedCoreFile.pathOnHost().toString())
+ .add(LZ4_PATH, "-f", coreFile.toString(), compressedCoreFile.toString())
.setTimeout(Duration.ofMinutes(30))
.execute();
- new UnixPath(compressedCoreFile.pathOnHost()).setGroupId(operatorGroupId).setPermissions("rw-r-----");
+ new UnixPath(compressedCoreFile).setGroupId(operatorGroupId).setPermissions("rw-r-----");
Files.delete(coreFile);
Path newCoredumpDirectory = doneCoredumpsPath.resolve(context.containerName().asString());
uncheck(() -> Files.createDirectories(newCoredumpDirectory));
- // Files.move() does not support moving non-empty directories across providers, move using host paths
- Files.move(coredumpDirectory.pathOnHost(), newCoredumpDirectory.resolve(coredumpDirectory.getFileName().toString()));
+ Files.move(coredumpDirectory, newCoredumpDirectory.resolve(coredumpDirectory.getFileName()));
}
- ContainerPath findCoredumpFileInProcessingDirectory(ContainerPath coredumpProccessingDirectory) {
- return (ContainerPath) FileFinder.files(coredumpProccessingDirectory)
+ Path findCoredumpFileInProcessingDirectory(Path coredumpProccessingDirectory) {
+ return FileFinder.files(coredumpProccessingDirectory)
.match(nameStartsWith(COREDUMP_FILENAME_PREFIX).and(nameEndsWith(".lz4").negate()))
.maxDepth(1)
.stream()
@@ -219,11 +217,11 @@ public class CoredumpHandler {
"No coredump file found in processing directory " + coredumpProccessingDirectory));
}
- void updateMetrics(NodeAgentContext context, ContainerPath containerCrashPath) {
+ void updateMetrics(NodeAgentContext context, Path containerCrashPathOnHost) {
Dimensions dimensions = generateDimensions(context);
// Unprocessed coredumps
- int numberOfUnprocessedCoredumps = FileFinder.files(containerCrashPath)
+ int numberOfUnprocessedCoredumps = FileFinder.files(containerCrashPathOnHost)
.match(nameStartsWith(".").negate())
.match(nameMatches(HS_ERR_PATTERN).negate())
.match(nameEndsWith(".lz4").negate())
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
index 280e58c91f1..280ebd86d13 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
@@ -26,15 +26,17 @@ import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentTask;
import com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder;
import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
@@ -108,10 +110,10 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer {
try {
context.log(logger, Level.FINE, "Checking certificate");
- ContainerPath containerSiaDirectory = context.containerPath(CONTAINER_SIA_DIRECTORY);
- ContainerPath privateKeyFile = (ContainerPath) SiaUtils.getPrivateKeyFile(containerSiaDirectory, context.identity());
- ContainerPath certificateFile = (ContainerPath) SiaUtils.getCertificateFile(containerSiaDirectory, context.identity());
- ContainerPath identityDocumentFile = containerSiaDirectory.resolve("vespa-node-identity-document.json");
+ Path containerSiaDirectory = context.pathOnHostFromPathInNode(CONTAINER_SIA_DIRECTORY);
+ Path privateKeyFile = SiaUtils.getPrivateKeyFile(containerSiaDirectory, context.identity());
+ Path certificateFile = SiaUtils.getCertificateFile(containerSiaDirectory, context.identity());
+ Path identityDocumentFile = containerSiaDirectory.resolve("vespa-node-identity-document.json");
if (!Files.exists(privateKeyFile) || !Files.exists(certificateFile) || !Files.exists(identityDocumentFile)) {
context.log(logger, "Certificate/private key/identity document file does not exist");
Files.createDirectories(privateKeyFile.getParent());
@@ -152,15 +154,15 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer {
}
public void clearCredentials(NodeAgentContext context) {
- FileFinder.files(context.containerPath(CONTAINER_SIA_DIRECTORY))
+ FileFinder.files(context.pathOnHostFromPathInNode(CONTAINER_SIA_DIRECTORY))
.deleteRecursively(context);
lastRefreshAttempt.remove(context.containerName());
}
@Override
public Duration certificateLifetime(NodeAgentContext context) {
- ContainerPath containerSiaDirectory = context.containerPath(CONTAINER_SIA_DIRECTORY);
- ContainerPath certificateFile = (ContainerPath) SiaUtils.getCertificateFile(containerSiaDirectory, context.identity());
+ Path containerSiaDirectory = context.pathOnHostFromPathInNode(CONTAINER_SIA_DIRECTORY);
+ Path certificateFile = SiaUtils.getCertificateFile(containerSiaDirectory, context.identity());
try {
X509Certificate certificate = readCertificateFromFile(certificateFile);
Instant now = clock.instant();
@@ -188,7 +190,7 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer {
now)) > 0;
}
- private void registerIdentity(NodeAgentContext context, ContainerPath privateKeyFile, ContainerPath certificateFile, ContainerPath identityDocumentFile) {
+ private void registerIdentity(NodeAgentContext context, Path privateKeyFile, Path certificateFile, Path identityDocumentFile) {
KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
SignedIdentityDocument signedIdentityDocument = identityDocumentClient.getNodeIdentityDocument(context.hostname().value());
Pkcs10Csr csr = csrGenerator.generateInstanceCsr(
@@ -206,13 +208,13 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer {
EntityBindingsMapper.toAttestationData(signedIdentityDocument),
csr);
EntityBindingsMapper.writeSignedIdentityDocumentToFile(identityDocumentFile, signedIdentityDocument);
- writePrivateKeyAndCertificate(context.userNamespace().vespaUserId(),
- privateKeyFile, keyPair.getPrivate(), certificateFile, instanceIdentity.certificate());
+ writePrivateKeyAndCertificate(context.userNamespace().vespaUserIdOnHost(), privateKeyFile, keyPair.getPrivate(),
+ certificateFile, instanceIdentity.certificate());
context.log(logger, "Instance successfully registered and credentials written to file");
}
}
- private void refreshIdentity(NodeAgentContext context, ContainerPath privateKeyFile, ContainerPath certificateFile, ContainerPath identityDocumentFile) {
+ private void refreshIdentity(NodeAgentContext context, Path privateKeyFile, Path certificateFile, Path identityDocumentFile) {
SignedIdentityDocument identityDocument = EntityBindingsMapper.readSignedIdentityDocumentFromFile(identityDocumentFile);
KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
Pkcs10Csr csr = csrGenerator.generateInstanceCsr(
@@ -234,8 +236,8 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer {
context.identity(),
identityDocument.providerUniqueId().asDottedString(),
csr);
- writePrivateKeyAndCertificate(context.userNamespace().vespaUserId(),
- privateKeyFile, keyPair.getPrivate(), certificateFile, instanceIdentity.certificate());
+ writePrivateKeyAndCertificate(context.userNamespace().vespaUserIdOnHost(), privateKeyFile, keyPair.getPrivate(),
+ certificateFile, instanceIdentity.certificate());
context.log(logger, "Instance successfully refreshed and credentials written to file");
} catch (ZtsClientException e) {
if (e.getErrorCode() == 403 && e.getDescription().startsWith("Certificate revoked")) {
@@ -251,23 +253,23 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer {
}
- private static void writePrivateKeyAndCertificate(int vespaUid,
- ContainerPath privateKeyFile,
+ private static void writePrivateKeyAndCertificate(int vespaUidOnHost,
+ Path privateKeyFile,
PrivateKey privateKey,
- ContainerPath certificateFile,
+ Path certificateFile,
X509Certificate certificate) {
- writeFile(privateKeyFile, vespaUid, KeyUtils.toPem(privateKey));
- writeFile(certificateFile, vespaUid, X509CertificateUtils.toPem(certificate));
+ writeFile(privateKeyFile, vespaUidOnHost, KeyUtils.toPem(privateKey));
+ writeFile(certificateFile, vespaUidOnHost, X509CertificateUtils.toPem(certificate));
}
- private static void writeFile(ContainerPath path, int vespaUid, String utf8Content) {
+ private static void writeFile(Path path, int vespaUidOnHost, String utf8Content) {
new UnixPath(path.resolveSibling(path.getFileName() + ".tmp"))
.writeUtf8File(utf8Content, "r--------")
- .setOwnerId(vespaUid)
+ .setOwnerId(vespaUidOnHost)
.atomicMove(path);
}
- private static X509Certificate readCertificateFromFile(ContainerPath certificateFile) throws IOException {
+ private static X509Certificate readCertificateFromFile(Path certificateFile) throws IOException {
String pemEncodedCertificate = new String(Files.readAllBytes(certificateFile));
return X509CertificateUtils.fromPem(pemEncodedCertificate);
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/AbstractProducer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/AbstractProducer.java
index 1756b81f795..6bcf41b89c2 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/AbstractProducer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/AbstractProducer.java
@@ -3,10 +3,10 @@ package com.yahoo.vespa.hosted.node.admin.maintenance.servicedump;
import com.yahoo.vespa.hosted.node.admin.container.ContainerOperations;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.CommandResult;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -48,8 +48,8 @@ abstract class AbstractProducer implements ArtifactProducer {
}
protected int findVespaServicePid(NodeAgentContext ctx, String configId) throws IOException {
- ContainerPath findPidBinary = ctx.containerPathUnderVespaHome("libexec/vespa/find-pid");
- CommandResult findPidResult = executeCommand(ctx, List.of(findPidBinary.pathInContainer(), configId), true);
+ Path findPidBinary = ctx.pathInNodeUnderVespaHome("libexec/vespa/find-pid");
+ CommandResult findPidResult = executeCommand(ctx, List.of(findPidBinary.toString(), configId), true);
return Integer.parseInt(findPidResult.getOutput());
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/Artifact.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/Artifact.java
index 6f29a9c2558..edab90afea7 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/Artifact.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/Artifact.java
@@ -1,8 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.servicedump;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
-
+import java.nio.file.Path;
import java.util.Optional;
/**
@@ -22,14 +21,18 @@ class Artifact {
}
private final Classification classification;
- private final ContainerPath file;
+ private final Path fileInNode;
+ private final Path fileOnHost;
private final boolean compressOnUpload;
private Artifact(Builder builder) {
- if (builder.file == null) {
+ if (builder.fileOnHost == null && builder.fileInNode == null) {
throw new IllegalArgumentException("No file specified");
+ } else if (builder.fileOnHost != null && builder.fileInNode != null) {
+ throw new IllegalArgumentException("Only a single file can be specified");
}
- this.file = builder.file;
+ this.fileInNode = builder.fileInNode;
+ this.fileOnHost = builder.fileOnHost;
this.classification = builder.classification;
this.compressOnUpload = Boolean.TRUE.equals(builder.compressOnUpload);
}
@@ -37,18 +40,21 @@ class Artifact {
static Builder newBuilder() { return new Builder(); }
Optional<Classification> classification() { return Optional.ofNullable(classification); }
- ContainerPath file() { return file; }
+ Optional<Path> fileInNode() { return Optional.ofNullable(fileInNode); }
+ Optional<Path> fileOnHost() { return Optional.ofNullable(fileOnHost); }
boolean compressOnUpload() { return compressOnUpload; }
static class Builder {
private Classification classification;
- private ContainerPath file;
+ private Path fileInNode;
+ private Path fileOnHost;
private Boolean compressOnUpload;
private Builder() {}
Builder classification(Classification c) { this.classification = c; return this; }
- Builder file(ContainerPath f) { this.file = f; return this; }
+ Builder fileInNode(Path f) { this.fileInNode = f; return this; }
+ Builder fileOnHost(Path f) { this.fileOnHost = f; return this; }
Builder compressOnUpload() { this.compressOnUpload = true; return this; }
Artifact build() { return new Artifact(this); }
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/ArtifactProducer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/ArtifactProducer.java
index 0394756cc77..e4a9e6aeea5 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/ArtifactProducer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/ArtifactProducer.java
@@ -1,9 +1,9 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.servicedump;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.CommandResult;
+import java.nio.file.Path;
import java.util.List;
import java.util.OptionalDouble;
@@ -22,8 +22,9 @@ interface ArtifactProducer {
String serviceId();
int servicePid();
CommandResult executeCommandInNode(List<String> command, boolean logOutput);
- ContainerPath outputContainerPath();
- ContainerPath containerPathUnderVespaHome(String relativePath);
+ Path outputDirectoryInNode();
+ Path pathInNodeUnderVespaHome(String relativePath);
+ Path pathOnHostFromPathInNode(Path pathInNode);
Options options();
interface Options {
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/JvmDumper.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/JvmDumper.java
index 8b6ca1384e9..cf206918568 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/JvmDumper.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/JvmDumper.java
@@ -1,9 +1,9 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.servicedump;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.yolean.concurrent.Sleeper;
+import java.nio.file.Path;
import java.time.Duration;
import java.util.List;
@@ -22,12 +22,12 @@ class JvmDumper {
@Override
public List<Artifact> produceArtifacts(Context ctx) {
- ContainerPath heapDumpFile = ctx.outputContainerPath().resolve("jvm-heap-dump.bin");
+ Path heapDumpFile = ctx.outputDirectoryInNode().resolve("jvm-heap-dump.bin");
List<String> cmd = List.of(
- "jmap", "-dump:live,format=b,file=" + heapDumpFile.pathInContainer(), Integer.toString(ctx.servicePid()));
+ "jmap", "-dump:live,format=b,file=" + heapDumpFile, Integer.toString(ctx.servicePid()));
ctx.executeCommandInNode(cmd, true);
return List.of(
- Artifact.newBuilder().classification(CONFIDENTIAL).file(heapDumpFile).compressOnUpload().build());
+ Artifact.newBuilder().classification(CONFIDENTIAL).fileInNode(heapDumpFile).compressOnUpload().build());
}
}
@@ -37,10 +37,10 @@ class JvmDumper {
@Override
public List<Artifact> produceArtifacts(Context ctx) {
- ContainerPath jmapReport = ctx.outputContainerPath().resolve("jvm-jmap.txt");
- List<String> cmd = List.of("bash", "-c", "jhsdb jmap --heap --pid " + ctx.servicePid() + " > " + jmapReport.pathInContainer());
+ Path jmapReport = ctx.outputDirectoryInNode().resolve("jvm-jmap.txt");
+ List<String> cmd = List.of("bash", "-c", "jhsdb jmap --heap --pid " + ctx.servicePid() + " > " + jmapReport);
ctx.executeCommandInNode(cmd, true);
- return List.of(Artifact.newBuilder().classification(INTERNAL).file(jmapReport).build());
+ return List.of(Artifact.newBuilder().classification(INTERNAL).fileInNode(jmapReport).build());
}
}
@@ -50,10 +50,10 @@ class JvmDumper {
@Override
public List<Artifact> produceArtifacts(Context ctx) {
- ContainerPath jstatReport = ctx.outputContainerPath().resolve("jvm-jstat.txt");
- List<String> cmd = List.of("bash", "-c", "jstat -gcutil " + ctx.servicePid() + " > " + jstatReport.pathInContainer());
+ Path jstatReport = ctx.outputDirectoryInNode().resolve("jvm-jstat.txt");
+ List<String> cmd = List.of("bash", "-c", "jstat -gcutil " + ctx.servicePid() + " > " + jstatReport);
ctx.executeCommandInNode(cmd, true);
- return List.of(Artifact.newBuilder().classification(INTERNAL).file(jstatReport).build());
+ return List.of(Artifact.newBuilder().classification(INTERNAL).fileInNode(jstatReport).build());
}
}
@@ -63,9 +63,9 @@ class JvmDumper {
@Override
public List<Artifact> produceArtifacts(Context ctx) {
- ContainerPath jstackReport = ctx.outputContainerPath().resolve("jvm-jstack.txt");
- ctx.executeCommandInNode(List.of("bash", "-c", "jstack " + ctx.servicePid() + " > " + jstackReport.pathInContainer()), true);
- return List.of(Artifact.newBuilder().classification(INTERNAL).file(jstackReport).build());
+ Path jstackReport = ctx.outputDirectoryInNode().resolve("jvm-jstack.txt");
+ ctx.executeCommandInNode(List.of("bash", "-c", "jstack " + ctx.servicePid() + " > " + jstackReport), true);
+ return List.of(Artifact.newBuilder().classification(INTERNAL).fileInNode(jstackReport).build());
}
}
@@ -80,9 +80,9 @@ class JvmDumper {
@Override
public List<Artifact> produceArtifacts(ArtifactProducer.Context ctx) {
int seconds = (int) (ctx.options().duration().orElse(30.0));
- ContainerPath outputFile = ctx.outputContainerPath().resolve("recording.jfr");
+ Path outputFile = ctx.outputDirectoryInNode().resolve("recording.jfr");
List<String> startCommand = List.of("jcmd", Integer.toString(ctx.servicePid()), "JFR.start", "name=host-admin",
- "path-to-gc-roots=true", "settings=profile", "filename=" + outputFile.pathInContainer(), "duration=" + seconds + "s");
+ "path-to-gc-roots=true", "settings=profile", "filename=" + outputFile, "duration=" + seconds + "s");
ctx.executeCommandInNode(startCommand, true);
sleeper.sleep(Duration.ofSeconds(seconds).plusSeconds(1));
int maxRetries = 10;
@@ -92,7 +92,7 @@ class JvmDumper {
.anyMatch(l -> l.contains("name=host-admin") && l.contains("running"));
if (!stillRunning) {
Artifact a = Artifact.newBuilder()
- .classification(CONFIDENTIAL).file(outputFile).compressOnUpload().build();
+ .classification(CONFIDENTIAL).fileInNode(outputFile).compressOnUpload().build();
return List.of(a);
}
sleeper.sleep(Duration.ofSeconds(1));
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PerfReporter.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PerfReporter.java
index 3dae6544304..07c8b709e04 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PerfReporter.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PerfReporter.java
@@ -1,8 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.servicedump;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
-
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@@ -26,15 +25,15 @@ class PerfReporter implements ArtifactProducer {
if (ctx.options().callGraphRecording()) {
perfRecordCommand.add("-g");
}
- ContainerPath recordFile = ctx.outputContainerPath().resolve("perf-record.bin");
+ Path recordFile = ctx.outputDirectoryInNode().resolve("perf-record.bin");
perfRecordCommand.addAll(
- List.of("--output=" + recordFile.pathInContainer(),
+ List.of("--output=" + recordFile,
"--pid=" + ctx.servicePid(), "sleep", Integer.toString(duration)));
ctx.executeCommandInNode(perfRecordCommand, true);
- ContainerPath reportFile = ctx.outputContainerPath().resolve("perf-report.txt");
- ctx.executeCommandInNode(List.of("bash", "-c", "perf report --input=" + recordFile.pathInContainer() + " > " + reportFile.pathInContainer()), true);
+ Path reportFile = ctx.outputDirectoryInNode().resolve("perf-report.txt");
+ ctx.executeCommandInNode(List.of("bash", "-c", "perf report --input=" + recordFile + " > " + reportFile), true);
return List.of(
- Artifact.newBuilder().classification(CONFIDENTIAL).file(recordFile).compressOnUpload().build(),
- Artifact.newBuilder().classification(INTERNAL).file(reportFile).build());
+ Artifact.newBuilder().classification(CONFIDENTIAL).fileInNode(recordFile).compressOnUpload().build(),
+ Artifact.newBuilder().classification(INTERNAL).fileInNode(reportFile).build());
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PmapReporter.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PmapReporter.java
index 8087ea7eec0..659628d03a0 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PmapReporter.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/PmapReporter.java
@@ -1,8 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.servicedump;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
-
+import java.nio.file.Path;
import java.util.List;
import static com.yahoo.vespa.hosted.node.admin.maintenance.servicedump.Artifact.Classification.INTERNAL;
@@ -16,9 +15,9 @@ class PmapReporter implements ArtifactProducer {
@Override
public List<Artifact> produceArtifacts(Context ctx) {
- ContainerPath pmapReport = ctx.outputContainerPath().resolve("pmap.txt");
- List<String> cmd = List.of("bash", "-c", "pmap -x " + ctx.servicePid() + " > " + pmapReport.pathInContainer());
+ Path pmapReport = ctx.outputDirectoryInNode().resolve("pmap.txt");
+ List<String> cmd = List.of("bash", "-c", "pmap -x " + ctx.servicePid() + " > " + pmapReport);
ctx.executeCommandInNode(cmd, true);
- return List.of(Artifact.newBuilder().classification(INTERNAL).file(pmapReport).build());
+ return List.of(Artifact.newBuilder().classification(INTERNAL).fileInNode(pmapReport).build());
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaLogDumper.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaLogDumper.java
index e6e8df7585e..24224789877 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaLogDumper.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaLogDumper.java
@@ -1,10 +1,10 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.servicedump;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.yolean.concurrent.Sleeper;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.time.Duration;
import java.util.List;
import java.util.logging.Logger;
@@ -33,12 +33,12 @@ class VespaLogDumper implements ArtifactProducer {
ctx.executeCommandInNode(List.of("kill", "-SIGPROF", Integer.toString(ctx.servicePid())), true);
sleeper.sleep(Duration.ofSeconds(3));
}
- ContainerPath vespaLogFile = ctx.containerPathUnderVespaHome("logs/vespa/vespa.log");
- ContainerPath destination = ctx.outputContainerPath().resolve("vespa.log");
+ Path vespaLogFile = ctx.pathOnHostFromPathInNode(ctx.pathInNodeUnderVespaHome("logs/vespa/vespa.log"));
+ Path destination = ctx.pathOnHostFromPathInNode(ctx.outputDirectoryInNode()).resolve("vespa.log");
if (Files.exists(vespaLogFile)) {
uncheck(() -> Files.copy(vespaLogFile, destination));
return List.of(
- Artifact.newBuilder().classification(CONFIDENTIAL).file(destination).compressOnUpload().build());
+ Artifact.newBuilder().classification(CONFIDENTIAL).fileOnHost(destination).compressOnUpload().build());
} else {
log.info("Log file '" + vespaLogFile + "' does not exist");
return List.of();
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java
index 59f5e0f3f40..27b3467163c 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/servicedump/VespaServiceDumperImpl.java
@@ -12,11 +12,11 @@ import com.yahoo.vespa.hosted.node.admin.maintenance.sync.SyncFileInfo;
import com.yahoo.vespa.hosted.node.admin.maintenance.sync.SyncFileInfo.Compression;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.CommandResult;
import com.yahoo.yolean.concurrent.Sleeper;
import java.net.URI;
+import java.nio.file.Path;
import java.time.Clock;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -88,21 +88,21 @@ public class VespaServiceDumperImpl implements VespaServiceDumper {
handleFailure(context, request, startedAt, "No artifacts requested");
return;
}
- ContainerPath directory = context.containerPathUnderVespaHome("tmp/vespa-service-dump");
- UnixPath unixPathDirectory = new UnixPath(directory);
+ UnixPath directoryInNode = new UnixPath(context.pathInNodeUnderVespaHome("tmp/vespa-service-dump"));
+ UnixPath directoryOnHost = new UnixPath(context.pathOnHostFromPathInNode(directoryInNode.toPath()));
try {
context.log(log, Level.INFO,
"Creating service dump for " + configId + " requested at "
+ Instant.ofEpochMilli(request.getCreatedMillisOrNull()));
storeReport(context, ServiceDumpReport.createStartedReport(request, startedAt));
- if (unixPathDirectory.exists()) {
- context.log(log, Level.INFO, "Removing existing directory '" + unixPathDirectory +"'.");
- unixPathDirectory.deleteRecursively();
+ if (directoryOnHost.exists()) {
+ context.log(log, Level.INFO, "Removing existing directory '" + directoryOnHost +"'.");
+ directoryOnHost.deleteRecursively();
}
- context.log(log, Level.INFO, "Creating '" + unixPathDirectory +"'.");
- unixPathDirectory.createDirectory("rwxrwxrwx");
+ context.log(log, Level.INFO, "Creating '" + directoryOnHost +"'.");
+ directoryOnHost.createDirectory("rwxrwxrwx");
URI destination = serviceDumpDestination(nodeSpec, createDumpId(request));
- ProducerContext producerCtx = new ProducerContext(context, directory, request);
+ ProducerContext producerCtx = new ProducerContext(context, directoryInNode.toPath(), request);
List<Artifact> producedArtifacts = new ArrayList<>();
for (ArtifactProducer producer : artifactProducers.resolve(requestedArtifacts)) {
context.log(log, "Producing artifact of type '" + producer.artifactName() + "'");
@@ -113,9 +113,9 @@ public class VespaServiceDumperImpl implements VespaServiceDumper {
} catch (Exception e) {
handleFailure(context, request, startedAt, e);
} finally {
- if (unixPathDirectory.exists()) {
- context.log(log, Level.INFO, "Deleting directory '" + unixPathDirectory +"'.");
- unixPathDirectory.deleteRecursively();
+ if (directoryOnHost.exists()) {
+ context.log(log, Level.INFO, "Deleting directory '" + directoryOnHost +"'.");
+ directoryOnHost.deleteRecursively();
}
}
}
@@ -126,8 +126,10 @@ public class VespaServiceDumperImpl implements VespaServiceDumper {
List<SyncFileInfo> filesToUpload = producedArtifacts.stream()
.map(a -> {
Compression compression = a.compressOnUpload() ? Compression.ZSTD : Compression.NONE;
+ Path fileInNode = a.fileInNode().orElse(null);
+ Path fileOnHost = fileInNode != null ? ctx.pathOnHostFromPathInNode(fileInNode) : a.fileOnHost().orElseThrow();
String classification = a.classification().map(Artifact.Classification::value).orElse(null);
- return SyncFileInfo.forServiceDump(destination, a.file(), expiry, compression, owner, classification);
+ return SyncFileInfo.forServiceDump(destination, fileOnHost, expiry, compression, owner, classification);
})
.collect(Collectors.toList());
ctx.log(log, Level.INFO,
@@ -177,13 +179,13 @@ public class VespaServiceDumperImpl implements VespaServiceDumper {
private class ProducerContext implements ArtifactProducer.Context, ArtifactProducer.Context.Options {
final NodeAgentContext nodeAgentCtx;
- final ContainerPath path;
+ final Path outputDirectoryInNode;
final ServiceDumpReport request;
volatile int pid = -1;
- ProducerContext(NodeAgentContext nodeAgentCtx, ContainerPath path, ServiceDumpReport request) {
+ ProducerContext(NodeAgentContext nodeAgentCtx, Path outputDirectoryInNode, ServiceDumpReport request) {
this.nodeAgentCtx = nodeAgentCtx;
- this.path = path;
+ this.outputDirectoryInNode = outputDirectoryInNode;
this.request = request;
}
@@ -192,8 +194,8 @@ public class VespaServiceDumperImpl implements VespaServiceDumper {
@Override
public int servicePid() {
if (pid == -1) {
- ContainerPath findPidBinary = nodeAgentCtx.containerPathUnderVespaHome("libexec/vespa/find-pid");
- CommandResult findPidResult = executeCommandInNode(List.of(findPidBinary.pathInContainer(), serviceId()), true);
+ Path findPidBinary = nodeAgentCtx.pathInNodeUnderVespaHome("libexec/vespa/find-pid");
+ CommandResult findPidResult = executeCommandInNode(List.of(findPidBinary.toString(), serviceId()), true);
this.pid = Integer.parseInt(findPidResult.getOutput());
}
return pid;
@@ -222,11 +224,16 @@ public class VespaServiceDumperImpl implements VespaServiceDumper {
return result;
}
- @Override public ContainerPath outputContainerPath() { return path; }
+ @Override public Path outputDirectoryInNode() { return outputDirectoryInNode; }
@Override
- public ContainerPath containerPathUnderVespaHome(String relativePath) {
- return nodeAgentCtx.containerPathUnderVespaHome(relativePath);
+ public Path pathInNodeUnderVespaHome(String relativePath) {
+ return nodeAgentCtx.pathInNodeUnderVespaHome(relativePath);
+ }
+
+ @Override
+ public Path pathOnHostFromPathInNode(Path pathInNode) {
+ return nodeAgentCtx.pathOnHostFromPathInNode(pathInNode);
}
@Override public Options options() { return this; }
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/ContainerData.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/ContainerData.java
index 07e3268b9e4..e32641c0276 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/ContainerData.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/ContainerData.java
@@ -1,8 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.nodeagent;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
-
import java.nio.file.Path;
/**
@@ -11,18 +9,27 @@ import java.nio.file.Path;
* @author hakon
*/
public interface ContainerData {
+ /**
+ * Add or overwrite file in container at path.
+ *
+ * @param pathInContainer The path to the file inside the container, absolute or relative root /.
+ * @param data The content of the file.
+ */
+ void addFile(Path pathInContainer, String data);
- /** Add or overwrite file in container at path. */
- void addFile(ContainerPath path, String data);
-
- /** Add directory in container at path. */
- void addDirectory(ContainerPath path);
+ /**
+ * Add directory in container at path.
+ *
+ * @param pathInContainer The path to the directory inside the container, absolute or relative root /.
+ */
+ void addDirectory(Path pathInContainer);
/**
* Symlink to a file in container at path.
- * @param symlink The path to the symlink inside the container
- * @param target The path to the target file for the symbolic link inside the container
+ *
+ * @param symlink The path to the symlink inside the container, absolute or relative root /.
+ * @param target The path to the target file for the symbolic link inside the container, absolute or relative root /.
*/
- void createSymlink(ContainerPath symlink, Path target);
+ void createSymlink(Path symlink, Path target);
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContext.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContext.java
index 8cf8553bc34..c585bd14e94 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContext.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContext.java
@@ -11,8 +11,8 @@ import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.Acl;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
import com.yahoo.vespa.hosted.node.admin.container.ContainerNetworkMode;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
+import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.Optional;
@@ -58,11 +58,42 @@ public interface NodeAgentContext extends TaskContext {
*/
double vcpuOnThisHost();
- ContainerPath containerPath(String pathInNode);
+ /** The file system used by the NodeAgentContext. All paths must have the same provider. */
+ FileSystem fileSystem();
- ContainerPath containerPathUnderVespaHome(String relativePath);
+ /**
+ * This method is the inverse of {@link #pathInNodeFromPathOnHost(Path)}}
+ *
+ * @param pathInNode absolute path in the container
+ * @return the absolute path on host pointing at the same inode
+ */
+ Path pathOnHostFromPathInNode(Path pathInNode);
+
+ default Path pathOnHostFromPathInNode(String pathInNode) {
+ return pathOnHostFromPathInNode(fileSystem().getPath(pathInNode));
+ }
- ContainerPath containerPathFromPathOnHost(Path pathOnHost);
+ /**
+ * This method is the inverse of {@link #pathOnHostFromPathInNode(Path)}
+ *
+ * @param pathOnHost absolute path on host
+ * @return the absolute path in the container pointing at the same inode
+ */
+ Path pathInNodeFromPathOnHost(Path pathOnHost);
+
+ default Path pathInNodeFromPathOnHost(String pathOnHost) {
+ return pathInNodeFromPathOnHost(fileSystem().getPath(pathOnHost));
+ }
+
+ /**
+ * @param relativePath relative path under Vespa home in container
+ * @return the absolute path under Vespa home in the container
+ */
+ Path pathInNodeUnderVespaHome(Path relativePath);
+
+ default Path pathInNodeUnderVespaHome(String relativePath) {
+ return pathInNodeUnderVespaHome(fileSystem().getPath(relativePath));
+ }
Optional<ApplicationId> hostExclusiveTo();
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java
index 9bcf5d58d6e..6925c1e9600 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java
@@ -16,11 +16,11 @@ import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.Acl;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
import com.yahoo.vespa.hosted.node.admin.container.ContainerNetworkMode;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerFileSystem;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
import java.nio.file.Path;
+import java.nio.file.ProviderMismatchException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -40,8 +40,9 @@ public class NodeAgentContextImpl implements NodeAgentContext {
private final AthenzIdentity identity;
private final ContainerNetworkMode containerNetworkMode;
private final ZoneApi zone;
- private final ContainerFileSystem containerFs;
- private final ContainerPath pathToVespaHome;
+ private final FileSystem fileSystem;
+ private final Path pathToNodeRootOnHost;
+ private final Path pathToVespaHome;
private final UserNamespace userNamespace;
private final double cpuSpeedup;
private final Set<NodeAgentTask> disabledNodeAgentTasks;
@@ -49,7 +50,8 @@ public class NodeAgentContextImpl implements NodeAgentContext {
public NodeAgentContextImpl(NodeSpec node, Acl acl, AthenzIdentity identity,
ContainerNetworkMode containerNetworkMode, ZoneApi zone,
- FlagSource flagSource, Path pathToContainerStorage, String pathToVespaHome,
+ FileSystem fileSystem, FlagSource flagSource,
+ Path pathToContainerStorage, Path pathToVespaHome,
UserNamespace userNamespace, double cpuSpeedup,
Optional<ApplicationId> hostExclusiveTo) {
if (cpuSpeedup <= 0)
@@ -61,8 +63,9 @@ public class NodeAgentContextImpl implements NodeAgentContext {
this.identity = Objects.requireNonNull(identity);
this.containerNetworkMode = Objects.requireNonNull(containerNetworkMode);
this.zone = Objects.requireNonNull(zone);
- this.containerFs = ContainerFileSystem.create(pathToContainerStorage.resolve(containerName.asString()), userNamespace);
- this.pathToVespaHome = containerFs.getPath(pathToVespaHome);
+ this.fileSystem = Objects.requireNonNull(fileSystem);
+ this.pathToNodeRootOnHost = requireValidPath(pathToContainerStorage).resolve(containerName.asString());
+ this.pathToVespaHome = requireValidPath(pathToVespaHome);
this.logPrefix = containerName.asString() + ": ";
this.userNamespace = Objects.requireNonNull(userNamespace);
this.cpuSpeedup = cpuSpeedup;
@@ -117,21 +120,41 @@ public class NodeAgentContextImpl implements NodeAgentContext {
}
@Override
- public ContainerPath containerPath(String pathInNode) {
- return containerFs.getPath(pathInNode);
+ public FileSystem fileSystem() {
+ return fileSystem;
}
@Override
- public ContainerPath containerPathUnderVespaHome(String relativePath) {
- if (relativePath.startsWith("/"))
- throw new IllegalArgumentException("Expected a relative path to the Vespa home, got: " + relativePath);
+ public Path pathOnHostFromPathInNode(Path pathInNode) {
+ requireValidPath(pathInNode);
- return pathToVespaHome.resolve(relativePath);
+ if (! pathInNode.isAbsolute())
+ throw new IllegalArgumentException("Expected an absolute path in the container, got: " + pathInNode);
+
+ return pathToNodeRootOnHost.resolve(pathInNode.getRoot().relativize(pathInNode));
}
@Override
- public ContainerPath containerPathFromPathOnHost(Path pathOnHost) {
- return ContainerPath.fromPathOnHost(containerFs, pathOnHost);
+ public Path pathInNodeFromPathOnHost(Path pathOnHost) {
+ requireValidPath(pathOnHost);
+
+ if (! pathOnHost.isAbsolute())
+ throw new IllegalArgumentException("Expected an absolute path on the host, got: " + pathOnHost);
+
+ if (!pathOnHost.startsWith(pathToNodeRootOnHost))
+ throw new IllegalArgumentException("Path " + pathOnHost + " does not exist in the container");
+
+ return pathOnHost.getRoot().resolve(pathToNodeRootOnHost.relativize(pathOnHost));
+ }
+
+ @Override
+ public Path pathInNodeUnderVespaHome(Path relativePath) {
+ requireValidPath(relativePath);
+
+ if (relativePath.isAbsolute())
+ throw new IllegalArgumentException("Expected a relative path to the Vespa home, got: " + relativePath);
+
+ return pathToVespaHome.resolve(relativePath);
}
@Override
@@ -163,11 +186,24 @@ public class NodeAgentContextImpl implements NodeAgentContext {
", identity=" + identity +
", dockerNetworking=" + containerNetworkMode +
", zone=" + zone +
+ ", pathToNodeRootOnHost=" + pathToNodeRootOnHost +
", pathToVespaHome=" + pathToVespaHome +
", hostExclusiveTo='" + hostExclusiveTo + '\'' +
'}';
}
+ private Path requireValidPath(Path path) {
+ Objects.requireNonNull(path);
+
+ Objects.requireNonNull(fileSystem); // to allow this method to be used in constructor.
+ if (!path.getFileSystem().provider().equals(fileSystem.provider())) {
+ throw new ProviderMismatchException("Expected file system provider " + fileSystem.provider() +
+ " but " + path + " had " + path.getFileSystem().provider());
+ }
+
+ return path;
+ }
+
public static NodeAgentContextImpl.Builder builder(NodeSpec node) {
return new Builder(new NodeSpec.Builder(node));
}
@@ -183,17 +219,16 @@ public class NodeAgentContextImpl implements NodeAgentContext {
/** For testing only! */
public static class Builder {
- private static final Path DEFAULT_CONTAINER_STORAGE = Path.of("/data/vespa/storage");
-
private NodeSpec.Builder nodeSpecBuilder;
private Acl acl;
private AthenzIdentity identity;
private ContainerNetworkMode containerNetworkMode;
private ZoneApi zone;
private UserNamespace userNamespace;
- private Path containerStorage;
+ private FileSystem fileSystem = FileSystems.getDefault();
private FlagSource flagSource;
private double cpuSpeedUp = 1;
+ private Path containerStorage;
private Optional<ApplicationId> hostExclusiveTo = Optional.empty();
private Builder(NodeSpec.Builder nodeSpecBuilder) {
@@ -232,7 +267,8 @@ public class NodeAgentContextImpl implements NodeAgentContext {
/** Sets the file system to use for paths. */
public Builder fileSystem(FileSystem fileSystem) {
- return containerStorage(fileSystem.getPath(DEFAULT_CONTAINER_STORAGE.toString()));
+ this.fileSystem = fileSystem;
+ return this;
}
public Builder flagSource(FlagSource flagSource) {
@@ -256,8 +292,6 @@ public class NodeAgentContextImpl implements NodeAgentContext {
}
public NodeAgentContextImpl build() {
- Objects.requireNonNull(containerStorage, "Must set one of containerStorage or fileSystem");
-
return new NodeAgentContextImpl(
nodeSpecBuilder.build(),
Optional.ofNullable(acl).orElse(Acl.EMPTY),
@@ -284,10 +318,11 @@ public class NodeAgentContextImpl implements NodeAgentContext {
return getId().region().value();
}
}),
+ fileSystem,
Optional.ofNullable(flagSource).orElseGet(InMemoryFlagSource::new),
- containerStorage,
- "/opt/vespa",
- Optional.ofNullable(userNamespace).orElseGet(() -> new UserNamespace(100000, 100000, "vespa", "vespa", 1000, 100)),
+ Optional.ofNullable(containerStorage).orElseGet(() -> fileSystem.getPath("/data/vespa/storage")),
+ fileSystem.getPath("/opt/vespa"),
+ Optional.ofNullable(userNamespace).orElseGet(() -> new UserNamespace(10000, 10000, "vespa", "users", 1000, 100)),
cpuSpeedUp, hostExclusiveTo);
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java
index 3ab196a052e..391aa46716a 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java
@@ -27,7 +27,6 @@ import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer;
import com.yahoo.vespa.hosted.node.admin.maintenance.identity.CredentialsMaintainer;
import com.yahoo.vespa.hosted.node.admin.maintenance.servicedump.VespaServiceDumper;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.ConvergenceException;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import java.nio.file.Path;
import java.time.Clock;
@@ -598,17 +597,17 @@ public class NodeAgentImpl implements NodeAgent {
protected ContainerData createContainerData(NodeAgentContext context) {
return new ContainerData() {
@Override
- public void addFile(ContainerPath path, String data) {
+ public void addFile(Path pathInContainer, String data) {
throw new UnsupportedOperationException("addFile not implemented");
}
@Override
- public void addDirectory(ContainerPath path) {
+ public void addDirectory(Path pathInContainer) {
throw new UnsupportedOperationException("addDirectory not implemented");
}
@Override
- public void createSymlink(ContainerPath symlink, Path target) {
+ public void createSymlink(Path symlink, Path target) {
throw new UnsupportedOperationException("createSymlink not implemented");
}
};
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespace.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespace.java
index 1a25b5c3c5e..5b53879ceec 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespace.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespace.java
@@ -1,20 +1,13 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.nodeagent;
-import java.util.Objects;
-
/**
* @author valerijf
*/
public class UserNamespace {
/** Total number of UID/GID that are mapped for each container */
- private static final int ID_RANGE = 65_536; // 2^16
-
- /**
- * IDs outside the ID range are translated to the overflow ID before being written to disk:
- * https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/Documentation/admin-guide/sysctl/fs.rst#overflowgid--overflowuid */
- private static final int OVERFLOW_ID = 65_534;
+ private static final int ID_RANGE = 1 << 16;
private final int uidOffset;
private final int gidOffset;
@@ -26,33 +19,36 @@ public class UserNamespace {
public UserNamespace(int uidOffset, int gidOffset, String vespaUser, String vespaGroup, int vespaUserId, int vespaGroupId) {
this.uidOffset = uidOffset;
this.gidOffset = gidOffset;
- this.vespaUser = Objects.requireNonNull(vespaUser);
- this.vespaGroup = Objects.requireNonNull(vespaGroup);
+ this.vespaUser = vespaUser;
+ this.vespaGroup = vespaGroup;
this.vespaUserId = vespaUserId;
this.vespaGroupId = vespaGroupId;
}
- public int userIdOnHost(int containerUid) { return toHostId(containerUid, uidOffset); }
- public int groupIdOnHost(int containerGid) { return toHostId(containerGid, gidOffset); }
- public int userIdInContainer(int hostUid) { return toContainerId(hostUid, uidOffset); }
- public int groupIdInContainer(int hostGid) { return toContainerId(hostGid, gidOffset); }
+ public int userIdOnHost(int userIdInContainer) {
+ assertValidId("User", userIdInContainer);
+ return uidOffset + userIdInContainer;
+ }
+
+ public int groupIdOnHost(int groupIdInContainer) {
+ assertValidId("Group", groupIdInContainer);
+ return gidOffset + groupIdInContainer;
+ }
+
+ int rootUserId() { return 0; }
+ int rootGroupId() { return 0; }
+ int rootUserIdOnHost() { return userIdOnHost(rootUserId()); }
+ int rootGroupIdOnHost() { return groupIdOnHost(rootGroupId()); }
public String vespaUser() { return vespaUser; }
public String vespaGroup() { return vespaGroup; }
public int vespaUserId() { return vespaUserId; }
public int vespaGroupId() { return vespaGroupId; }
+ public int vespaUserIdOnHost() { return userIdOnHost(vespaUserId()); }
+ public int vespaGroupIdOnHost() { return groupIdOnHost(vespaGroupId()); }
- public int idRange() { return ID_RANGE; }
- public int overflowId() { return OVERFLOW_ID; }
-
- private static int toHostId(int containerId, int idOffset) {
- if (containerId < 0 || containerId > ID_RANGE)
- throw new IllegalArgumentException("Invalid container id: " + containerId);
- return idOffset + containerId;
- }
-
- private static int toContainerId(int hostId, int idOffset) {
- hostId = hostId - idOffset;
- return hostId < 0 || hostId >= ID_RANGE ? OVERFLOW_ID : hostId;
+ private static void assertValidId(String type, int id) {
+ if (id < 0) throw new IllegalArgumentException(type + " ID cannot be negative, was " + id);
+ if (id >= ID_RANGE) throw new IllegalArgumentException(type + " ID must be less than " + ID_RANGE + ", was " + id);
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystem.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystem.java
index 078a60ba7a5..495b72e4554 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystem.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystem.java
@@ -1,20 +1,15 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.task.util.fs;
-import com.yahoo.vespa.hosted.node.admin.nodeagent.UserNamespace;
-
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.WatchService;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.util.Set;
-import static com.yahoo.yolean.Exceptions.uncheck;
-
/**
* @author valerijf
*/
@@ -86,8 +81,7 @@ public class ContainerFileSystem extends FileSystem {
throw new UnsupportedOperationException();
}
- public static ContainerFileSystem create(Path containerStorageRoot, UserNamespace userNamespace) {
- uncheck(() -> Files.createDirectories(containerStorageRoot));
- return new ContainerFileSystemProvider(containerStorageRoot, userNamespace).getFileSystem(null);
+ public static ContainerFileSystem create(Path containerStorageRoot, int uidOffset, int gidOffset) {
+ return new ContainerFileSystemProvider(containerStorageRoot, uidOffset, gidOffset).getFileSystem(null);
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemProvider.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemProvider.java
index a44f90b164b..73cd3f8cfc5 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemProvider.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemProvider.java
@@ -1,8 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.task.util.fs;
-import com.yahoo.vespa.hosted.node.admin.nodeagent.UserNamespace;
-
import java.io.IOException;
import java.net.URI;
import java.nio.channels.SeekableByteChannel;
@@ -12,7 +10,6 @@ import java.nio.file.DirectoryStream;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemAlreadyExistsException;
-import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
@@ -45,10 +42,10 @@ class ContainerFileSystemProvider extends FileSystemProvider {
private final ContainerUserPrincipalLookupService userPrincipalLookupService;
private final Path containerRootOnHost;
- ContainerFileSystemProvider(Path containerRootOnHost, UserNamespace userNamespace) {
+ ContainerFileSystemProvider(Path containerRootOnHost, int uidOffset, int gidOffset) {
this.containerFs = new ContainerFileSystem(this);
this.userPrincipalLookupService = new ContainerUserPrincipalLookupService(
- containerRootOnHost.getFileSystem().getUserPrincipalLookupService(), userNamespace);
+ containerRootOnHost.getFileSystem().getUserPrincipalLookupService(), uidOffset, gidOffset);
this.containerRootOnHost = containerRootOnHost;
}
@@ -83,9 +80,8 @@ class ContainerFileSystemProvider extends FileSystemProvider {
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
Path pathOnHost = pathOnHost(path);
- boolean existedBefore = Files.exists(pathOnHost);
SeekableByteChannel seekableByteChannel = provider(pathOnHost).newByteChannel(pathOnHost, options, attrs);
- if (!existedBefore) fixOwnerToContainerRoot(toContainerPath(path));
+ fixOwnerToContainerRoot(toContainerPath(path));
return seekableByteChannel;
}
@@ -98,9 +94,8 @@ class ContainerFileSystemProvider extends FileSystemProvider {
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
Path pathOnHost = pathOnHost(dir);
- boolean existedBefore = Files.exists(pathOnHost);
provider(pathOnHost).createDirectory(pathOnHost);
- if (!existedBefore) fixOwnerToContainerRoot(toContainerPath(dir));
+ fixOwnerToContainerRoot(toContainerPath(dir));
}
@Override
@@ -126,20 +121,6 @@ class ContainerFileSystemProvider extends FileSystemProvider {
}
@Override
- public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
- Path pathOnHost = pathOnHost(link);
- if (target instanceof ContainerPath)
- target = pathOnHost.getFileSystem().getPath(toContainerPath(target).pathInContainer());
- provider(pathOnHost).createSymbolicLink(pathOnHost, target, attrs);
- }
-
- @Override
- public Path readSymbolicLink(Path link) throws IOException {
- Path pathOnHost = pathOnHost(link);
- return provider(pathOnHost).readSymbolicLink(pathOnHost);
- }
-
- @Override
public boolean isSameFile(Path path, Path path2) throws IOException {
// 'path' FS provider should be 'this'
if (path2 instanceof ContainerPath)
@@ -199,12 +180,12 @@ class ContainerFileSystemProvider extends FileSystemProvider {
return provider(pathOnHost).readAttributes(pathOnHost, attributes, options);
Map<String, Object> attrs = new HashMap<>(provider(pathOnHost).readAttributes(pathOnHost, "unix:*", options));
- int uid = userPrincipalLookupService.userIdInContainer((int) attrs.get("uid"));
- int gid = userPrincipalLookupService.groupIdInContainer((int) attrs.get("gid"));
+ int uid = userPrincipalLookupService.hostUidToContainerUid((int) attrs.get("uid"));
+ int gid = userPrincipalLookupService.hostGidToContainerGid((int) attrs.get("gid"));
attrs.put("uid", uid);
attrs.put("gid", gid);
- attrs.put("owner", userPrincipalLookupService.userPrincipal(uid, (UserPrincipal) attrs.get("owner")));
- attrs.put("group", userPrincipalLookupService.groupPrincipal(gid, (GroupPrincipal) attrs.get("group")));
+ attrs.put("owner", new ContainerUserPrincipal(uid, (UserPrincipal) attrs.get("owner")));
+ attrs.put("group", new ContainerGroupPrincipal(gid, (GroupPrincipal) attrs.get("group")));
return attrs;
}
@@ -220,8 +201,8 @@ class ContainerFileSystemProvider extends FileSystemProvider {
switch (attribute.substring(index + 1)) {
case "owner": return cast(value, ContainerUserPrincipal.class).baseFsPrincipal();
case "group": return cast(value, ContainerGroupPrincipal.class).baseFsPrincipal();
- case "uid": return userPrincipalLookupService.userIdOnHost(cast(value, Integer.class));
- case "gid": return userPrincipalLookupService.groupIdOnHost(cast(value, Integer.class));
+ case "uid": return userPrincipalLookupService.containerUidToHostUid(cast(value, Integer.class));
+ case "gid": return userPrincipalLookupService.containerGidToHostGid(cast(value, Integer.class));
}
} // else basic file attribute
return value;
@@ -269,7 +250,7 @@ class ContainerFileSystemProvider extends FileSystemProvider {
private static <T> T cast(Object value, Class<T> type) {
if (type.isInstance(value)) return type.cast(value);
- throw new ProviderMismatchException("Expected " + type.getSimpleName() + ", was " + value.getClass().getName());
+ throw new ProviderMismatchException("Expected " + type.getName() + ", was " + value.getClass().getName());
}
private static Path pathOnHost(Path path) {
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupService.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupService.java
index ae65f6a7f7f..893e86ca239 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupService.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupService.java
@@ -1,7 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.task.util.fs;
-import com.yahoo.vespa.hosted.node.admin.nodeagent.UserNamespace;
+import com.google.common.collect.ImmutableBiMap;
import java.io.IOException;
import java.nio.file.attribute.GroupPrincipal;
@@ -9,60 +9,58 @@ import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.attribute.UserPrincipalNotFoundException;
import java.util.Objects;
+import java.util.Optional;
/**
* @author valerijf
*/
class ContainerUserPrincipalLookupService extends UserPrincipalLookupService {
+ /** Total number of UID/GID that are mapped for each container */
+ private static final int ID_RANGE = 1 << 16;
+
+ /**
+ * IDs outside the ID range are translated to the overflow ID before being written to disk:
+ * https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/Documentation/admin-guide/sysctl/fs.rst#overflowgid--overflowuid */
+ static final int OVERFLOW_ID = 65_534;
+
+ private static final ImmutableBiMap<String, Integer> CONTAINER_IDS_BY_NAME = ImmutableBiMap.<String, Integer>builder()
+ .put("root", 0)
+ .put("vespa", 1000)
+ .build();
+
private final UserPrincipalLookupService baseFsUserPrincipalLookupService;
- private final UserNamespace userNamespace;
+ private final int uidOffset;
+ private final int gidOffset;
- ContainerUserPrincipalLookupService(UserPrincipalLookupService baseFsUserPrincipalLookupService, UserNamespace userNamespace) {
- this.baseFsUserPrincipalLookupService = Objects.requireNonNull(baseFsUserPrincipalLookupService);
- this.userNamespace = Objects.requireNonNull(userNamespace);
+ ContainerUserPrincipalLookupService(UserPrincipalLookupService baseFsUserPrincipalLookupService, int uidOffset, int gidOffset) {
+ this.baseFsUserPrincipalLookupService = baseFsUserPrincipalLookupService;
+ this.uidOffset = uidOffset;
+ this.gidOffset = gidOffset;
}
- public int userIdOnHost(int containerUid) { return userNamespace.userIdOnHost(containerUid); }
- public int groupIdOnHost(int containerGid) { return userNamespace.groupIdOnHost(containerGid); }
- public int userIdInContainer(int hostUid) { return userNamespace.userIdInContainer(hostUid); }
- public int groupIdInContainer(int hostGid) { return userNamespace.groupIdInContainer(hostGid); }
+ public int containerUidToHostUid(int containerUid) { return containerIdToHostId(containerUid, uidOffset); }
+ public int containerGidToHostGid(int containerGid) { return containerIdToHostId(containerGid, gidOffset); }
+ public int hostUidToContainerUid(int hostUid) { return hostIdToContainerId(hostUid, uidOffset); }
+ public int hostGidToContainerGid(int hostGid) { return hostIdToContainerId(hostGid, gidOffset); }
@Override
public ContainerUserPrincipal lookupPrincipalByName(String name) throws IOException {
- int containerUid = resolveName(name, userNamespace.vespaUser(), userNamespace.vespaUserId());
- String user = resolveId(containerUid, userNamespace.vespaUser(), userNamespace.vespaUserId());
- String hostUid = String.valueOf(userIdOnHost(containerUid));
- return new ContainerUserPrincipal(containerUid, user, baseFsUserPrincipalLookupService.lookupPrincipalByName(hostUid));
+ int containerUid = resolve(name);
+ String hostUid = String.valueOf(containerUidToHostUid(containerUid));
+ return new ContainerUserPrincipal(containerUid, baseFsUserPrincipalLookupService.lookupPrincipalByName(hostUid));
}
@Override
public ContainerGroupPrincipal lookupPrincipalByGroupName(String group) throws IOException {
- int containerGid = resolveName(group, userNamespace.vespaGroup(), userNamespace.vespaGroupId());
- String name = resolveId(containerGid, userNamespace.vespaGroup(), userNamespace.vespaGroupId());
- String hostGid = String.valueOf(groupIdOnHost(containerGid));
- return new ContainerGroupPrincipal(containerGid, name, baseFsUserPrincipalLookupService.lookupPrincipalByGroupName(hostGid));
- }
-
- public ContainerUserPrincipal userPrincipal(int uid, UserPrincipal baseFsPrincipal) {
- String name = resolveId(uid, userNamespace.vespaUser(), userNamespace.vespaUserId());
- return new ContainerUserPrincipal(uid, name, baseFsPrincipal);
- }
-
- public ContainerGroupPrincipal groupPrincipal(int gid, GroupPrincipal baseFsPrincipal) {
- String name = resolveId(gid, userNamespace.vespaGroup(), userNamespace.vespaGroupId());
- return new ContainerGroupPrincipal(gid, name, baseFsPrincipal);
+ int containerGid = resolve(group);
+ String hostGid = String.valueOf(containerGidToHostGid(containerGid));
+ return new ContainerGroupPrincipal(containerGid, baseFsUserPrincipalLookupService.lookupPrincipalByGroupName(hostGid));
}
- private String resolveId(int id, String vespaName, int vespaId) {
- if (id == 0) return "root";
- if (id == vespaId) return vespaName;
- return String.valueOf(id);
- }
-
- private int resolveName(String name, String vespaName, int vespaId) throws UserPrincipalNotFoundException {
- if (name.equals("root")) return 0;
- if (name.equals(vespaName)) return vespaId;
+ private static int resolve(String name) throws UserPrincipalNotFoundException {
+ Integer id = CONTAINER_IDS_BY_NAME.get(name);
+ if (id != null) return id;
try {
return Integer.parseInt(name);
@@ -76,9 +74,9 @@ class ContainerUserPrincipalLookupService extends UserPrincipalLookupService {
private final String name;
private final UserPrincipal baseFsPrincipal;
- private NamedPrincipal(int id, String name, UserPrincipal baseFsPrincipal) {
+ private NamedPrincipal(int id, UserPrincipal baseFsPrincipal) {
this.id = id;
- this.name = Objects.requireNonNull(name);
+ this.name = Optional.ofNullable(CONTAINER_IDS_BY_NAME.inverse().get(id)).orElseGet(() -> Integer.toString(id));
this.baseFsPrincipal = Objects.requireNonNull(baseFsPrincipal);
}
@@ -115,12 +113,23 @@ class ContainerUserPrincipalLookupService extends UserPrincipalLookupService {
}
static final class ContainerUserPrincipal extends NamedPrincipal {
- private ContainerUserPrincipal(int id, String name, UserPrincipal baseFsPrincipal) { super(id, name, baseFsPrincipal); }
+ ContainerUserPrincipal(int id, UserPrincipal baseFsPrincipal) { super(id, baseFsPrincipal); }
}
static final class ContainerGroupPrincipal extends NamedPrincipal implements GroupPrincipal {
- private ContainerGroupPrincipal(int id, String name, GroupPrincipal baseFsPrincipal) { super(id, name, baseFsPrincipal); }
+ ContainerGroupPrincipal(int id, GroupPrincipal baseFsPrincipal) { super(id, baseFsPrincipal); }
@Override public GroupPrincipal baseFsPrincipal() { return (GroupPrincipal) super.baseFsPrincipal(); }
}
+
+ private static int containerIdToHostId(int id, int idOffset) {
+ if (id < 0 || id > ID_RANGE)
+ throw new IllegalArgumentException("Invalid container id: " + id);
+ return idOffset + id;
+ }
+
+ private static int hostIdToContainerId(int id, int idOffset) {
+ id = id - idOffset;
+ return id < 0 || id >= ID_RANGE ? OVERFLOW_ID : id;
+ }
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/package-info.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/package-info.java
deleted file mode 100644
index a2a86604fdd..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-@ExportPackage
-package com.yahoo.vespa.hosted.node.admin.task.util.fs;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerFailTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerFailTest.java
index a2dbfe0db4b..8683c2cb417 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerFailTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerFailTest.java
@@ -6,7 +6,6 @@ import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContextImpl;
-import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.Test;
import java.util.List;
@@ -34,7 +33,7 @@ public class ContainerFailTest {
.build();
tester.addChildNodeRepositoryNode(nodeSpec);
- NodeAgentContext context = NodeAgentContextImpl.builder(nodeSpec).fileSystem(TestFileSystem.create()).build();
+ NodeAgentContext context = NodeAgentContextImpl.builder(nodeSpec).build();
tester.inOrder(tester.containerOperations).createContainer(containerMatcher(containerName), any(), any());
tester.inOrder(tester.containerOperations).resumeNode(containerMatcher(containerName));
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainerTest.java
index cd020d81450..b7b247bbf87 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainerTest.java
@@ -11,7 +11,6 @@ import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContextImpl;
import com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder;
import com.yahoo.vespa.hosted.node.admin.task.util.file.DiskSize;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.TestTerminal;
import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.After;
@@ -51,8 +50,9 @@ public class StorageMaintainerTest {
fileSystem.getPath("/data/vespa/storage/container-archive"));
@Test
- public void testDiskUsed() {
+ public void testDiskUsed() throws IOException {
NodeAgentContext context = NodeAgentContextImpl.builder("host-1.domain.tld").fileSystem(fileSystem).build();
+ Files.createDirectories(context.pathOnHostFromPathInNode("/"));
terminal.expectCommand("du -xsk /data/vespa/storage/host-1 2>&1", 0, "321\t/data/vespa/storage/host-1/");
assertEquals(Optional.of(DiskSize.of(328_704)), storageMaintainer.diskUsageFor(context));
@@ -76,7 +76,7 @@ public class StorageMaintainerTest {
Path pathToArchiveDir = fileSystem.getPath("/data/vespa/storage/container-archive");
Files.createDirectories(pathToArchiveDir);
- Path containerStorageRoot = context1.containerPath("/").pathOnHost().getParent();
+ Path containerStorageRoot = context1.pathOnHostFromPathInNode("/").getParent();
Set<String> containerStorageRootContentsBeforeArchive = FileFinder.from(containerStorageRoot)
.maxDepth(1)
.stream()
@@ -115,21 +115,21 @@ public class StorageMaintainerTest {
NodeAgentContext context = NodeAgentContextImpl.builder(containerName + ".domain.tld")
.fileSystem(fileSystem).build();
- ContainerPath containerVespaHome = context.containerPathUnderVespaHome("");
- Files.createDirectories(context.containerPath("/etc/something"));
- Files.createFile(context.containerPath("/etc/something/conf"));
+ Path containerVespaHomeOnHost = context.pathOnHostFromPathInNode(context.pathInNodeUnderVespaHome(""));
+ Files.createDirectories(context.pathOnHostFromPathInNode("/etc/something"));
+ Files.createFile(context.pathOnHostFromPathInNode("/etc/something/conf"));
- Files.createDirectories(containerVespaHome.resolve("logs/vespa"));
- Files.createFile(containerVespaHome.resolve("logs/vespa/vespa.log"));
- Files.createFile(containerVespaHome.resolve("logs/vespa/zookeeper.log"));
+ Files.createDirectories(containerVespaHomeOnHost.resolve("logs/vespa"));
+ Files.createFile(containerVespaHomeOnHost.resolve("logs/vespa/vespa.log"));
+ Files.createFile(containerVespaHomeOnHost.resolve("logs/vespa/zookeeper.log"));
- Files.createDirectories(containerVespaHome.resolve("var/db"));
- Files.createFile(containerVespaHome.resolve("var/db/some-file"));
+ Files.createDirectories(containerVespaHomeOnHost.resolve("var/db"));
+ Files.createFile(containerVespaHomeOnHost.resolve("var/db/some-file"));
- ContainerPath containerRoot = context.containerPath("/");
- Set<String> actualContents = FileFinder.files(containerRoot)
+ Path containerRootOnHost = context.pathOnHostFromPathInNode("/");
+ Set<String> actualContents = FileFinder.files(containerRootOnHost)
.stream()
- .map(fileAttributes -> containerRoot.relativize(fileAttributes.path()).toString())
+ .map(fileAttributes -> containerRootOnHost.relativize(fileAttributes.path()).toString())
.collect(Collectors.toSet());
Set<String> expectedContents = Set.of(
"etc/something/conf",
@@ -145,6 +145,7 @@ public class StorageMaintainerTest {
NodeAgentContext context = NodeAgentContextImpl.builder(
NodeSpec.Builder.testSpec("h123a.domain.tld").realResources(new NodeResources(1, 1, 1, 1)).build())
.fileSystem(fileSystem).build();
+ Files.createDirectories(context.pathOnHostFromPathInNode("/"));
mockDiskUsage(500L);
storageMaintainer.cleanDiskIfFull(context);
@@ -157,6 +158,7 @@ public class StorageMaintainerTest {
NodeSpec.Builder.testSpec("h123a.domain.tld").realResources(new NodeResources(1, 1, 1, 1)).build())
.fileSystem(fileSystem).build();
+ Files.createDirectories(context.pathOnHostFromPathInNode("/"));
mockDiskUsage(950_000L);
storageMaintainer.cleanDiskIfFull(context);
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java
index ded99cf3778..eacaa038194 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java
@@ -4,11 +4,10 @@ package com.yahoo.vespa.hosted.node.admin.maintenance.coredump;
import com.yahoo.vespa.hosted.node.admin.container.ContainerOperations;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContextImpl;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.CommandResult;
-import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.Test;
+import java.nio.file.Path;
import java.util.List;
import java.util.Map;
@@ -28,10 +27,9 @@ public class CoreCollectorTest {
private final String JDK_PATH = "/path/to/jdk/java";
private final ContainerOperations docker = mock(ContainerOperations.class);
private final CoreCollector coreCollector = new CoreCollector(docker);
- private final NodeAgentContext context = NodeAgentContextImpl.builder("container-123.domain.tld")
- .fileSystem(TestFileSystem.create()).build();
+ private final NodeAgentContext context = NodeAgentContextImpl.builder("container-123.domain.tld").build();
- private final ContainerPath TEST_CORE_PATH = context.containerPath("/tmp/core.1234");
+ private final Path TEST_CORE_PATH = Path.of("/tmp/core.1234");
private final String TEST_BIN_PATH = "/usr/bin/program";
private final List<String> GDB_BACKTRACE = List.of("[New Thread 2703]",
"Core was generated by `/usr/bin/program\'.", "Program terminated with signal 11, Segmentation fault.",
@@ -40,7 +38,7 @@ public class CoreCollectorTest {
@Test
public void extractsBinaryPathTest() {
- final String[] cmd = {"file", TEST_CORE_PATH.pathInContainer()};
+ final String[] cmd = {"file", TEST_CORE_PATH.toString()};
mockExec(cmd,
"/tmp/core.1234: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from " +
@@ -128,7 +126,7 @@ public class CoreCollectorTest {
@Test
public void collectsDataTest() {
- mockExec(new String[]{"file", TEST_CORE_PATH.pathInContainer()},
+ mockExec(new String[]{"file", TEST_CORE_PATH.toString()},
"/tmp/core.1234: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from " +
"'/usr/bin/program'");
mockExec(new String[]{"stat", GDB_PATH_RHEL7_DT9}, "", "stat: No such file or directory");
@@ -148,7 +146,7 @@ public class CoreCollectorTest {
@Test
public void collectsPartialIfBacktraceFailsTest() {
- mockExec(new String[]{"file", TEST_CORE_PATH.pathInContainer()},
+ mockExec(new String[]{"file", TEST_CORE_PATH.toString()},
"/tmp/core.1234: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from " +
"'/usr/bin/program'");
mockExec(new String[]{"stat", GDB_PATH_RHEL7_DT9}, "The stat output");
@@ -161,7 +159,7 @@ public class CoreCollectorTest {
@Test
public void reportsJstackInsteadOfGdbForJdkCores() {
- mockExec(new String[]{"file", TEST_CORE_PATH.pathInContainer()},
+ mockExec(new String[]{"file", TEST_CORE_PATH.toString()},
"dump.core.5954: ELF 64-bit LSB core file x86-64, version 1 (SYSV), too many program header sections (33172)");
mockExec(new String[]{"stat", GDB_PATH_RHEL7_DT9}, "", "stat: No such file or directory");
@@ -182,7 +180,7 @@ public class CoreCollectorTest {
@Test
public void metadata_for_java_heap_dump() {
- assertEquals(JAVA_HEAP_DUMP_METADATA, coreCollector.collect(context, context.containerPath("/dump_java_pid123.hprof")));
+ assertEquals(JAVA_HEAP_DUMP_METADATA, coreCollector.collect(context, Path.of("dump_java_pid123.hprof")));
}
private void mockExec(String[] cmd, String output) {
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
index 9f507f451b9..31883311e33 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
@@ -7,7 +7,6 @@ import com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContextImpl;
import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;
-import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.vespa.hosted.node.admin.task.util.process.TestChildProcess2;
import com.yahoo.vespa.hosted.node.admin.task.util.process.TestTerminal;
import com.yahoo.vespa.test.file.TestFileSystem;
@@ -48,7 +47,7 @@ public class CoredumpHandlerTest {
private final FileSystem fileSystem = TestFileSystem.create();
private final NodeAgentContext context = NodeAgentContextImpl.builder("container-123.domain.tld")
.fileSystem(fileSystem).build();
- private final ContainerPath containerCrashPath = context.containerPath("/var/crash");
+ private final Path crashPathInContainer = fileSystem.getPath("/var/crash");
private final Path doneCoredumpsPath = fileSystem.getPath("/home/docker/dumps");
private final TestTerminal terminal = new TestTerminal();
@@ -59,38 +58,38 @@ public class CoredumpHandlerTest {
@SuppressWarnings("unchecked")
private final Supplier<String> coredumpIdSupplier = mock(Supplier.class);
private final CoredumpHandler coredumpHandler = new CoredumpHandler(terminal, coreCollector, coredumpReporter,
- containerCrashPath.pathInContainer(), doneCoredumpsPath, 100, metrics, clock, coredumpIdSupplier);
+ crashPathInContainer.toString(), doneCoredumpsPath, 100, metrics, clock, coredumpIdSupplier);
@Test
public void coredump_enqueue_test() throws IOException {
- ContainerPath crashPath = context.containerPath("/some/crash/path");
- ContainerPath processingDir = context.containerPath("/some/other/processing");
+ final Path crashPathOnHost = fileSystem.getPath("/home/docker/container-1/some/crash/path");
+ final Path processingDir = fileSystem.getPath("/home/docker/container-1/some/other/processing");
- Files.createDirectories(crashPath);
- createFileAged(crashPath.resolve("bash.core.431"), Duration.ZERO);
+ Files.createDirectories(crashPathOnHost);
+ createFileAged(crashPathOnHost.resolve("bash.core.431"), Duration.ZERO);
- assertFolderContents(crashPath, "bash.core.431");
- Optional<ContainerPath> enqueuedPath = coredumpHandler.enqueueCoredump(crashPath, processingDir);
+ assertFolderContents(crashPathOnHost, "bash.core.431");
+ Optional<Path> enqueuedPath = coredumpHandler.enqueueCoredump(crashPathOnHost, processingDir);
assertEquals(Optional.empty(), enqueuedPath);
// bash.core.431 finished writing... and 2 more have since been written
clock.advance(Duration.ofMinutes(3));
- createFileAged(crashPath.resolve("vespa-proton.core.119"), Duration.ofMinutes(10));
- createFileAged(crashPath.resolve("vespa-slobrok.core.673"), Duration.ofMinutes(5));
+ createFileAged(crashPathOnHost.resolve("vespa-proton.core.119"), Duration.ofMinutes(10));
+ createFileAged(crashPathOnHost.resolve("vespa-slobrok.core.673"), Duration.ofMinutes(5));
when(coredumpIdSupplier.get()).thenReturn("id-123").thenReturn("id-321");
- enqueuedPath = coredumpHandler.enqueueCoredump(crashPath, processingDir);
+ enqueuedPath = coredumpHandler.enqueueCoredump(crashPathOnHost, processingDir);
assertEquals(Optional.of(processingDir.resolve("id-123")), enqueuedPath);
- assertFolderContents(crashPath, "bash.core.431", "vespa-slobrok.core.673");
+ assertFolderContents(crashPathOnHost, "bash.core.431", "vespa-slobrok.core.673");
assertFolderContents(processingDir, "id-123");
assertFolderContents(processingDir.resolve("id-123"), "dump_vespa-proton.core.119");
verify(coredumpIdSupplier, times(1)).get();
// Enqueue another
- enqueuedPath = coredumpHandler.enqueueCoredump(crashPath, processingDir);
+ enqueuedPath = coredumpHandler.enqueueCoredump(crashPathOnHost, processingDir);
assertEquals(Optional.of(processingDir.resolve("id-321")), enqueuedPath);
- assertFolderContents(crashPath, "bash.core.431");
+ assertFolderContents(crashPathOnHost, "bash.core.431");
assertFolderContents(processingDir, "id-123", "id-321");
assertFolderContents(processingDir.resolve("id-321"), "dump_vespa-slobrok.core.673");
verify(coredumpIdSupplier, times(2)).get();
@@ -98,45 +97,46 @@ public class CoredumpHandlerTest {
@Test
public void enqueue_with_hs_err_files() throws IOException {
- ContainerPath crashPath = context.containerPath("/some/crash/path");
- ContainerPath processingDir = context.containerPath("/some/other/processing");
- Files.createDirectories(crashPath);
+ final Path crashPathOnHost = fileSystem.getPath("/home/docker/container-1/some/crash/path");
+ final Path processingDir = fileSystem.getPath("/home/docker/container-1/some/other/processing");
+ Files.createDirectories(crashPathOnHost);
- createFileAged(crashPath.resolve("java.core.69"), Duration.ofSeconds(515));
- createFileAged(crashPath.resolve("hs_err_pid69.log"), Duration.ofSeconds(520));
+ createFileAged(crashPathOnHost.resolve("java.core.69"), Duration.ofSeconds(515));
+ createFileAged(crashPathOnHost.resolve("hs_err_pid69.log"), Duration.ofSeconds(520));
- createFileAged(crashPath.resolve("java.core.2420"), Duration.ofSeconds(540));
- createFileAged(crashPath.resolve("hs_err_pid2420.log"), Duration.ofSeconds(549));
- createFileAged(crashPath.resolve("hs_err_pid2421.log"), Duration.ofSeconds(550));
+ createFileAged(crashPathOnHost.resolve("java.core.2420"), Duration.ofSeconds(540));
+ createFileAged(crashPathOnHost.resolve("hs_err_pid2420.log"), Duration.ofSeconds(549));
+ createFileAged(crashPathOnHost.resolve("hs_err_pid2421.log"), Duration.ofSeconds(550));
when(coredumpIdSupplier.get()).thenReturn("id-123").thenReturn("id-321");
- Optional<ContainerPath> enqueuedPath = coredumpHandler.enqueueCoredump(crashPath, processingDir);
+ Optional<Path> enqueuedPath = coredumpHandler.enqueueCoredump(crashPathOnHost, processingDir);
assertEquals(Optional.of(processingDir.resolve("id-123")), enqueuedPath);
- assertFolderContents(crashPath, "hs_err_pid69.log", "java.core.69");
+ assertFolderContents(crashPathOnHost, "hs_err_pid69.log", "java.core.69");
assertFolderContents(processingDir, "id-123");
assertFolderContents(processingDir.resolve("id-123"), "hs_err_pid2420.log", "hs_err_pid2421.log", "dump_java.core.2420");
}
@Test
public void coredump_to_process_test() throws IOException {
- ContainerPath processingDir = context.containerPath("/some/other/processing");
+ final Path crashPathOnHost = fileSystem.getPath("/home/docker/container-1/some/crash/path");
+ final Path processingDir = fileSystem.getPath("/home/docker/container-1/some/other/processing");
// Initially there are no core dumps
- Optional<ContainerPath> enqueuedPath = coredumpHandler.enqueueCoredump(containerCrashPath, processingDir);
+ Optional<Path> enqueuedPath = coredumpHandler.enqueueCoredump(crashPathOnHost, processingDir);
assertEquals(Optional.empty(), enqueuedPath);
// 3 core dumps occur
- Files.createDirectories(containerCrashPath);
- createFileAged(containerCrashPath.resolve("bash.core.431"), Duration.ZERO);
- createFileAged(containerCrashPath.resolve("vespa-proton.core.119"), Duration.ofMinutes(10));
- createFileAged(containerCrashPath.resolve("vespa-slobrok.core.673"), Duration.ofMinutes(5));
+ Files.createDirectories(crashPathOnHost);
+ createFileAged(crashPathOnHost.resolve("bash.core.431"), Duration.ZERO);
+ createFileAged(crashPathOnHost.resolve("vespa-proton.core.119"), Duration.ofMinutes(10));
+ createFileAged(crashPathOnHost.resolve("vespa-slobrok.core.673"), Duration.ofMinutes(5));
when(coredumpIdSupplier.get()).thenReturn("id-123");
- enqueuedPath = coredumpHandler.getCoredumpToProcess(containerCrashPath, processingDir);
+ enqueuedPath = coredumpHandler.getCoredumpToProcess(crashPathOnHost, processingDir);
assertEquals(Optional.of(processingDir.resolve("id-123")), enqueuedPath);
// Running this again wont enqueue new core dumps as we are still processing the one enqueued previously
- enqueuedPath = coredumpHandler.getCoredumpToProcess(containerCrashPath, processingDir);
+ enqueuedPath = coredumpHandler.getCoredumpToProcess(crashPathOnHost, processingDir);
assertEquals(Optional.of(processingDir.resolve("id-123")), enqueuedPath);
verify(coredumpIdSupplier, times(1)).get();
}
@@ -164,10 +164,11 @@ public class CoredumpHandlerTest {
"}}";
- ContainerPath coredumpDirectory = context.containerPath("/var/crash/id-123");
- Files.createDirectories(coredumpDirectory.pathOnHost());
+ Path coredumpDirectoryInContainer = fileSystem.getPath("/var/crash/id-123");
+ Path coredumpDirectory = context.pathOnHostFromPathInNode(coredumpDirectoryInContainer);
+ Files.createDirectories(coredumpDirectory);
Files.createFile(coredumpDirectory.resolve("dump_core.456"));
- when(coreCollector.collect(eq(context), eq(coredumpDirectory.resolve("dump_core.456"))))
+ when(coreCollector.collect(eq(context), eq(coredumpDirectoryInContainer.resolve("dump_core.456"))))
.thenReturn(metadata);
assertEquals(expectedMetadataStr, coredumpHandler.getMetadata(context, coredumpDirectory, () -> attributes));
@@ -180,12 +181,12 @@ public class CoredumpHandlerTest {
@Test(expected = IllegalStateException.class)
public void cant_get_metadata_if_no_core_file() throws IOException {
- coredumpHandler.getMetadata(context, context.containerPath("/fake/path"), Map::of);
+ coredumpHandler.getMetadata(context, fileSystem.getPath("/fake/path"), Map::of);
}
@Test(expected = IllegalStateException.class)
public void fails_to_get_core_file_if_only_compressed() throws IOException {
- ContainerPath coredumpDirectory = context.containerPath("/path/to/coredump/proccessing/id-123");
+ Path coredumpDirectory = fileSystem.getPath("/path/to/coredump/proccessing/id-123");
Files.createDirectories(coredumpDirectory);
Files.createFile(coredumpDirectory.resolve("dump_bash.core.431.lz4"));
coredumpHandler.findCoredumpFileInProcessingDirectory(coredumpDirectory);
@@ -193,14 +194,14 @@ public class CoredumpHandlerTest {
@Test
public void process_single_coredump_test() throws IOException {
- ContainerPath coredumpDirectory = context.containerPath("/path/to/coredump/proccessing/id-123");
+ Path coredumpDirectory = fileSystem.getPath("/path/to/coredump/proccessing/id-123");
Files.createDirectories(coredumpDirectory);
Files.write(coredumpDirectory.resolve("metadata.json"), "metadata".getBytes());
Files.createFile(coredumpDirectory.resolve("dump_bash.core.431"));
assertFolderContents(coredumpDirectory, "metadata.json", "dump_bash.core.431");
- terminal.interceptCommand("/usr/bin/lz4 -f /data/vespa/storage/container-123/path/to/coredump/proccessing/id-123/dump_bash.core.431 " +
- "/data/vespa/storage/container-123/path/to/coredump/proccessing/id-123/dump_bash.core.431.lz4 2>&1",
+ terminal.interceptCommand("/usr/bin/lz4 -f /path/to/coredump/proccessing/id-123/dump_bash.core.431 " +
+ "/path/to/coredump/proccessing/id-123/dump_bash.core.431.lz4 2>&1",
commandLine -> {
uncheck(() -> Files.createFile(fileSystem.getPath(commandLine.getArguments().get(3))));
return new TestChildProcess2(0, "");
@@ -215,10 +216,10 @@ public class CoredumpHandlerTest {
@Test
public void report_enqueued_and_processed_metrics() throws IOException {
- Path processingPath = containerCrashPath.resolve("processing");
- Files.createFile(containerCrashPath.resolve("dump-1"));
- Files.createFile(containerCrashPath.resolve("dump-2"));
- Files.createFile(containerCrashPath.resolve("hs_err_pid2.log"));
+ Path processingPath = crashPathInContainer.resolve("processing");
+ Files.createFile(crashPathInContainer.resolve("dump-1"));
+ Files.createFile(crashPathInContainer.resolve("dump-2"));
+ Files.createFile(crashPathInContainer.resolve("hs_err_pid2.log"));
Files.createDirectory(processingPath);
Files.createFile(processingPath.resolve("metadata.json"));
Files.createFile(processingPath.resolve("dump-3"));
@@ -227,7 +228,7 @@ public class CoredumpHandlerTest {
.createParents()
.createNewFile();
- coredumpHandler.updateMetrics(context, containerCrashPath);
+ coredumpHandler.updateMetrics(context, crashPathInContainer);
List<DimensionMetrics> updatedMetrics = metrics.getMetricsByType(Metrics.DimensionType.PRETAGGED);
assertEquals(1, updatedMetrics.size());
Map<String, Number> values = updatedMetrics.get(0).getMetrics();
@@ -237,7 +238,7 @@ public class CoredumpHandlerTest {
@Before
public void setup() throws IOException {
- Files.createDirectories(containerCrashPath.pathOnHost());
+ Files.createDirectories(crashPathInContainer);
}
@After
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
index f8dc05b5eda..232b95f5ede 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
@@ -5,7 +5,6 @@ import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContextImpl;
-import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.Test;
import org.mockito.InOrder;
@@ -155,7 +154,7 @@ public class NodeAdminImplTest {
}
private NodeAgentContext createNodeAgentContext(String hostname) {
- return NodeAgentContextImpl.builder(hostname).fileSystem(TestFileSystem.create()).build();
+ return NodeAgentContextImpl.builder(hostname).build();
}
private NodeAgentWithScheduler mockNodeAgentWithSchedulerFactory(NodeAgentContext context) {
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImplTest.java
index 1c439234a34..b91afd0bfef 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImplTest.java
@@ -7,7 +7,6 @@ import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.Test;
import java.nio.file.FileSystem;
-import java.nio.file.Path;
import java.util.List;
import static org.junit.Assert.assertEquals;
@@ -26,54 +25,54 @@ public class NodeAgentContextImplTest {
public void path_on_host_from_path_in_node_test() {
assertEquals(
"/data/vespa/storage/container-1",
- context.containerPath("/").pathOnHost().toString());
+ context.pathOnHostFromPathInNode("/").toString());
assertEquals(
"/data/vespa/storage/container-1/dev/null",
- context.containerPath("/dev/null").pathOnHost().toString());
+ context.pathOnHostFromPathInNode("/dev/null").toString());
}
@Test(expected=IllegalArgumentException.class)
public void path_in_container_must_be_absolute() {
- context.containerPath("some/relative/path");
+ context.pathOnHostFromPathInNode("some/relative/path");
}
@Test
public void path_in_node_from_path_on_host_test() {
assertEquals(
"/dev/null",
- context.containerPathFromPathOnHost(fileSystem.getPath("/data/vespa/storage/container-1/dev/null")).pathInContainer());
+ context.pathInNodeFromPathOnHost(fileSystem.getPath("/data/vespa/storage/container-1/dev/null")).toString());
}
@Test(expected=IllegalArgumentException.class)
public void path_on_host_must_be_absolute() {
- context.containerPathFromPathOnHost(Path.of("some/relative/path"));
+ context.pathInNodeFromPathOnHost("some/relative/path");
}
@Test(expected=IllegalArgumentException.class)
public void path_on_host_must_be_inside_container_storage_of_context() {
- context.containerPathFromPathOnHost(fileSystem.getPath("/data/vespa/storage/container-2/dev/null"));
+ context.pathInNodeFromPathOnHost(fileSystem.getPath("/data/vespa/storage/container-2/dev/null"));
}
@Test(expected=IllegalArgumentException.class)
public void path_on_host_must_be_inside_container_storage() {
- context.containerPathFromPathOnHost(fileSystem.getPath("/home"));
+ context.pathInNodeFromPathOnHost(fileSystem.getPath("/home"));
}
@Test
public void path_under_vespa_host_in_container_test() {
assertEquals(
"/opt/vespa",
- context.containerPathUnderVespaHome("").pathInContainer());
+ context.pathInNodeUnderVespaHome("").toString());
assertEquals(
"/opt/vespa/logs/vespa/vespa.log",
- context.containerPathUnderVespaHome("logs/vespa/vespa.log").pathInContainer());
+ context.pathInNodeUnderVespaHome("logs/vespa/vespa.log").toString());
}
@Test(expected=IllegalArgumentException.class)
public void path_under_vespa_home_must_be_relative() {
- context.containerPathUnderVespaHome("/home");
+ context.pathInNodeUnderVespaHome("/home");
}
@Test
@@ -87,9 +86,9 @@ public class NodeAgentContextImplTest {
assertTrue(context2.isDisabled(NodeAgentTask.CoreDumps));
}
- private NodeAgentContext createContextWithDisabledTasks(String... tasks) {
+ private static NodeAgentContext createContextWithDisabledTasks(String... tasks) {
InMemoryFlagSource flagSource = new InMemoryFlagSource();
flagSource.withListFlag(PermanentFlags.DISABLED_HOST_ADMIN_TASKS.id(), List.of(tasks), String.class);
- return NodeAgentContextImpl.builder("node123").fileSystem(fileSystem).flagSource(flagSource).build();
+ return NodeAgentContextImpl.builder("node123").flagSource(flagSource).build();
}
}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextManagerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextManagerTest.java
index 197b1c81b57..51fedd54381 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextManagerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextManagerTest.java
@@ -1,7 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.nodeagent;
-import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.Test;
import java.time.Clock;
@@ -143,7 +142,7 @@ public class NodeAgentContextManagerTest {
}
private static NodeAgentContext generateContext() {
- return NodeAgentContextImpl.builder("container-123.domain.tld").fileSystem(TestFileSystem.create()).build();
+ return NodeAgentContextImpl.builder("container-123.domain.tld").build();
}
private static class AsyncExecutor<T> {
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java
index 1c6831e6379..8764db502bb 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java
@@ -27,12 +27,10 @@ import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer;
import com.yahoo.vespa.hosted.node.admin.maintenance.identity.CredentialsMaintainer;
import com.yahoo.vespa.hosted.node.admin.maintenance.servicedump.VespaServiceDumper;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.ConvergenceException;
-import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
-import java.nio.file.FileSystem;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
@@ -74,7 +72,6 @@ public class NodeAgentImplTest {
private final CredentialsMaintainer credentialsMaintainer = mock(CredentialsMaintainer.class);
private final InMemoryFlagSource flagSource = new InMemoryFlagSource();
private final ManualClock clock = new ManualClock(Instant.now());
- private final FileSystem fileSystem = TestFileSystem.create();
@Before
public void setUp() {
@@ -236,7 +233,7 @@ public class NodeAgentImplTest {
nodeAgent.doConverge(secondContext);
inOrder.verify(orchestrator, never()).resume(any(String.class));
- NodeAgentContext thirdContext = NodeAgentContextImpl.builder(specBuilder.vcpu(5).build()).fileSystem(fileSystem).cpuSpeedUp(1.25).build();
+ NodeAgentContext thirdContext = NodeAgentContextImpl.builder(specBuilder.vcpu(5).build()).cpuSpeedUp(1.25).build();
nodeAgent.doConverge(thirdContext);
ContainerResources resourcesAfterThird = ContainerResources.from(0, 4, 16);
mockGetContainer(dockerImage, resourcesAfterThird, true);
@@ -780,7 +777,7 @@ public class NodeAgentImplTest {
}
private NodeAgentContext createContext(NodeSpec nodeSpec) {
- return NodeAgentContextImpl.builder(nodeSpec).fileSystem(fileSystem).build();
+ return NodeAgentContextImpl.builder(nodeSpec).build();
}
private NodeSpec.Builder nodeBuilder(NodeState state) {
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespaceTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespaceTest.java
deleted file mode 100644
index 73b59a17c37..00000000000
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/UserNamespaceTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.nodeagent;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-/**
- * @author valerijf
- */
-class UserNamespaceTest {
-
- private final UserNamespace userNamespace = new UserNamespace(1000, 2000, "vespa", "users", 1000, 100);
-
- @Test
- public void translates_between_ids() {
- assertEquals(1001, userNamespace.userIdOnHost(1));
- assertEquals(2001, userNamespace.groupIdOnHost(1));
- assertEquals(1, userNamespace.userIdInContainer(1001));
- assertEquals(1, userNamespace.groupIdInContainer(2001));
-
- assertEquals(userNamespace.overflowId(), userNamespace.userIdInContainer(1));
- assertEquals(userNamespace.overflowId(), userNamespace.userIdInContainer(999999));
-
- assertThrows(IllegalArgumentException.class, () -> userNamespace.userIdOnHost(-1));
- assertThrows(IllegalArgumentException.class, () -> userNamespace.userIdOnHost(70_000));
- }
-} \ No newline at end of file
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemTest.java
index a5fc6a1373f..eb15450b756 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerFileSystemTest.java
@@ -1,20 +1,17 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.task.util.fs;
-import com.yahoo.vespa.hosted.node.admin.nodeagent.UserNamespace;
import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;
import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.jupiter.api.Test;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.StandardOpenOption;
import java.util.Map;
+import static com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerUserPrincipalLookupService.OVERFLOW_ID;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
@@ -24,8 +21,7 @@ class ContainerFileSystemTest {
private final FileSystem fileSystem = TestFileSystem.create();
private final UnixPath containerRootOnHost = new UnixPath(fileSystem.getPath("/data/storage/ctr1"));
- private final UserNamespace userNamespace = new UserNamespace(10_000, 11_000, "vespa", "users", 1000, 100);
- private final ContainerFileSystem containerFs = ContainerFileSystem.create(containerRootOnHost.createDirectories().toPath(), userNamespace);
+ private final ContainerFileSystem containerFs = ContainerFileSystem.create(containerRootOnHost.createDirectories().toPath(), 10_000, 11_000);
@Test
public void creates_files_and_directories_with_container_root_as_owner() throws IOException {
@@ -45,47 +41,17 @@ class ContainerFileSystemTest {
}
@Test
- public void file_write_and_read() throws IOException {
- ContainerPath containerPath = ContainerPath.fromPathInContainer(containerFs, Path.of("/file"));
- UnixPath unixPath = new UnixPath(containerPath);
- unixPath.writeUtf8File("hello");
- assertOwnership(containerPath, 0, 0, 10000, 11000);
-
- unixPath.setOwnerId(500).setGroupId(200);
- assertOwnership(containerPath, 500, 200, 10500, 11200);
- Files.write(containerPath, " world".getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
- assertOwnership(containerPath, 500, 200, 10500, 11200); // Owner should not have been updated as the file already existed
-
- assertEquals("hello world", unixPath.readUtf8File());
- }
-
- @Test
public void copy() throws IOException {
UnixPath hostFile = new UnixPath(fileSystem.getPath("/file")).createNewFile();
ContainerPath destination = ContainerPath.fromPathInContainer(containerFs, Path.of("/dest"));
// If file is copied to JimFS path, the UID/GIDs are not fixed
Files.copy(hostFile.toPath(), destination.pathOnHost());
- assertEquals(String.valueOf(userNamespace.overflowId()), Files.getOwner(destination).getName());
+ assertEquals(String.valueOf(OVERFLOW_ID), Files.getOwner(destination).getName());
Files.delete(destination);
Files.copy(hostFile.toPath(), destination);
assertOwnership(destination, 0, 0, 10000, 11000);
-
- // Set owner + group on both source host file and destination container file
- hostFile.setOwnerId(5).setGroupId(10);
- new UnixPath(destination).setOwnerId(500).setGroupId(200);
- assertOwnership(destination, 500, 200, 10500, 11200);
- // Copy the host file to destination again with COPY_ATTRIBUTES and REPLACE_EXISTING
- Files.copy(hostFile.toPath(), destination, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
- // The destination is recreated, so the owner should be root
- assertOwnership(destination, 0, 0, 10000, 11000);
-
- // Set owner + group and copy within ContainerFS
- new UnixPath(destination).setOwnerId(500).setGroupId(200);
- ContainerPath destination2 = ContainerPath.fromPathInContainer(containerFs, Path.of("/dest2"));
- Files.copy(destination, destination2, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
- assertOwnership(destination2, 0, 0, 10000, 11000);
}
@Test
@@ -95,41 +61,12 @@ class ContainerFileSystemTest {
// If file is moved to JimFS path, the UID/GIDs are not fixed
Files.move(hostFile.toPath(), destination.pathOnHost());
- assertEquals(String.valueOf(userNamespace.overflowId()), Files.getOwner(destination).getName());
+ assertEquals(String.valueOf(OVERFLOW_ID), Files.getOwner(destination).getName());
Files.delete(destination);
hostFile.createNewFile();
Files.move(hostFile.toPath(), destination);
assertOwnership(destination, 0, 0, 10000, 11000);
-
- // Set owner + group on both source host file and destination container file
- hostFile.createNewFile();
- hostFile.setOwnerId(5).setGroupId(10);
- new UnixPath(destination).setOwnerId(500).setGroupId(200);
- assertOwnership(destination, 500, 200, 10500, 11200);
- // Move the host file to destination again with COPY_ATTRIBUTES and REPLACE_EXISTING
- Files.move(hostFile.toPath(), destination, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
- // The destination is recreated, so the owner should be root
- assertOwnership(destination, 0, 0, 10000, 11000);
-
- // Set owner + group and move within ContainerFS
- new UnixPath(destination).setOwnerId(500).setGroupId(200);
- ContainerPath destination2 = ContainerPath.fromPathInContainer(containerFs, Path.of("/dest2"));
- Files.move(destination, destination2, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
- assertOwnership(destination2, 0, 0, 10000, 11000);
- }
-
- @Test
- public void symlink() throws IOException {
- ContainerPath source = ContainerPath.fromPathInContainer(containerFs, Path.of("/src"));
- // Symlink from ContainerPath to some relative path (different FS provider)
- Files.createSymbolicLink(source, fileSystem.getPath("../relative/target"));
- assertEquals(fileSystem.getPath("../relative/target"), Files.readSymbolicLink(source));
- Files.delete(source);
-
- // Symlinks from ContainerPath to a ContainerPath: Target is resolved within container with base FS provider
- Files.createSymbolicLink(source, ContainerPath.fromPathInContainer(containerFs, Path.of("/path/in/container")));
- assertEquals(fileSystem.getPath("/path/in/container"), Files.readSymbolicLink(source));
}
private static void assertOwnership(ContainerPath path, int contUid, int contGid, int hostUid, int hostGid) throws IOException {
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerPathTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerPathTest.java
index 6bca8c2f0b1..ebbbaf3b525 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerPathTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerPathTest.java
@@ -1,7 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.task.util.fs;
-import com.yahoo.vespa.hosted.node.admin.nodeagent.UserNamespace;
import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -13,7 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.nio.file.FileSystem;
@@ -27,7 +25,7 @@ import java.nio.file.Path;
class ContainerPathTest {
private final FileSystem baseFs = TestFileSystem.create();
- private final ContainerFileSystem containerFs = ContainerFileSystem.create(baseFs.getPath("/data/storage/ctr1"), mock(UserNamespace.class));
+ private final ContainerFileSystem containerFs = ContainerFileSystem.create(baseFs.getPath("/data/storage/ctr1"), 0, 0);
@Test
public void create_new_container_path() {
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupServiceTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupServiceTest.java
index bc26cfa73f3..a459c24049e 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupServiceTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/fs/ContainerUserPrincipalLookupServiceTest.java
@@ -1,7 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.task.util.fs;
-import com.yahoo.vespa.hosted.node.admin.nodeagent.UserNamespace;
import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.jupiter.api.Test;
@@ -18,22 +17,35 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
*/
class ContainerUserPrincipalLookupServiceTest {
- private final UserNamespace userNamespace = new UserNamespace(10_000, 11_000, "vespa", "users", 1000, 100);
private final ContainerUserPrincipalLookupService userPrincipalLookupService =
- new ContainerUserPrincipalLookupService(TestFileSystem.create().getUserPrincipalLookupService(), userNamespace);
+ new ContainerUserPrincipalLookupService(TestFileSystem.create().getUserPrincipalLookupService(), 1000, 2000);
@Test
public void correctly_resolves_ids() throws IOException {
ContainerUserPrincipal user = userPrincipalLookupService.lookupPrincipalByName("1000");
assertEquals("vespa", user.getName());
- assertEquals("11000", user.baseFsPrincipal().getName());
+ assertEquals("2000", user.baseFsPrincipal().getName());
assertEquals(user, userPrincipalLookupService.lookupPrincipalByName("vespa"));
- ContainerGroupPrincipal group = userPrincipalLookupService.lookupPrincipalByGroupName("100");
- assertEquals("users", group.getName());
- assertEquals("11100", group.baseFsPrincipal().getName());
- assertEquals(group, userPrincipalLookupService.lookupPrincipalByGroupName("users"));
+ ContainerGroupPrincipal group = userPrincipalLookupService.lookupPrincipalByGroupName("1000");
+ assertEquals("vespa", group.getName());
+ assertEquals("3000", group.baseFsPrincipal().getName());
+ assertEquals(group, userPrincipalLookupService.lookupPrincipalByGroupName("vespa"));
assertThrows(UserPrincipalNotFoundException.class, () -> userPrincipalLookupService.lookupPrincipalByName("test"));
}
-}
+
+ @Test
+ public void translates_between_ids() {
+ assertEquals(1001, userPrincipalLookupService.containerUidToHostUid(1));
+ assertEquals(2001, userPrincipalLookupService.containerGidToHostGid(1));
+ assertEquals(1, userPrincipalLookupService.hostUidToContainerUid(1001));
+ assertEquals(1, userPrincipalLookupService.hostGidToContainerGid(2001));
+
+ assertEquals(65_534, userPrincipalLookupService.hostUidToContainerUid(1));
+ assertEquals(65_534, userPrincipalLookupService.hostUidToContainerUid(999999));
+
+ assertThrows(IllegalArgumentException.class, () -> userPrincipalLookupService.containerUidToHostUid(-1));
+ assertThrows(IllegalArgumentException.class, () -> userPrincipalLookupService.containerUidToHostUid(70_000));
+ }
+} \ No newline at end of file