aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--docker-api/.gitignore1
-rw-r--r--docker-api/CMakeLists.txt2
-rw-r--r--docker-api/OWNERS1
-rw-r--r--docker-api/pom.xml150
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerEngine.java99
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java263
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java479
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImageGarbageCollector.java200
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/ContainerNotFoundException.java13
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/DockerException.java16
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/DockerExecTimeoutException.java17
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/package-info.java5
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/package-info.java5
-rw-r--r--docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImplTest.java64
-rw-r--r--docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerEngineTest.java147
-rw-r--r--docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImageGarbageCollectionTest.java284
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java2
-rw-r--r--node-admin/pom.xml6
-rw-r--r--node-admin/src/main/application/services.xml3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/Container.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Container.java)3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerId.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerId.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerName.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerName.java)3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerOperations.java7
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResources.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerResources.java)3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerStats.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java)3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResult.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ProcessResult.java)5
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentials.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/RegistryCredentials.java)3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentialsProvider.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Counter.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Counter.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/DimensionMetrics.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/DimensionMetrics.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Dimensions.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Dimensions.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Gauge.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Gauge.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricValue.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/MetricValue.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Metrics.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Metrics.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/package-info.java (renamed from docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/package-info.java)2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java8
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContext.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java26
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerNameTest.java (renamed from docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerNameTest.java)2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResourcesTest.java (renamed from docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerResourcesTest.java)2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResultTest.java (renamed from docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ProcessResultTest.java)2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricsTest.java (renamed from docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/metrics/MetricsTest.java)6
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerFailTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerOperationsMock.java14
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerTester.java6
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/MultiContainerTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RebootTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RestartTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java17
-rw-r--r--pom.xml1
59 files changed, 68 insertions, 1851 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 09d060f5d74..1b1934a8a08 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -76,7 +76,6 @@ add_subdirectory(clustercontroller-core)
add_subdirectory(clustercontroller-reindexer)
add_subdirectory(clustercontroller-utils)
add_subdirectory(defaults)
-add_subdirectory(docker-api)
add_subdirectory(docproc)
add_subdirectory(docprocs)
add_subdirectory(document)
diff --git a/docker-api/.gitignore b/docker-api/.gitignore
deleted file mode 100644
index 1d1fe94df49..00000000000
--- a/docker-api/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-Dockerfile \ No newline at end of file
diff --git a/docker-api/CMakeLists.txt b/docker-api/CMakeLists.txt
deleted file mode 100644
index edcdcfb5bfc..00000000000
--- a/docker-api/CMakeLists.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-install(FILES target/docker-api-jar-with-dependencies.jar DESTINATION conf/node-admin-app/components)
diff --git a/docker-api/OWNERS b/docker-api/OWNERS
deleted file mode 100644
index e030acdbc5b..00000000000
--- a/docker-api/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-freva
diff --git a/docker-api/pom.xml b/docker-api/pom.xml
deleted file mode 100644
index 749eca97c53..00000000000
--- a/docker-api/pom.xml
+++ /dev/null
@@ -1,150 +0,0 @@
-<?xml version="1.0"?>
-<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
- http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>parent</artifactId>
- <version>7-SNAPSHOT</version>
- <relativePath>../parent/pom.xml</relativePath>
- </parent>
-
- <artifactId>docker-api</artifactId>
- <version>7-SNAPSHOT</version>
- <packaging>container-plugin</packaging>
- <name>${project.artifactId}</name>
-
- <dependencies>
- <!-- Provided -->
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>container-dev</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config-provisioning</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <!-- Compile -->
- <dependency>
- <groupId>com.github.docker-java</groupId>
- <artifactId>docker-java</artifactId>
- <version>3.1.2</version>
- <scope>compile</scope>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-json-provider</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.glassfish.jersey.core</groupId>
- <artifactId>jersey-client</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.glassfish.jersey.core</groupId>
- <artifactId>jersey-common</artifactId>
- </exclusion>
- <exclusion>
- <groupId>javax.ws.rs</groupId>
- <artifactId>javax.ws.rs-api</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>annotations</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpcore</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk15on</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk15on</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpcore</artifactId>
- <!-- We explicitly specify the version of httpcore to be used by
- docker-java so the dependency is declared closer to the root of maven and
- more likely be the version that is finally being used. -->
- <version>4.4.1</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <!-- We explicitly specify the version of httpclient to be used by
- docker-java so the dependency is declared closer to the root of maven and
- more likely be the version that is finally being used. -->
- <version>4.5</version>
- <scope>compile</scope>
- </dependency>
-
- <!-- Test -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>testutil</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>bundle-plugin</artifactId>
- <extensions>true</extensions>
- <configuration>
- <attachBundleArtifact>true</attachBundleArtifact>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerEngine.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerEngine.java
deleted file mode 100644
index 7a8d98f0e85..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerEngine.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
-
-import com.yahoo.config.provision.DockerImage;
-
-import java.net.InetAddress;
-import java.nio.file.Path;
-import java.time.Duration;
-import java.util.List;
-import java.util.Optional;
-import java.util.OptionalLong;
-
-/**
- * API that hides the access to a specific container engine like Docker or Podman.
- */
-public interface ContainerEngine {
-
- interface CreateContainerCommand {
- CreateContainerCommand withHostName(String hostname);
- CreateContainerCommand withResources(ContainerResources containerResources);
- CreateContainerCommand withLabel(String name, String value);
- CreateContainerCommand withEnvironment(String name, String value);
-
- /**
- * Mounts a directory on host inside the container.
- *
- * <p>Bind mount content will be <b>private</b> to this container (and host) only.
- *
- * <p>When using this method and selinux is enabled (/usr/sbin/sestatus), starting
- * multiple containers which mount host's /foo directory into the container, will make
- * /foo's content visible/readable/writable only inside the container which was last
- * started and on the host. All the other containers will get "Permission denied".
- *
- * <p>Use {@link #withSharedVolume(Path, Path)} to mount a given host directory
- * into multiple containers.
- */
- CreateContainerCommand withVolume(Path path, Path volumePath);
-
- /**
- * Mounts a directory on host inside the container.
- *
- * <p>The bind mount content will be <b>shared</b> among multiple containers.
- *
- * @see #withVolume(Path, Path)
- */
- CreateContainerCommand withSharedVolume(Path path, Path volumePath);
- CreateContainerCommand withNetworkMode(String mode);
- CreateContainerCommand withIpAddress(InetAddress address);
- CreateContainerCommand withUlimit(String name, int softLimit, int hardLimit);
- CreateContainerCommand withEntrypoint(String... entrypoint);
- CreateContainerCommand withManagedBy(String manager);
- CreateContainerCommand withAddCapability(String capabilityName);
- CreateContainerCommand withDropCapability(String capabilityName);
- CreateContainerCommand withSecurityOpt(String securityOpt);
- CreateContainerCommand withDnsOption(String dnsOption);
- CreateContainerCommand withPrivileged(boolean privileged);
-
- void create();
- }
-
- CreateContainerCommand createContainerCommand(DockerImage dockerImage, ContainerName containerName);
-
- Optional<ContainerStats> getContainerStats(ContainerName containerName);
-
- void startContainer(ContainerName containerName);
-
- void stopContainer(ContainerName containerName);
-
- void deleteContainer(ContainerName containerName);
-
- void updateContainer(ContainerName containerName, ContainerResources containerResources);
-
- Optional<Container> getContainer(ContainerName containerName);
-
- /**
- * Checks if the image is currently being pulled or is already pulled, if not, starts an async
- * pull of the image
- *
- * @param image Docker image to pull
- * @return true iff image being pulled, false otherwise
- */
- boolean pullImageAsyncIfNeeded(DockerImage image, RegistryCredentials registryCredentials);
-
- boolean noManagedContainersRunning(String manager);
-
- List<ContainerName> listManagedContainers(String manager);
-
- boolean deleteUnusedDockerImages(List<DockerImage> excludes, Duration minImageAgeToDelete);
-
- /**
- * @param containerName The name of the container
- * @param user can be "username", "username:group", "uid" or "uid:gid"
- * @param timeoutSeconds Timeout for the process to finish in seconds or without timeout if empty
- * @param command The command with arguments to run
- *
- * @return exitcodes, stdout and stderr in the ProcessResult
- */
- ProcessResult executeInContainerAsUser(ContainerName containerName, String user, OptionalLong timeoutSeconds, String... command);
-}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java
deleted file mode 100644
index 7f8a2b68741..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImpl.java
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
-
-import com.github.dockerjava.api.DockerClient;
-import com.github.dockerjava.api.command.CreateContainerCmd;
-import com.github.dockerjava.api.model.Bind;
-import com.github.dockerjava.api.model.Capability;
-import com.github.dockerjava.api.model.HostConfig;
-import com.github.dockerjava.api.model.Ulimit;
-import com.yahoo.config.provision.DockerImage;
-import com.yahoo.vespa.hosted.dockerapi.exception.DockerException;
-
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static com.yahoo.vespa.hosted.dockerapi.DockerEngine.LABEL_NAME_MANAGEDBY;
-
-class CreateContainerCommandImpl implements ContainerEngine.CreateContainerCommand {
-
- private final DockerClient docker;
- private final DockerImage dockerImage;
- private final ContainerName containerName;
- private final Map<String, String> labels = new HashMap<>();
- private final List<String> environmentAssignments = new ArrayList<>();
- private final List<String> volumeBindSpecs = new ArrayList<>();
- private final List<String> dnsOptions = new ArrayList<>();
- private final List<Ulimit> ulimits = new ArrayList<>();
- private final Set<Capability> addCapabilities = new HashSet<>();
- private final Set<Capability> dropCapabilities = new HashSet<>();
- private final Set<String> securityOpts = new HashSet<>();
-
- private Optional<String> hostName = Optional.empty();
- private Optional<ContainerResources> containerResources = Optional.empty();
- private Optional<String> networkMode = Optional.empty();
- private Optional<String> ipv4Address = Optional.empty();
- private Optional<String> ipv6Address = Optional.empty();
- private Optional<String[]> entrypoint = Optional.empty();
- private boolean privileged = false;
-
- CreateContainerCommandImpl(DockerClient docker, DockerImage dockerImage, ContainerName containerName) {
- this.docker = docker;
- this.dockerImage = dockerImage;
- this.containerName = containerName;
- }
-
-
- @Override
- public ContainerEngine.CreateContainerCommand withHostName(String hostName) {
- this.hostName = Optional.of(hostName);
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withResources(ContainerResources containerResources) {
- this.containerResources = Optional.of(containerResources);
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withLabel(String name, String value) {
- assert !name.contains("=");
- labels.put(name, value);
- return this;
- }
-
- public ContainerEngine.CreateContainerCommand withManagedBy(String manager) {
- return withLabel(LABEL_NAME_MANAGEDBY, manager);
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withAddCapability(String capabilityName) {
- addCapabilities.add(Capability.valueOf(capabilityName));
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withDropCapability(String capabilityName) {
- dropCapabilities.add(Capability.valueOf(capabilityName));
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withSecurityOpt(String securityOpt) {
- securityOpts.add(securityOpt);
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withDnsOption(String dnsOption) {
- dnsOptions.add(dnsOption);
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withPrivileged(boolean privileged) {
- this.privileged = privileged;
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withUlimit(String name, int softLimit, int hardLimit) {
- ulimits.add(new Ulimit(name, softLimit, hardLimit));
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withEntrypoint(String... entrypoint) {
- if (entrypoint.length < 1) throw new IllegalArgumentException("Entrypoint must contain at least 1 element");
- this.entrypoint = Optional.of(entrypoint);
- return this;
- }
-
-
- @Override
- public ContainerEngine.CreateContainerCommand withEnvironment(String name, String value) {
- assert name.indexOf('=') == -1;
- environmentAssignments.add(name + "=" + value);
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withVolume(Path path, Path volumePath) {
- volumeBindSpecs.add(path + ":" + volumePath + ":Z");
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withSharedVolume(Path path, Path volumePath) {
- volumeBindSpecs.add(path + ":" + volumePath + ":z");
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withNetworkMode(String mode) {
- networkMode = Optional.of(mode);
- return this;
- }
-
- @Override
- public ContainerEngine.CreateContainerCommand withIpAddress(InetAddress address) {
- if (address instanceof Inet6Address) {
- ipv6Address = Optional.of(address.getHostAddress());
- } else {
- ipv4Address = Optional.of(address.getHostAddress());
- }
- return this;
- }
-
- @Override
- public void create() {
- try {
- createCreateContainerCmd().exec();
- } catch (RuntimeException e) {
- throw new DockerException("Failed to create container " + toString(), e);
- }
- }
-
- private CreateContainerCmd createCreateContainerCmd() {
- List<Bind> volumeBinds = volumeBindSpecs.stream().map(Bind::parse).collect(Collectors.toList());
-
- final HostConfig hostConfig = new HostConfig()
- .withSecurityOpts(new ArrayList<>(securityOpts))
- .withBinds(volumeBinds)
- .withUlimits(ulimits)
- // Docker version 1.13.1 patch 94 changed default pids.max for the Docker container's cgroup
- // from max to 4096. -1L reinstates "max". File: /sys/fs/cgroup/pids/docker/CONTAINERID/pids.max.
- .withPidsLimit(-1L)
- .withCapAdd(addCapabilities.toArray(new Capability[0]))
- .withCapDrop(dropCapabilities.toArray(new Capability[0]))
- .withDnsOptions(dnsOptions)
- .withPrivileged(privileged);
-
- containerResources.ifPresent(cr -> hostConfig
- .withCpuShares(cr.cpuShares())
- .withMemory(cr.memoryBytes())
- // MemorySwap is the total amount of memory and swap, if MemorySwap == Memory, then container has no access swap
- .withMemorySwap(cr.memoryBytes())
- .withCpuPeriod(cr.cpuQuota() > 0 ? (long) cr.cpuPeriod() : null)
- .withCpuQuota(cr.cpuQuota() > 0 ? (long) cr.cpuQuota() : null));
-
- final CreateContainerCmd containerCmd = docker
- .createContainerCmd(dockerImage.asString())
- .withHostConfig(hostConfig)
- .withName(containerName.asString())
- .withLabels(labels)
- .withEnv(environmentAssignments);
-
- hostName.ifPresent(containerCmd::withHostName);
- networkMode.ifPresent(hostConfig::withNetworkMode);
- ipv4Address.ifPresent(containerCmd::withIpv4Address);
- ipv6Address.ifPresent(containerCmd::withIpv6Address);
- entrypoint.ifPresent(containerCmd::withEntrypoint);
-
- return containerCmd;
- }
-
- /** Maps ("--env", {"A", "B", "C"}) to "--env A --env B --env C" */
- private static String toRepeatedOption(String option, Collection<String> optionValues) {
- return optionValues.stream()
- .map(optionValue -> option + " " + optionValue)
- .collect(Collectors.joining(" "));
- }
-
- private static String toOptionalOption(String option, Optional<?> value) {
- return value.map(o -> option + " " + o).orElse("");
- }
-
- private static String toFlagOption(String option, boolean value) {
- return value ? option : "";
- }
-
- /** Make toString() print the equivalent arguments to 'docker run' */
- @Override
- public String toString() {
- List<String> labelList = labels.entrySet().stream()
- .map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.toList());
- List<String> ulimitList = ulimits.stream()
- .map(ulimit -> ulimit.getName() + "=" + ulimit.getSoft() + ":" + ulimit.getHard())
- .collect(Collectors.toList());
- List<String> addCapabilitiesList = addCapabilities.stream().map(Enum<Capability>::toString).sorted().collect(Collectors.toList());
- List<String> dropCapabilitiesList = dropCapabilities.stream().map(Enum<Capability>::toString).sorted().collect(Collectors.toList());
- Optional<String> entrypointExecuteable = entrypoint.map(args -> args[0]);
- String entrypointArgs = entrypoint.map(Stream::of).orElseGet(Stream::empty)
- .skip(1)
- .collect(Collectors.joining(" "));
-
- return Stream.of(
- "--name " + containerName.asString(),
- toOptionalOption("--hostname", hostName),
- toOptionalOption("--cpu-shares", containerResources.map(ContainerResources::cpuShares)),
- toOptionalOption("--cpus", containerResources.map(ContainerResources::cpus)),
- toOptionalOption("--memory", containerResources.map(ContainerResources::memoryBytes)),
- toRepeatedOption("--label", labelList),
- toRepeatedOption("--ulimit", ulimitList),
- "--pids-limit -1",
- toRepeatedOption("--env", environmentAssignments),
- toRepeatedOption("--volume", volumeBindSpecs),
- toRepeatedOption("--cap-add", addCapabilitiesList),
- toRepeatedOption("--cap-drop", dropCapabilitiesList),
- toRepeatedOption("--security-opt", securityOpts),
- toRepeatedOption("--dns-option", dnsOptions),
- toOptionalOption("--net", networkMode),
- toOptionalOption("--ip", ipv4Address),
- toOptionalOption("--ip6", ipv6Address),
- toOptionalOption("--entrypoint", entrypointExecuteable),
- toFlagOption("--privileged", privileged),
- dockerImage.asString(),
- entrypointArgs)
- .filter(s -> !s.isEmpty())
- .collect(Collectors.joining(" "));
- }
-}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java
deleted file mode 100644
index 3b7b2b8d54c..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java
+++ /dev/null
@@ -1,479 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.github.dockerjava.api.DockerClient;
-import com.github.dockerjava.api.command.ExecCreateCmdResponse;
-import com.github.dockerjava.api.command.InspectContainerResponse;
-import com.github.dockerjava.api.command.InspectExecResponse;
-import com.github.dockerjava.api.command.InspectImageResponse;
-import com.github.dockerjava.api.command.PullImageCmd;
-import com.github.dockerjava.api.command.UpdateContainerCmd;
-import com.github.dockerjava.api.exception.DockerClientException;
-import com.github.dockerjava.api.exception.NotFoundException;
-import com.github.dockerjava.api.exception.NotModifiedException;
-import com.github.dockerjava.api.model.AuthConfig;
-import com.github.dockerjava.api.model.HostConfig;
-import com.github.dockerjava.api.model.Image;
-import com.github.dockerjava.api.model.Statistics;
-import com.github.dockerjava.core.DefaultDockerClientConfig;
-import com.github.dockerjava.core.DockerClientConfig;
-import com.github.dockerjava.core.DockerClientImpl;
-import com.github.dockerjava.core.async.ResultCallbackTemplate;
-import com.github.dockerjava.core.command.ExecStartResultCallback;
-import com.github.dockerjava.core.command.PullImageResultCallback;
-import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory;
-import com.google.inject.Inject;
-import com.yahoo.config.provision.DockerImage;
-import com.yahoo.vespa.hosted.dockerapi.exception.ContainerNotFoundException;
-import com.yahoo.vespa.hosted.dockerapi.exception.DockerException;
-import com.yahoo.vespa.hosted.dockerapi.exception.DockerExecTimeoutException;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Counter;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Dimensions;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Gauge;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.time.Clock;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.OptionalLong;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public class DockerEngine implements ContainerEngine {
-
- private static final Logger logger = Logger.getLogger(DockerEngine.class.getName());
-
- static final String LABEL_NAME_MANAGEDBY = "com.yahoo.vespa.managedby";
- private static final String FRAMEWORK_CONTAINER_PREFIX = "/";
- private static final Duration WAIT_BEFORE_KILLING = Duration.ofSeconds(10);
-
- private final Object monitor = new Object();
- private final Set<DockerImage> scheduledPulls = new HashSet<>();
-
- private final DockerClient dockerClient;
- private final DockerImageGarbageCollector dockerImageGC;
- private final Metrics metrics;
- private final Counter numberOfDockerApiFails;
- private final Clock clock;
-
- @Inject
- public DockerEngine(Metrics metrics) {
- this(createDockerClient(), metrics, Clock.systemUTC());
- }
-
- DockerEngine(DockerClient dockerClient, Metrics metrics, Clock clock) {
- this.dockerClient = dockerClient;
- this.dockerImageGC = new DockerImageGarbageCollector(this);
- this.metrics = metrics;
- this.clock = clock;
-
- numberOfDockerApiFails = metrics.declareCounter("docker.api_fails");
- }
-
- @Override
- public boolean pullImageAsyncIfNeeded(DockerImage image, RegistryCredentials registryCredentials) {
- try {
- synchronized (monitor) {
- if (scheduledPulls.contains(image)) return true;
- if (imageIsDownloaded(image)) return false;
-
- scheduledPulls.add(image);
-
- logger.log(Level.INFO, "Starting download of " + image.asString());
- PullImageCmd pullCmd = dockerClient.pullImageCmd(image.asString());
- if (!registryCredentials.equals(RegistryCredentials.none)) {
- logger.log(Level.INFO, "Authenticating with " + registryCredentials.registryAddress());
- AuthConfig authConfig = new AuthConfig().withUsername(registryCredentials.username())
- .withPassword(registryCredentials.password())
- .withRegistryAddress(registryCredentials.registryAddress());
- pullCmd = pullCmd.withAuthConfig(authConfig);
- }
- pullCmd.exec(new ImagePullCallback(image));
- return true;
- }
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to pull image '" + image.asString() + "'", e);
- }
- }
-
- private void removeScheduledPoll(DockerImage image) {
- synchronized (monitor) {
- scheduledPulls.remove(image);
- }
- }
-
- /**
- * Check if a given image is already in the local registry
- */
- boolean imageIsDownloaded(DockerImage dockerImage) {
- return inspectImage(dockerImage).isPresent();
- }
-
- private Optional<InspectImageResponse> inspectImage(DockerImage dockerImage) {
- try {
- return Optional.of(dockerClient.inspectImageCmd(dockerImage.asString()).exec());
- } catch (NotFoundException e) {
- return Optional.empty();
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to inspect image '" + dockerImage.asString() + "'", e);
- }
- }
-
- @Override
- public CreateContainerCommand createContainerCommand(DockerImage image, ContainerName containerName) {
- return new CreateContainerCommandImpl(dockerClient, image, containerName);
- }
-
-
- @Override
- public ProcessResult executeInContainerAsUser(ContainerName containerName, String user, OptionalLong timeoutSeconds, String... command) {
- try {
- ExecCreateCmdResponse response = execCreateCmd(containerName, user, command);
-
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- ByteArrayOutputStream errors = new ByteArrayOutputStream();
- ExecStartResultCallback callback = dockerClient.execStartCmd(response.getId())
- .exec(new ExecStartResultCallback(output, errors));
-
- if (timeoutSeconds.isPresent()) {
- if (!callback.awaitCompletion(timeoutSeconds.getAsLong(), TimeUnit.SECONDS))
- throw new DockerExecTimeoutException(String.format(
- "Command '%s' did not finish within %d seconds.", command[0], timeoutSeconds.getAsLong()));
- } else {
- // Wait for completion no timeout
- callback.awaitCompletion();
- }
-
- InspectExecResponse state = dockerClient.inspectExecCmd(response.getId()).exec();
- if (state.isRunning())
- throw new DockerException("Command '%s' did not finish within %s seconds.");
-
- return new ProcessResult(state.getExitCode(), new String(output.toByteArray()), new String(errors.toByteArray()));
- } catch (RuntimeException | InterruptedException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Container '" + containerName.asString()
- + "' failed to execute " + Arrays.toString(command), e);
- }
- }
-
- private ExecCreateCmdResponse execCreateCmd(ContainerName containerName, String user, String... command) {
- try {
- return dockerClient.execCreateCmd(containerName.asString())
- .withCmd(command)
- .withAttachStdout(true)
- .withAttachStderr(true)
- .withUser(user)
- .exec();
- } catch (NotFoundException e) {
- throw new ContainerNotFoundException(containerName);
- }
- }
-
- private Optional<InspectContainerResponse> inspectContainerCmd(String container) {
- try {
- return Optional.of(dockerClient.inspectContainerCmd(container).exec());
- } catch (NotFoundException ignored) {
- return Optional.empty();
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to get info for container '" + container + "'", e);
- }
- }
-
- @Override
- public Optional<ContainerStats> getContainerStats(ContainerName containerName) {
- try {
- DockerStatsCallback statsCallback = dockerClient.statsCmd(containerName.asString()).exec(new DockerStatsCallback());
- statsCallback.awaitCompletion(5, TimeUnit.SECONDS);
- return statsCallback.stats.map(DockerEngine::containerStatsFrom);
- } catch (NotFoundException ignored) {
- return Optional.empty();
- } catch (RuntimeException | InterruptedException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to get stats for container '" + containerName.asString() + "'", e);
- }
- }
-
- @Override
- public void startContainer(ContainerName containerName) {
- try {
- dockerClient.startContainerCmd(containerName.asString()).exec();
- } catch (NotFoundException e) {
- throw new ContainerNotFoundException(containerName);
- } catch (NotModifiedException ignored) {
- // If is already started, ignore
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to start container '" + containerName.asString() + "'", e);
- }
- }
-
- @Override
- public void stopContainer(ContainerName containerName) {
- try {
- dockerClient.stopContainerCmd(containerName.asString()).withTimeout((int) WAIT_BEFORE_KILLING.getSeconds()).exec();
- } catch (NotFoundException e) {
- throw new ContainerNotFoundException(containerName);
- } catch (NotModifiedException ignored) {
- // If is already stopped, ignore
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to stop container '" + containerName.asString() + "'", e);
- }
- }
-
- @Override
- public void deleteContainer(ContainerName containerName) {
- try {
- dockerClient.removeContainerCmd(containerName.asString()).exec();
- } catch (NotFoundException e) {
- throw new ContainerNotFoundException(containerName);
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to delete container '" + containerName.asString() + "'", e);
- }
- }
-
- @Override
- public void updateContainer(ContainerName containerName, ContainerResources resources) {
- try {
- UpdateContainerCmd updateContainerCmd = dockerClient.updateContainerCmd(containerName.asString())
- .withCpuShares(resources.cpuShares())
- .withMemory(resources.memoryBytes())
- .withMemorySwap(resources.memoryBytes())
-
- // Command line argument `--cpus c` is sent over to docker daemon as "NanoCPUs", which is the
- // value of `c * 1e9`. This however, is just a shorthand for `--cpu-period p` and `--cpu-quota q`
- // where p = 100000 and q = c * 100000.
- // See: https://docs.docker.com/config/containers/resource_constraints/#configure-the-default-cfs-scheduler
- // --cpus requires API 1.25+ on create and 1.29+ on update
- // NanoCPUs is supported in docker-java as of 3.1.0 on create and not at all on update
- // TODO: Simplify this to .withNanoCPUs(resources.cpu()) when docker-java supports it
- .withCpuPeriod(resources.cpuPeriod())
- .withCpuQuota(resources.cpuQuota());
-
- updateContainerCmd.exec();
- } catch (NotFoundException e) {
- throw new ContainerNotFoundException(containerName);
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to update container '" + containerName.asString() + "' to " + resources, e);
- }
- }
-
- @Override
- public Optional<Container> getContainer(ContainerName containerName) {
- return asContainer(containerName.asString()).findFirst();
- }
-
- private Stream<Container> asContainer(String container) {
- return inspectContainerCmd(container)
- .map(response -> new Container(
- new ContainerId(response.getId()),
- response.getConfig().getHostName(),
- DockerImage.fromString(response.getConfig().getImage()),
- containerResourcesFromHostConfig(response.getHostConfig()),
- toContainerName(response.getName()),
- Container.State.valueOf(response.getState().getStatus().toUpperCase()),
- response.getState().getPid()
- ))
- .stream();
- }
-
- private static ContainerResources containerResourcesFromHostConfig(HostConfig hostConfig) {
- // Docker keeps an internal state of what the period and quota are: in cgroups, the quota is always set
- // (default is 100000), but docker will report it as 0 unless explicitly set by the user.
- // This may lead to a state where the quota is set, but period is 0 (accord to docker), which will
- // mess up the calculation below. This can only happen if someone sets it manually, since this class
- // will always set both quota and period.
- final double cpus = hostConfig.getCpuQuota() > 0 ?
- (double) hostConfig.getCpuQuota() / hostConfig.getCpuPeriod() : 0;
- return new ContainerResources(cpus, hostConfig.getCpuShares(), hostConfig.getMemory());
- }
-
- private boolean isManagedBy(com.github.dockerjava.api.model.Container container, String manager) {
- final Map<String, String> labels = container.getLabels();
- return labels != null && manager.equals(labels.get(LABEL_NAME_MANAGEDBY));
- }
-
- private ContainerName toContainerName(String encodedContainerName) {
- return new ContainerName(encodedContainerName.substring(FRAMEWORK_CONTAINER_PREFIX.length()));
- }
-
- @Override
- public boolean noManagedContainersRunning(String manager) {
- return listAllContainers().stream()
- .filter(container -> isManagedBy(container, manager))
- .noneMatch(container -> "running".equalsIgnoreCase(container.getState()));
- }
-
- @Override
- public List<ContainerName> listManagedContainers(String manager) {
- return listAllContainers().stream()
- .filter(container -> isManagedBy(container, manager))
- .map(container -> toContainerName(container.getNames()[0]))
- .collect(Collectors.toList());
- }
-
- List<com.github.dockerjava.api.model.Container> listAllContainers() {
- try {
- return dockerClient.listContainersCmd().withShowAll(true).exec();
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to list all containers", e);
- }
- }
-
- List<Image> listAllImages() {
- try {
- return dockerClient.listImagesCmd().withShowAll(true).exec();
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to list all images", e);
- }
- }
-
- void deleteImage(String imageReference) {
- try {
- dockerClient.removeImageCmd(imageReference).exec();
- } catch (NotFoundException ignored) {
- // Image was already deleted, ignore
- } catch (RuntimeException e) {
- numberOfDockerApiFails.increment();
- throw new DockerException("Failed to delete image by reference '" + imageReference + "'", e);
- }
- }
-
- @Override
- public boolean deleteUnusedDockerImages(List<DockerImage> excludes, Duration minImageAgeToDelete) {
- List<String> excludedRefs = excludes.stream().map(DockerImage::asString).collect(Collectors.toList());
- return dockerImageGC.deleteUnusedDockerImages(excludedRefs, minImageAgeToDelete);
- }
-
- private class ImagePullCallback extends PullImageResultCallback {
-
- private final DockerImage dockerImage;
- private final Instant startedAt;
-
- private ImagePullCallback(DockerImage dockerImage) {
- this.dockerImage = dockerImage;
- this.startedAt = clock.instant();
- }
-
- @Override
- public void onError(Throwable throwable) {
- removeScheduledPoll(dockerImage);
- logger.log(Level.SEVERE, "Could not download image " + dockerImage.asString(), throwable);
- }
-
- @Override
- public void onComplete() {
- if (imageIsDownloaded(dockerImage)) {
- logger.log(Level.INFO, "Download completed: " + dockerImage.asString());
- removeScheduledPoll(dockerImage);
- } else {
- numberOfDockerApiFails.increment();
- throw new DockerClientException("Could not download image: " + dockerImage);
- }
- sampleDuration();
- }
-
- private void sampleDuration() {
- Gauge gauge = metrics.declareGauge("docker.imagePullDurationSecs",
- new Dimensions(Map.of("image", dockerImage.asString())));
- Duration pullDuration = Duration.between(startedAt, clock.instant());
- gauge.sample(pullDuration.getSeconds());
- }
-
- }
-
- // docker-java currently (3.0.8) does not support getting docker stats with stream=false, therefore we need
- // to subscribe to the stream and complete as soon we get the first result.
- private class DockerStatsCallback extends ResultCallbackTemplate<DockerStatsCallback, Statistics> {
- private Optional<Statistics> stats = Optional.empty();
- private final CountDownLatch completed = new CountDownLatch(1);
-
- @Override
- public void onNext(Statistics stats) {
- if (stats != null) {
- this.stats = Optional.of(stats);
- completed.countDown();
- onComplete();
- }
- }
-
- @Override
- public boolean awaitCompletion(long timeout, TimeUnit timeUnit) throws InterruptedException {
- // For some reason it takes as long to execute onComplete as the awaitCompletion timeout is, therefore
- // we have own awaitCompletion that completes as soon as we get the first result.
- return completed.await(timeout, timeUnit);
- }
- }
-
- private static DockerClient createDockerClient() {
- JerseyDockerCmdExecFactory dockerFactory = new JerseyDockerCmdExecFactory()
- .withMaxPerRouteConnections(10)
- .withMaxTotalConnections(100)
- .withConnectTimeout((int) Duration.ofSeconds(100).toMillis())
- .withReadTimeout((int) Duration.ofMinutes(30).toMillis());
-
- DockerClientConfig dockerClientConfig = new DefaultDockerClientConfig.Builder()
- .withDockerHost("unix:///var/run/docker.sock")
- .build();
-
- return DockerClientImpl.getInstance(dockerClientConfig)
- .withDockerCmdExecFactory(dockerFactory);
- }
-
- private static ContainerStats containerStatsFrom(Statistics statistics) {
- return new ContainerStats(Optional.ofNullable(statistics.getNetworks()).orElseGet(Map::of)
- .entrySet().stream()
- .collect(Collectors.toMap(
- Map.Entry::getKey,
- e -> new ContainerStats.NetworkStats(e.getValue().getRxBytes(), e.getValue().getRxDropped(),
- e.getValue().getRxErrors(), e.getValue().getTxBytes(),
- e.getValue().getTxDropped(), e.getValue().getTxErrors()),
- (u, v) -> {
- throw new IllegalStateException();
- },
- TreeMap::new)),
- new ContainerStats.MemoryStats(statistics.getMemoryStats().getStats().getCache(),
- statistics.getMemoryStats().getUsage(),
- statistics.getMemoryStats().getLimit()),
- new ContainerStats.CpuStats(statistics.getCpuStats().getCpuUsage().getPercpuUsage().size(),
- statistics.getCpuStats().getSystemCpuUsage(),
- statistics.getCpuStats().getCpuUsage().getTotalUsage(),
- statistics.getCpuStats().getCpuUsage().getUsageInKernelmode(),
- statistics.getCpuStats().getThrottlingData().getThrottledTime(),
- statistics.getCpuStats().getThrottlingData().getPeriods(),
- statistics.getCpuStats().getThrottlingData().getThrottledPeriods()));
- }
-
- // For testing only, create ContainerStats from JSON returned by docker daemon stats API
- public static ContainerStats statsFromJson(String json) {
- try {
- Statistics statistics = new ObjectMapper().readValue(json, Statistics.class);
- return containerStatsFrom(statistics);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
-}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImageGarbageCollector.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImageGarbageCollector.java
deleted file mode 100644
index 0560d84577a..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImageGarbageCollector.java
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
-
-import com.github.dockerjava.api.model.Container;
-import com.github.dockerjava.api.model.Image;
-import com.google.common.base.Strings;
-import com.yahoo.collections.Pair;
-
-import java.time.Clock;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * This class keeps track of downloaded docker images and helps delete images that have not been recently used
- *
- * <p>Definitions:
- * <ul>
- * <li>Every image has exactly 1 id</li>
- * <li>Every image has between 0..n tags, see
- * <a href="https://docs.docker.com/engine/reference/commandline/tag/">docker tag</a> for more</li>
- * <li>Every image has 0..1 parent ids</li>
- * </ul>
- *
- * <p>Limitations:
- * <ol>
- * <li>Image that has more than 1 tag cannot be deleted by ID</li>
- * <li>Deleting a tag of an image with multiple tags will only remove the tag, the image with the
- * remaining tags will remain</li>
- * <li>Deleting the last tag of an image will delete the entire image.</li>
- * <li>Image cannot be deleted if:</li>
- * <ol>
- * <li>It has 1 or more children</li>
- * <li>A container uses it</li>
- * </ol>
- * </ol>
- *
- * @author freva
- */
-class DockerImageGarbageCollector {
- private static final Logger logger = Logger.getLogger(DockerImageGarbageCollector.class.getName());
-
- private final Map<String, Instant> lastTimeUsedByImageId = new ConcurrentHashMap<>();
- private final DockerEngine docker;
- private final Clock clock;
-
- DockerImageGarbageCollector(DockerEngine docker) {
- this(docker, Clock.systemUTC());
- }
-
- DockerImageGarbageCollector(DockerEngine docker, Clock clock) {
- this.docker = docker;
- this.clock = clock;
- }
-
- /**
- * This method must be called frequently enough to see all containers to know which images are being used
- *
- * @param excludes List of image references (tag or id) that should not be deleted regardless of their used status
- * @param minImageAgeToDelete Minimum duration after which an image can be removed if it has not been used
- * @return true iff at least 1 image was deleted
- */
- boolean deleteUnusedDockerImages(List<String> excludes, Duration minImageAgeToDelete) {
- List<Image> images = docker.listAllImages();
- List<Container> containers = docker.listAllContainers();
-
- Map<String, Image> imageByImageId = images.stream().collect(Collectors.toMap(Image::getId, Function.identity()));
-
- // Find all the ancestors for every local image id, this includes the image id itself
- Map<String, Set<String>> ancestorsByImageId = images.stream()
- .map(Image::getId)
- .collect(Collectors.toMap(
- Function.identity(),
- imageId -> {
- Set<String> ancestors = new HashSet<>();
- while (!Strings.isNullOrEmpty(imageId)) {
- ancestors.add(imageId);
- imageId = Optional.of(imageId).map(imageByImageId::get).map(Image::getParentId).orElse(null);
- }
- return ancestors;
- }
- ));
-
- // The set of images that we want to keep is:
- // 1. The images that were recently used
- // 2. The images that were explicitly excluded
- // 3. All of the ancestors of from images in 1 & 2
- Set<String> imagesToKeep = Stream
- .concat(
- getRecentlyUsedImageIds(images, containers, minImageAgeToDelete).stream(), // 1
- referencesToImages(excludes, images).stream()) // 2
- .flatMap(imageId -> ancestorsByImageId.getOrDefault(imageId, Collections.emptySet()).stream()) // 3
- .collect(Collectors.toSet());
-
- // Now take all the images we have locally
- return imageByImageId.keySet().stream()
-
- // filter out images we want to keep
- .filter(imageId -> !imagesToKeep.contains(imageId))
-
- // Sort images in an order is safe to delete (children before parents)
- .sorted((o1, o2) -> {
- // If image2 is parent of image1, image1 comes before image2
- if (imageIsDescendantOf(imageByImageId, o1, o2)) return -1;
- // If image1 is parent of image2, image2 comes before image1
- else if (imageIsDescendantOf(imageByImageId, o2, o1)) return 1;
- // Otherwise, sort lexicographically by image name (For testing)
- else return o1.compareTo(o2);
- })
-
- // Map back to image
- .map(imageByImageId::get)
-
- // Delete image, if successful also remove last usage time to prevent re-download being instantly deleted
- .peek(image -> {
- // Deleting an image by image ID with multiple tags will fail -> delete by tags instead
- referencesOf(image).forEach(imageReference -> {
- logger.info("Deleting unused image " + imageReference);
- docker.deleteImage(imageReference);
- });
- lastTimeUsedByImageId.remove(image.getId());
- })
- .count() > 0;
- }
-
- private Set<String> getRecentlyUsedImageIds(List<Image> images, List<Container> containers, Duration minImageAgeToDelete) {
- final Instant now = clock.instant();
-
- // Add any already downloaded image to the list once
- images.forEach(image -> lastTimeUsedByImageId.putIfAbsent(image.getId(), now));
-
- // Update last used time for all current containers
- containers.forEach(container -> lastTimeUsedByImageId.put(container.getImageId(), now));
-
- // Return list of images that have been used within minImageAgeToDelete
- return lastTimeUsedByImageId.entrySet().stream()
- .filter(entry -> Duration.between(entry.getValue(), now).minus(minImageAgeToDelete).isNegative())
- .map(Map.Entry::getKey)
- .collect(Collectors.toSet());
- }
-
- /**
- * Map given references (image tags or ids) to images.
- *
- * This only works if the given tag is actually present locally. This is fine, because if it isn't - we can't delete
- * it, so no harm done.
- */
- private Set<String> referencesToImages(List<String> references, List<Image> images) {
- Map<String, String> imageIdByImageTag = images.stream()
- .flatMap(image -> referencesOf(image).stream()
- .map(repoTag -> new Pair<>(repoTag, image.getId())))
- .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond));
-
- return references.stream()
- .map(ref -> imageIdByImageTag.getOrDefault(ref, ref))
- .collect(Collectors.toUnmodifiableSet());
- }
-
- /**
- * @return true if ancestor is a parent or grand-parent or grand-grand-parent, etc. of img
- */
- private boolean imageIsDescendantOf(Map<String, Image> imageIdToImage, String img, String ancestor) {
- while (imageIdToImage.containsKey(img)) {
- img = imageIdToImage.get(img).getParentId();
- if (img == null) return false;
- if (ancestor.equals(img)) return true;
- }
- return false;
- }
-
- /**
- * Returns list of references to given image, preferring image tag(s), if any exist.
- *
- * If image is untagged, its ID is returned instead.
- */
- private static List<String> referencesOf(Image image) {
- if (image.getRepoTags() == null) {
- return List.of(image.getId());
- }
- return Arrays.stream(image.getRepoTags())
- // Docker API returns untagged images as having the tag "<none>:<none>".
- .map(tag -> {
- if ("<none>:<none>".equals(tag)) return image.getId();
- return tag;
- })
- .collect(Collectors.toUnmodifiableList());
- }
-
-}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/ContainerNotFoundException.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/ContainerNotFoundException.java
deleted file mode 100644
index b237228ee8e..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/ContainerNotFoundException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.exception;
-
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
-
-/**
- * @author freva
- */
-public class ContainerNotFoundException extends DockerException {
- public ContainerNotFoundException(ContainerName containerName) {
- super("No such container: " + containerName.asString());
- }
-}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/DockerException.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/DockerException.java
deleted file mode 100644
index df6bb702bf7..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/DockerException.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.exception;
-
-/**
- * This exception wraps any exception thrown by docker-java
- */
-@SuppressWarnings("serial")
-public class DockerException extends RuntimeException {
- public DockerException(String message) {
- super(message);
- }
-
- public DockerException(String message, Exception exception) {
- super(message, exception);
- }
-}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/DockerExecTimeoutException.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/DockerExecTimeoutException.java
deleted file mode 100644
index 39813db5c1e..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/DockerExecTimeoutException.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.exception;
-
-/**
- * Runtime exception to be thrown when the exec commands did not finish in time.
- *
- * The underlying process has not been killed. If you need the process to be
- * killed you need to wrap it into a commands that times out.
- *
- * @author smorgrav
- */
-@SuppressWarnings("serial")
-public class DockerExecTimeoutException extends DockerException {
- public DockerExecTimeoutException(String msg) {
- super(msg);
- }
-}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/package-info.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/package-info.java
deleted file mode 100644
index a5ec5f6c235..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/exception/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-@ExportPackage
-package com.yahoo.vespa.hosted.dockerapi.exception;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/package-info.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/package-info.java
deleted file mode 100644
index 2d34d85876d..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-@ExportPackage
-package com.yahoo.vespa.hosted.dockerapi;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImplTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImplTest.java
deleted file mode 100644
index b4ba6dbb502..00000000000
--- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/CreateContainerCommandImplTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
-
-import com.yahoo.config.provision.DockerImage;
-import org.junit.Test;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.file.Paths;
-
-import static org.junit.Assert.assertEquals;
-
-public class CreateContainerCommandImplTest {
-
- @Test
- public void testToString() throws UnknownHostException {
- DockerImage dockerImage = DockerImage.fromString("docker.registry.domain.tld/my/image:1.2.3");
- ContainerResources containerResources = new ContainerResources(2.5, 100, 1024);
- String hostname = "docker-1.region.domain.tld";
- ContainerName containerName = ContainerName.fromHostname(hostname);
-
- ContainerEngine.CreateContainerCommand createContainerCommand = new CreateContainerCommandImpl(
- null, dockerImage, containerName)
- .withHostName(hostname)
- .withResources(containerResources)
- .withLabel("my-label", "test-label")
- .withUlimit("nofile", 1, 2)
- .withUlimit("nproc", 10, 20)
- .withEnvironment("env1", "val1")
- .withEnvironment("env2", "val2")
- .withVolume(Paths.get("vol1"), Paths.get("/host/vol1"))
- .withAddCapability("SYS_PTRACE")
- .withAddCapability("SYS_ADMIN")
- .withDropCapability("NET_ADMIN")
- .withNetworkMode("bridge")
- .withIpAddress(InetAddress.getByName("10.0.0.1"))
- .withIpAddress(InetAddress.getByName("::1"))
- .withEntrypoint("/path/to/program", "arg1", "arg2")
- .withPrivileged(true);
-
- assertEquals("--name docker-1 " +
- "--hostname docker-1.region.domain.tld " +
- "--cpu-shares 100 " +
- "--cpus 2.5 " +
- "--memory 1024 " +
- "--label my-label=test-label " +
- "--ulimit nofile=1:2 " +
- "--ulimit nproc=10:20 " +
- "--pids-limit -1 " +
- "--env env1=val1 " +
- "--env env2=val2 " +
- "--volume vol1:/host/vol1:Z " +
- "--cap-add SYS_ADMIN " +
- "--cap-add SYS_PTRACE " +
- "--cap-drop NET_ADMIN " +
- "--net bridge " +
- "--ip 10.0.0.1 " +
- "--ip6 0:0:0:0:0:0:0:1 " +
- "--entrypoint /path/to/program " +
- "--privileged docker.registry.domain.tld/my/image:1.2.3 " +
- "arg1 " +
- "arg2", createContainerCommand.toString());
- }
-}
diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerEngineTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerEngineTest.java
deleted file mode 100644
index 71bdb321305..00000000000
--- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerEngineTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
-
-import com.github.dockerjava.api.DockerClient;
-import com.github.dockerjava.api.async.ResultCallback;
-import com.github.dockerjava.api.command.ExecCreateCmd;
-import com.github.dockerjava.api.command.ExecCreateCmdResponse;
-import com.github.dockerjava.api.command.ExecStartCmd;
-import com.github.dockerjava.api.command.InspectExecCmd;
-import com.github.dockerjava.api.command.InspectExecResponse;
-import com.github.dockerjava.api.command.InspectImageCmd;
-import com.github.dockerjava.api.command.InspectImageResponse;
-import com.github.dockerjava.api.command.PullImageCmd;
-import com.github.dockerjava.api.exception.NotFoundException;
-import com.github.dockerjava.core.command.ExecStartResultCallback;
-import com.yahoo.config.provision.DockerImage;
-import com.yahoo.test.ManualClock;
-import com.yahoo.vespa.hosted.dockerapi.metrics.DimensionMetrics;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
-
-import java.time.Duration;
-import java.util.Optional;
-import java.util.OptionalLong;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * @author Tony Vaagenes
- */
-public class DockerEngineTest {
-
- private final DockerClient dockerClient = mock(DockerClient.class);
- private final Metrics metrics = new Metrics();
- private final ManualClock clock = new ManualClock();
- private final DockerEngine docker = new DockerEngine(dockerClient, metrics, clock);
-
- @Test
- public void testExecuteCompletes() {
- final String containerId = "container-id";
- final String[] command = new String[] {"/bin/ls", "-l"};
- final String execId = "exec-id";
- final int exitCode = 3;
-
- final ExecCreateCmdResponse response = mock(ExecCreateCmdResponse.class);
- when(response.getId()).thenReturn(execId);
-
- final ExecCreateCmd execCreateCmd = mock(ExecCreateCmd.class);
- when(dockerClient.execCreateCmd(any(String.class))).thenReturn(execCreateCmd);
- when(execCreateCmd.withCmd(ArgumentMatchers.<String>any())).thenReturn(execCreateCmd);
- when(execCreateCmd.withAttachStdout(any(Boolean.class))).thenReturn(execCreateCmd);
- when(execCreateCmd.withAttachStderr(any(Boolean.class))).thenReturn(execCreateCmd);
- when(execCreateCmd.withUser(any(String.class))).thenReturn(execCreateCmd);
- when(execCreateCmd.exec()).thenReturn(response);
-
- final ExecStartCmd execStartCmd = mock(ExecStartCmd.class);
- when(dockerClient.execStartCmd(any(String.class))).thenReturn(execStartCmd);
- when(execStartCmd.exec(any(ExecStartResultCallback.class))).thenReturn(mock(ExecStartResultCallback.class));
-
- final InspectExecCmd inspectExecCmd = mock(InspectExecCmd.class);
- final InspectExecResponse state = mock(InspectExecResponse.class);
- when(dockerClient.inspectExecCmd(any(String.class))).thenReturn(inspectExecCmd);
- when(inspectExecCmd.exec()).thenReturn(state);
- when(state.isRunning()).thenReturn(false);
- when(state.getExitCode()).thenReturn(exitCode);
-
- final ProcessResult result = docker.executeInContainerAsUser(
- new ContainerName(containerId), "root", OptionalLong.empty(), command);
- assertEquals(exitCode, result.getExitStatus());
- }
-
- @Test
- @SuppressWarnings({"unchecked", "rawtypes"})
- public void pullImageAsyncIfNeededSuccessfully() {
- final DockerImage image = DockerImage.fromString("registry.example.com/test:1.2.3");
-
- InspectImageResponse inspectImageResponse = mock(InspectImageResponse.class);
- when(inspectImageResponse.getId()).thenReturn(image.asString());
-
- InspectImageCmd imageInspectCmd = mock(InspectImageCmd.class);
- when(imageInspectCmd.exec())
- .thenThrow(new NotFoundException("Image not found"))
- .thenReturn(inspectImageResponse);
-
- ArgumentCaptor<ResultCallback> resultCallback = ArgumentCaptor.forClass(ResultCallback.class);
- PullImageCmd pullImageCmd = mock(PullImageCmd.class);
- when(pullImageCmd.exec(resultCallback.capture())).thenReturn(null);
-
- when(dockerClient.inspectImageCmd(image.asString())).thenReturn(imageInspectCmd);
- when(dockerClient.pullImageCmd(eq(image.asString()))).thenReturn(pullImageCmd);
-
- assertTrue("Should return true, we just scheduled the pull", docker.pullImageAsyncIfNeeded(image, RegistryCredentials.none));
- assertTrue("Should return true, the pull i still ongoing", docker.pullImageAsyncIfNeeded(image, RegistryCredentials.none));
-
- assertTrue(docker.imageIsDownloaded(image));
- clock.advance(Duration.ofMinutes(10));
- resultCallback.getValue().onComplete();
- assertPullDuration(Duration.ofMinutes(10), image.asString());
- assertFalse(docker.pullImageAsyncIfNeeded(image, RegistryCredentials.none));
- }
-
- @Test
- @SuppressWarnings({"unchecked", "rawtypes"})
- public void pullImageAsyncIfNeededWithError() {
- final DockerImage image = DockerImage.fromString("registry.example.com/test:1.2.3");
-
- InspectImageCmd imageInspectCmd = mock(InspectImageCmd.class);
- when(imageInspectCmd.exec()).thenThrow(new NotFoundException("Image not found"));
-
- ArgumentCaptor<ResultCallback> resultCallback = ArgumentCaptor.forClass(ResultCallback.class);
- PullImageCmd pullImageCmd = mock(PullImageCmd.class);
- when(pullImageCmd.exec(resultCallback.capture())).thenReturn(null);
-
- when(dockerClient.inspectImageCmd(image.asString())).thenReturn(imageInspectCmd);
- when(dockerClient.pullImageCmd(eq(image.asString()))).thenReturn(pullImageCmd);
-
- assertTrue("Should return true, we just scheduled the pull", docker.pullImageAsyncIfNeeded(image, RegistryCredentials.none));
- assertTrue("Should return true, the pull is still ongoing", docker.pullImageAsyncIfNeeded(image, RegistryCredentials.none));
-
- try {
- resultCallback.getValue().onComplete();
- } catch (Exception ignored) { }
-
- assertFalse(docker.imageIsDownloaded(image));
- assertTrue("Should return true, new pull scheduled", docker.pullImageAsyncIfNeeded(image, RegistryCredentials.none));
- }
-
- private void assertPullDuration(Duration duration, String image) {
- Optional<DimensionMetrics> byImage = metrics.getDefaultMetrics().stream()
- .filter(metrics -> image.equals(metrics.getDimensions().asMap().get("image")))
- .findFirst();
- assertTrue("Found metric for image=" + image, byImage.isPresent());
- Number durationInSecs = byImage.get().getMetrics().get("docker.imagePullDurationSecs");
- assertNotNull(durationInSecs);
- assertEquals(duration, Duration.ofSeconds(durationInSecs.longValue()));
- }
-
-}
diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImageGarbageCollectionTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImageGarbageCollectionTest.java
deleted file mode 100644
index c725b0642c9..00000000000
--- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImageGarbageCollectionTest.java
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.github.dockerjava.api.model.Image;
-import com.yahoo.test.ManualClock;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.time.Duration;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * @author freva
- */
-public class DockerImageGarbageCollectionTest {
-
- private final ImageGcTester gcTester = new ImageGcTester();
-
- @Test
- public void noImagesMeansNoUnusedImages() {
- gcTester.withExistingImages()
- .expectDeletedImages();
- }
-
- @Test
- public void singleImageWithoutContainersIsUnused() {
- gcTester.withExistingImages(new ImageBuilder("image-1"))
- // Even though nothing is using the image, we will keep it for at least 1h
- .expectDeletedImagesAfterMinutes(0)
- .expectDeletedImagesAfterMinutes(30)
- .expectDeletedImagesAfterMinutes(30, "image-1");
- }
-
- @Test
- public void singleImageWithContainerIsUsed() {
- gcTester.withExistingImages(ImageBuilder.forId("image-1"))
- .andExistingContainers(ContainerBuilder.forId("container-1").withImageId("image-1"))
- .expectDeletedImages();
- }
-
- @Test
- public void multipleUnusedImagesAreIdentified() {
- gcTester.withExistingImages(
- ImageBuilder.forId("image-1"),
- ImageBuilder.forId("image-2"))
- .expectDeletedImages("image-1", "image-2");
- }
-
- @Test
- public void multipleUnusedLeavesAreIdentified() {
- gcTester.withExistingImages(
- ImageBuilder.forId("parent-image"),
- ImageBuilder.forId("image-1").withParentId("parent-image"),
- ImageBuilder.forId("image-2").withParentId("parent-image"))
- .expectDeletedImages("image-1", "image-2", "parent-image");
- }
-
- @Test
- public void unusedLeafWithUsedSiblingIsIdentified() {
- gcTester.withExistingImages(
- ImageBuilder.forId("parent-image"),
- ImageBuilder.forId("image-1").withParentId("parent-image").withTags("latest"),
- ImageBuilder.forId("image-2").withParentId("parent-image").withTags("1.24"))
- .andExistingContainers(ContainerBuilder.forId("vespa-node-1").withImageId("image-1"))
- .expectDeletedImages("1.24"); // Deleting the only tag will delete the image
- }
-
- @Test
- public void unusedImagesWithMultipleTags() {
- gcTester.withExistingImages(
- ImageBuilder.forId("parent-image"),
- ImageBuilder.forId("image-1").withParentId("parent-image")
- .withTags("vespa-6", "vespa-6.28", "vespa:latest"))
- .expectDeletedImages("vespa-6", "vespa-6.28", "vespa:latest", "parent-image");
- }
-
-
- @Test
- public void unusedImagesWithMultipleUntagged() {
- gcTester.withExistingImages(ImageBuilder.forId("image1")
- .withTags("<none>:<none>"),
- ImageBuilder.forId("image2")
- .withTags("<none>:<none>"))
- .expectDeletedImages("image1", "image2");
- }
-
- @Test
- public void taggedImageWithNoContainersIsUnused() {
- gcTester.withExistingImages(ImageBuilder.forId("image-1").withTags("vespa-6"))
- .expectDeletedImages("vespa-6");
- }
-
- @Test
- public void unusedImagesWithSimpleImageGc() {
- gcTester.withExistingImages(ImageBuilder.forId("parent-image"))
- .expectDeletedImagesAfterMinutes(30)
- .withExistingImages(
- ImageBuilder.forId("parent-image"),
- ImageBuilder.forId("image-1").withParentId("parent-image"))
- .expectDeletedImagesAfterMinutes(0)
- .expectDeletedImagesAfterMinutes(30)
- // At this point, parent-image has been unused for 1h, but image-1 depends on parent-image and it has
- // only been unused for 30m, so we cannot delete parent-image yet. 30 mins later both can be removed
- .expectDeletedImagesAfterMinutes(30, "image-1", "parent-image");
- }
-
- @Test
- public void reDownloadingImageIsNotImmediatelyDeleted() {
- gcTester.withExistingImages(ImageBuilder.forId("image"))
- .expectDeletedImages("image") // After 1h we delete image
- .expectDeletedImagesAfterMinutes(0) // image is immediately re-downloaded, but is not deleted
- .expectDeletedImagesAfterMinutes(10)
- .expectDeletedImages("image"); // 1h after re-download it is deleted again
- }
-
- @Test
- public void reDownloadingImageIsNotImmediatelyDeletedWhenDeletingByTag() {
- gcTester.withExistingImages(ImageBuilder.forId("image").withTags("image-1", "my-tag"))
- .expectDeletedImages("image-1", "my-tag") // After 1h we delete image
- .expectDeletedImagesAfterMinutes(0) // image is immediately re-downloaded, but is not deleted
- .expectDeletedImagesAfterMinutes(10)
- .expectDeletedImages("image-1", "my-tag"); // 1h after re-download it is deleted again
- }
-
- /** Same scenario as in {@link #multipleUnusedImagesAreIdentified()} */
- @Test
- public void doesNotDeleteExcludedByIdImages() {
- gcTester.withExistingImages(
- ImageBuilder.forId("parent-image"),
- ImageBuilder.forId("image-1").withParentId("parent-image"),
- ImageBuilder.forId("image-2").withParentId("parent-image"))
- // Normally, image-1 and parent-image should also be deleted, but because we exclude image-1
- // we cannot delete parent-image, so only image-2 is deleted
- .expectDeletedImages(List.of("image-1"), "image-2");
- }
-
- /** Same as in {@link #doesNotDeleteExcludedByIdImages()} but with tags */
- @Test
- public void doesNotDeleteExcludedByTagImages() {
- gcTester.withExistingImages(
- ImageBuilder.forId("parent-image").withTags("rhel-6"),
- ImageBuilder.forId("image-1").withParentId("parent-image").withTags("vespa:6.288.16"),
- ImageBuilder.forId("image-2").withParentId("parent-image").withTags("vespa:6.289.94"))
- .expectDeletedImages(List.of("vespa:6.288.16"), "vespa:6.289.94");
- }
-
- @Test
- public void exludingNotDownloadedImageIsNoop() {
- gcTester.withExistingImages(
- ImageBuilder.forId("parent-image").withTags("rhel-6"),
- ImageBuilder.forId("image-1").withParentId("parent-image").withTags("vespa:6.288.16"),
- ImageBuilder.forId("image-2").withParentId("parent-image").withTags("vespa:6.289.94"))
- .expectDeletedImages(List.of("vespa:6.300.1"), "vespa:6.288.16", "vespa:6.289.94", "rhel-6");
- }
-
- private class ImageGcTester {
- private final DockerEngine docker = mock(DockerEngine.class);
- private final ManualClock clock = new ManualClock();
- private final DockerImageGarbageCollector imageGC = new DockerImageGarbageCollector(docker, clock);
- private final Map<String, Integer> numDeletes = new HashMap<>();
- private boolean initialized = false;
-
- private ImageGcTester withExistingImages(ImageBuilder... images) {
- when(docker.listAllImages()).thenReturn(Arrays.stream(images)
- .map(ImageBuilder::toImage)
- .collect(Collectors.toList()));
- return this;
- }
-
- private ImageGcTester andExistingContainers(ContainerBuilder... containers) {
- when(docker.listAllContainers()).thenReturn(Arrays.stream(containers)
- .map(ContainerBuilder::toContainer)
- .collect(Collectors.toList()));
- return this;
- }
-
- private ImageGcTester expectDeletedImages(String... imageIds) {
- return expectDeletedImagesAfterMinutes(60, imageIds);
- }
-
- private ImageGcTester expectDeletedImages(List<String> except, String... imageIds) {
- return expectDeletedImagesAfterMinutes(60, except, imageIds);
- }
- private ImageGcTester expectDeletedImagesAfterMinutes(int minutesAfter, String... imageIds) {
- return expectDeletedImagesAfterMinutes(minutesAfter, Collections.emptyList(), imageIds);
- }
-
- private ImageGcTester expectDeletedImagesAfterMinutes(int minutesAfter, List<String> except, String... imageIds) {
- if (!initialized) {
- // Run once with a very long expiry to initialize internal state of existing images
- imageGC.deleteUnusedDockerImages(Collections.emptyList(), Duration.ofDays(999));
- initialized = true;
- }
-
- clock.advance(Duration.ofMinutes(minutesAfter));
-
- imageGC.deleteUnusedDockerImages(except, Duration.ofHours(1).minusSeconds(1));
-
- Arrays.stream(imageIds)
- .forEach(imageId -> {
- int newValue = numDeletes.getOrDefault(imageId, 0) + 1;
- numDeletes.put(imageId, newValue);
- verify(docker, times(newValue)).deleteImage(eq(imageId));
- });
-
- verify(docker, times(numDeletes.values().stream().mapToInt(i -> i).sum())).deleteImage(any());
- return this;
- }
- }
-
- /**
- * Serializes object to a JSON string using Jackson, then deserializes it to an instance of toClass
- * (again using Jackson). This can be used to create Jackson classes with no public constructors.
- * @throws IllegalArgumentException if Jackson fails to serialize or deserialize.
- */
- private static <T> T createFrom(Class<T> toClass, Object object) throws IllegalArgumentException {
- final String serialized;
- try {
- serialized = new ObjectMapper().writeValueAsString(object);
- } catch (JsonProcessingException e) {
- throw new IllegalArgumentException("Failed to serialize object " + object + " to "
- + toClass + " with Jackson: " + e, e);
- }
- try {
- return new ObjectMapper().readValue(serialized, toClass);
- } catch (IOException e) {
- throw new IllegalArgumentException("Failed to convert " + serialized + " to "
- + toClass + " with Jackson: " + e, e);
- }
- }
-
- // Workaround for Image class that can't be instantiated directly in Java (instantiate via Jackson instead).
- private static class ImageBuilder {
- // Json property names must match exactly the property names in the Image class.
- @JsonProperty("Id")
- private final String id;
-
- @JsonProperty("ParentId")
- private String parentId = ""; // docker-java returns empty string and not null if the parent is not present
-
- @JsonProperty("RepoTags")
- private String[] repoTags = null;
-
- private ImageBuilder(String id) { this.id = id; }
-
- private static ImageBuilder forId(String id) { return new ImageBuilder(id); }
- private ImageBuilder withParentId(String parentId) { this.parentId = parentId; return this; }
- private ImageBuilder withTags(String... tags) { this.repoTags = tags; return this; }
- private Image toImage() { return createFrom(Image.class, this); }
- }
-
- // Workaround for Container class that can't be instantiated directly in Java (instantiate via Jackson instead).
- private static class ContainerBuilder {
- // Json property names must match exactly the property names in the Container class.
- @JsonProperty("Id")
- private final String id;
-
- @JsonProperty("ImageID")
- private String imageId;
-
- private ContainerBuilder(String id) { this.id = id; }
- private static ContainerBuilder forId(final String id) { return new ContainerBuilder(id); }
- private ContainerBuilder withImageId(String imageId) { this.imageId = imageId; return this; }
-
- private com.github.dockerjava.api.model.Container toContainer() {
- return createFrom(com.github.dockerjava.api.model.Container.class, this);
- }
- }
-}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java
index 158f626cf84..6818a6b991f 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/ExternalMetrics.java
@@ -33,7 +33,7 @@ public class ExternalMetrics {
private static final Logger log = Logger.getLogger(ExternalMetrics.class.getName());
- // NOTE: node service id must be kept in sync with the same constant _value_ used in docker-api:Metrics.java
+ // NOTE: node service id must be kept in sync with the same constant _value_ used in node-admin:Metrics.java
public static final ServiceId VESPA_NODE_SERVICE_ID = toServiceId("vespa.node");
public static final DimensionId ROLE_DIMENSION = toDimensionId("role");
diff --git a/node-admin/pom.xml b/node-admin/pom.xml
index 52873501744..473d106775e 100644
--- a/node-admin/pom.xml
+++ b/node-admin/pom.xml
@@ -21,12 +21,6 @@
<!-- Provided -->
<dependency>
<groupId>com.yahoo.vespa</groupId>
- <artifactId>docker-api</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
<artifactId>config-provisioning</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
diff --git a/node-admin/src/main/application/services.xml b/node-admin/src/main/application/services.xml
index d978b358032..e8747ca2977 100644
--- a/node-admin/src/main/application/services.xml
+++ b/node-admin/src/main/application/services.xml
@@ -5,8 +5,7 @@
<!-- Please update container test when changing this file -->
<accesslog type="json" fileNamePattern="logs/vespa/node-admin/access-json.log.%Y%m%d%H%M%S" symlinkName="access-json.log" compressOnRotation="true" compressionType="zstd" bufferSize='262144' queueSize='1024'/>
- <component id="docker-api" class="com.yahoo.vespa.hosted.dockerapi.DockerEngine" bundle="docker-api"/>
- <component id="metrics" class="com.yahoo.vespa.hosted.dockerapi.metrics.Metrics" bundle="docker-api"/>
+ <component id="metrics" class="com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics" bundle="node-admin"/>
<preprocess:include file="variant.xml" required="false"/>
</container>
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Container.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/Container.java
index 9e304e5ef4d..7aeb43e44f4 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Container.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/Container.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import com.yahoo.config.provision.DockerImage;
@@ -8,7 +8,6 @@ import java.util.Objects;
/**
* @author stiankri
*/
-// TODO: Move this to node-admin when docker-api module can be removed
public class Container {
private final ContainerId id;
public final String hostname;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerId.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerId.java
index b86238324f0..0e6a4835abf 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerId.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerId.java
@@ -1,6 +1,6 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
//
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import java.util.Objects;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerName.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerName.java
index 53bfc59652c..49dacc44335 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerName.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerName.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import java.util.Objects;
import java.util.regex.Pattern;
@@ -9,7 +9,6 @@ import java.util.regex.Pattern;
*
* @author bakksjo
*/
-// TODO: Move this to node-admin when docker-api module can be removed
public class ContainerName {
private static final Pattern LEGAL_CONTAINER_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9-]+$");
private final String name;
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 8bd6382fa5f..e48927279a4 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
@@ -2,13 +2,6 @@
package com.yahoo.vespa.hosted.node.admin.container;
import com.yahoo.config.provision.DockerImage;
-import com.yahoo.vespa.hosted.dockerapi.Container;
-import com.yahoo.vespa.hosted.dockerapi.ContainerId;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
-import com.yahoo.vespa.hosted.dockerapi.ContainerResources;
-import com.yahoo.vespa.hosted.dockerapi.ContainerStats;
-import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
-import com.yahoo.vespa.hosted.dockerapi.RegistryCredentials;
import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.ContainerData;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerResources.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResources.java
index 37e265bf411..f01527f58d1 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerResources.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResources.java
@@ -1,12 +1,11 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import java.util.Objects;
/**
* @author valerijf
*/
-// TODO: Move this to node-admin when docker-api module can be removed
public class ContainerResources {
public static final ContainerResources UNLIMITED = ContainerResources.from(0, 0, 0);
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerStats.java
index dc2db50d3ab..78adcebe31e 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerStats.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -11,7 +11,6 @@ import java.util.Objects;
*
* @author freva
*/
-// TODO: Move this to node-admin when docker-api module can be removed
public class ContainerStats {
private final Map<String, NetworkStats> networkStatsByInterface;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ProcessResult.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResult.java
index eb81b40434a..066a65eb409 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ProcessResult.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResult.java
@@ -1,9 +1,9 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import java.util.Objects;
-// TODO: Consider replacing usages of this with CommandResult when docker-api module can be removed
+// TODO: Replace usages of this with CommandResult
public class ProcessResult {
private final int exitStatus;
private final String output;
@@ -44,4 +44,5 @@ public class ProcessResult {
+ " errors=" + errors
+ " }";
}
+
}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/RegistryCredentials.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentials.java
index 130519ecfd6..6437497fa68 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/RegistryCredentials.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentials.java
@@ -1,5 +1,5 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import java.util.Objects;
@@ -8,7 +8,6 @@ import java.util.Objects;
*
* @author mpolden
*/
-// TODO: Move this to node-admin when docker-api module can be removed
public class RegistryCredentials {
public static final RegistryCredentials none = new RegistryCredentials("", "", "");
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentialsProvider.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentialsProvider.java
index 44e00425a11..1cd342d68a5 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentialsProvider.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentialsProvider.java
@@ -1,8 +1,6 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.container;
-import com.yahoo.vespa.hosted.dockerapi.RegistryCredentials;
-
/**
* Interface for retrieving credentials for a container registry.
*
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Counter.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Counter.java
index 3a0b820c846..9cb26d71275 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Counter.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Counter.java
@@ -1,5 +1,5 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.metrics;
+package com.yahoo.vespa.hosted.node.admin.container.metrics;
/**
* @author freva
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/DimensionMetrics.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/DimensionMetrics.java
index 590ef207e3f..929fcde9492 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/DimensionMetrics.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/DimensionMetrics.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.metrics;
+package com.yahoo.vespa.hosted.node.admin.container.metrics;
import java.util.HashMap;
import java.util.Map;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Dimensions.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Dimensions.java
index 63b92e06505..2999a3c8839 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Dimensions.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Dimensions.java
@@ -1,5 +1,5 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.metrics;
+package com.yahoo.vespa.hosted.node.admin.container.metrics;
import java.util.HashMap;
import java.util.Map;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Gauge.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Gauge.java
index b413475fc2b..d3a7072b08b 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Gauge.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Gauge.java
@@ -1,5 +1,5 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.metrics;
+package com.yahoo.vespa.hosted.node.admin.container.metrics;
/**
* @author freva
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/MetricValue.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricValue.java
index b20aa1b11ff..07a6ffd9d22 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/MetricValue.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricValue.java
@@ -1,5 +1,5 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.metrics;
+package com.yahoo.vespa.hosted.node.admin.container.metrics;
/**
* @author freva
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Metrics.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Metrics.java
index f9b169f0a93..ec3b2d347e5 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/Metrics.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Metrics.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.metrics;
+package com.yahoo.vespa.hosted.node.admin.container.metrics;
import com.google.inject.Inject;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/package-info.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/package-info.java
index 3e511560d94..3f514f96f84 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/metrics/package-info.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/package-info.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
-package com.yahoo.vespa.hosted.dockerapi.metrics;
+package com.yahoo.vespa.hosted.node.admin.container.metrics;
import com.yahoo.osgi.annotation.ExportPackage;
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 9ced178cff9..15705557447 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
@@ -5,8 +5,8 @@ import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.vespa.hosted.dockerapi.Container;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.node.admin.container.Container;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
import com.yahoo.vespa.hosted.node.admin.maintenance.coredump.CoredumpHandler;
import com.yahoo.vespa.hosted.node.admin.maintenance.disk.CoredumpCleanupRule;
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 ab3cb3147fa..16b7e462ad3 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
@@ -1,7 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.coredump;
-import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
+import com.yahoo.vespa.hosted.node.admin.container.ProcessResult;
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;
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 7f79efdeab9..0fd912b528c 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
@@ -2,8 +2,8 @@
package com.yahoo.vespa.hosted.node.admin.maintenance.coredump;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Dimensions;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
+import com.yahoo.vespa.hosted.node.admin.container.metrics.Dimensions;
+import com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.ConvergenceException;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
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 eb04765c621..a0d074c29d9 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
@@ -20,7 +20,7 @@ import com.yahoo.vespa.athenz.identityprovider.client.CsrGenerator;
import com.yahoo.vespa.athenz.identityprovider.client.DefaultIdentityDocumentClient;
import com.yahoo.vespa.athenz.tls.AthenzIdentityVerifier;
import com.yahoo.vespa.athenz.utils.SiaUtils;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
import com.yahoo.vespa.hosted.node.admin.component.ConfigServerInfo;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentTask;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java
index 5d7ab48753f..8c8f3d88a71 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java
@@ -1,10 +1,10 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.nodeadmin;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Counter;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Dimensions;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Gauge;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
+import com.yahoo.vespa.hosted.node.admin.container.metrics.Counter;
+import com.yahoo.vespa.hosted.node.admin.container.metrics.Dimensions;
+import com.yahoo.vespa.hosted.node.admin.container.metrics.Gauge;
+import com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContextManager;
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 1eac9103583..479f2342773 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
@@ -6,7 +6,7 @@ import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.zone.ZoneApi;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
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;
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 5e2ce5e565e..c0406f91c30 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
@@ -12,7 +12,7 @@ import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+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;
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 267e72ea0aa..a41bfac57c0 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
@@ -10,11 +10,6 @@ import com.yahoo.vespa.flags.DoubleFlag;
import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
-import com.yahoo.vespa.hosted.dockerapi.Container;
-import com.yahoo.vespa.hosted.dockerapi.ContainerResources;
-import com.yahoo.vespa.hosted.dockerapi.RegistryCredentials;
-import com.yahoo.vespa.hosted.dockerapi.exception.ContainerNotFoundException;
-import com.yahoo.vespa.hosted.dockerapi.exception.DockerException;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeAttributes;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeMembership;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeRepository;
@@ -22,7 +17,10 @@ import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeState;
import com.yahoo.vespa.hosted.node.admin.configserver.orchestrator.Orchestrator;
import com.yahoo.vespa.hosted.node.admin.configserver.orchestrator.OrchestratorException;
+import com.yahoo.vespa.hosted.node.admin.container.Container;
import com.yahoo.vespa.hosted.node.admin.container.ContainerOperations;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerResources;
+import com.yahoo.vespa.hosted.node.admin.container.RegistryCredentials;
import com.yahoo.vespa.hosted.node.admin.container.RegistryCredentialsProvider;
import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer;
@@ -284,13 +282,9 @@ public class NodeAgentImpl implements NodeAgent {
private void stopServices(NodeAgentContext context) {
context.log(logger, "Stopping services");
if (containerState == ABSENT) return;
- try {
- hasStartedServices = hasResumedNode = false;
- firstSuccessfulHealthCheckInstant = Optional.empty();
- containerOperations.stopServices(context);
- } catch (ContainerNotFoundException e) {
- containerState = ABSENT;
- }
+ hasStartedServices = hasResumedNode = false;
+ firstSuccessfulHealthCheckInstant = Optional.empty();
+ containerOperations.stopServices(context);
}
@Override
@@ -307,8 +301,6 @@ public class NodeAgentImpl implements NodeAgent {
if (!output.isBlank()) {
context.log(logger, "Suspend services output: " + output);
}
- } catch (ContainerNotFoundException e) {
- containerState = ABSENT;
} catch (RuntimeException e) {
// It's bad to continue as-if nothing happened, but on the other hand if we do not proceed to
// remove container, we will not be able to upgrade to fix any problems in the suspend logic!
@@ -424,12 +416,6 @@ public class NodeAgentImpl implements NodeAgent {
context.log(logger, Level.INFO, "Converged");
} catch (ConvergenceException e) {
context.log(logger, e.getMessage());
- } catch (ContainerNotFoundException e) {
- containerState = ABSENT;
- context.log(logger, Level.WARNING, "Container unexpectedly gone, resetting containerState to " + containerState);
- } catch (DockerException e) {
- numberOfUnhandledException++;
- context.log(logger, Level.SEVERE, "Caught a DockerException", e);
} catch (Throwable e) {
numberOfUnhandledException++;
context.log(logger, Level.SEVERE, "Unhandled exception, ignoring", e);
diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerNameTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerNameTest.java
index cace96ab207..f98c78c740a 100644
--- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerNameTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerNameTest.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import org.junit.Test;
diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerResourcesTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResourcesTest.java
index daf4639ad29..d2ff57e5c09 100644
--- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerResourcesTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResourcesTest.java
@@ -1,5 +1,5 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import org.junit.Test;
diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ProcessResultTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResultTest.java
index 590833151f2..f7b832bd566 100644
--- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ProcessResultTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResultTest.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi;
+package com.yahoo.vespa.hosted.node.admin.container;
import org.junit.Test;
diff --git a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/metrics/MetricsTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricsTest.java
index fc153ee0562..6c48fe65142 100644
--- a/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/metrics/MetricsTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricsTest.java
@@ -1,13 +1,13 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.dockerapi.metrics;
+package com.yahoo.vespa.hosted.node.admin.container.metrics;
import org.junit.Test;
import java.util.Map;
import java.util.stream.Collectors;
-import static com.yahoo.vespa.hosted.dockerapi.metrics.Metrics.APPLICATION_HOST;
-import static com.yahoo.vespa.hosted.dockerapi.metrics.Metrics.DimensionType.DEFAULT;
+import static com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics.APPLICATION_HOST;
+import static com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics.DimensionType.DEFAULT;
import static org.junit.Assert.assertEquals;
/**
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 d187c8e5476..04d86a69057 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
@@ -2,7 +2,7 @@
package com.yahoo.vespa.hosted.node.admin.integration;
import com.yahoo.config.provision.DockerImage;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+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;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerOperationsMock.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerOperationsMock.java
index 9600444c8f0..a19e031e41f 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerOperationsMock.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerOperationsMock.java
@@ -2,13 +2,13 @@
package com.yahoo.vespa.hosted.node.admin.integration;
import com.yahoo.config.provision.DockerImage;
-import com.yahoo.vespa.hosted.dockerapi.Container;
-import com.yahoo.vespa.hosted.dockerapi.ContainerId;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
-import com.yahoo.vespa.hosted.dockerapi.ContainerResources;
-import com.yahoo.vespa.hosted.dockerapi.ContainerStats;
-import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
-import com.yahoo.vespa.hosted.dockerapi.RegistryCredentials;
+import com.yahoo.vespa.hosted.node.admin.container.Container;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerId;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerResources;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerStats;
+import com.yahoo.vespa.hosted.node.admin.container.ProcessResult;
+import com.yahoo.vespa.hosted.node.admin.container.RegistryCredentials;
import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
import com.yahoo.vespa.hosted.node.admin.container.ContainerOperations;
import com.yahoo.vespa.hosted.node.admin.nodeagent.ContainerData;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerTester.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerTester.java
index c5092e833b2..9153afd8e54 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerTester.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerTester.java
@@ -4,9 +4,9 @@ package com.yahoo.vespa.hosted.node.admin.integration;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.flags.InMemoryFlagSource;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
-import com.yahoo.vespa.hosted.dockerapi.RegistryCredentials;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
+import com.yahoo.vespa.hosted.node.admin.container.RegistryCredentials;
+import com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
import com.yahoo.vespa.hosted.node.admin.configserver.orchestrator.Orchestrator;
import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/MultiContainerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/MultiContainerTest.java
index 78e9dc78a0f..86db3ae092e 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/MultiContainerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/MultiContainerTest.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.hosted.node.admin.integration;
import com.yahoo.config.provision.DockerImage;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+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.configserver.noderepository.NodeState;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentContext;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RebootTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RebootTest.java
index c77c3034142..dad02f46d88 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RebootTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RebootTest.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.hosted.node.admin.integration;
import com.yahoo.config.provision.DockerImage;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+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.nodeadmin.NodeAdminStateUpdater;
import org.junit.Test;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RestartTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RestartTest.java
index 319623e69de..160948d7996 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RestartTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RestartTest.java
@@ -2,7 +2,7 @@
package com.yahoo.vespa.hosted.node.admin.integration;
import com.yahoo.config.provision.DockerImage;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeAttributes;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
import org.junit.Test;
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 afa2262b2df..3c22305d006 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
@@ -1,7 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.coredump;
-import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
+import com.yahoo.vespa.hosted.node.admin.container.ProcessResult;
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;
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 6dd7241fd63..d5ac850c770 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
@@ -2,8 +2,8 @@
package com.yahoo.vespa.hosted.node.admin.maintenance.coredump;
import com.yahoo.test.ManualClock;
-import com.yahoo.vespa.hosted.dockerapi.metrics.DimensionMetrics;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
+import com.yahoo.vespa.hosted.node.admin.container.metrics.DimensionMetrics;
+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;
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 b33f52ff629..68519cfc53d 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
@@ -2,7 +2,7 @@
package com.yahoo.vespa.hosted.node.admin.nodeadmin;
import com.yahoo.test.ManualClock;
-import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
+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 org.junit.Test;
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 ff9ca86365d..454374fe3bd 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
@@ -9,12 +9,6 @@ import com.yahoo.config.provision.NodeType;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
-import com.yahoo.vespa.hosted.dockerapi.Container;
-import com.yahoo.vespa.hosted.dockerapi.ContainerId;
-import com.yahoo.vespa.hosted.dockerapi.ContainerName;
-import com.yahoo.vespa.hosted.dockerapi.ContainerResources;
-import com.yahoo.vespa.hosted.dockerapi.RegistryCredentials;
-import com.yahoo.vespa.hosted.dockerapi.exception.DockerException;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeAttributes;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeSpec;
@@ -22,7 +16,12 @@ import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeState;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.OrchestratorStatus;
import com.yahoo.vespa.hosted.node.admin.configserver.orchestrator.Orchestrator;
import com.yahoo.vespa.hosted.node.admin.configserver.orchestrator.OrchestratorException;
+import com.yahoo.vespa.hosted.node.admin.container.Container;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerId;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerName;
import com.yahoo.vespa.hosted.node.admin.container.ContainerOperations;
+import com.yahoo.vespa.hosted.node.admin.container.ContainerResources;
+import com.yahoo.vespa.hosted.node.admin.container.RegistryCredentials;
import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer;
import com.yahoo.vespa.hosted.node.admin.maintenance.identity.CredentialsMaintainer;
@@ -556,12 +555,12 @@ public class NodeAgentImplTest {
NodeAgentImpl nodeAgent = spy(makeNodeAgent(null, false));
when(containerOperations.pullImageAsyncIfNeeded(any(), eq(dockerImage), any())).thenReturn(false);
- doThrow(new DockerException("Failed to set up network")).doNothing().when(containerOperations).startContainer(eq(context));
+ doThrow(new RuntimeException("Failed to set up network")).doNothing().when(containerOperations).startContainer(eq(context));
try {
nodeAgent.doConverge(context);
- fail("Expected to get DockerException");
- } catch (DockerException ignored) { }
+ fail("Expected to get RuntimeException");
+ } catch (RuntimeException ignored) { }
verify(containerOperations, never()).removeContainer(eq(context), any());
verify(containerOperations, times(1)).createContainer(eq(context), any(), any());
diff --git a/pom.xml b/pom.xml
index fa7fa8f6ae3..8cc214930f4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,7 +71,6 @@
<module>controller-api</module>
<module>controller-server</module>
<module>defaults</module>
- <module>docker-api</module>
<module>docproc</module>
<module>docprocs</module>
<module>document</module>