summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--application-model/pom.xml2
-rw-r--r--clustercontroller-apps/pom.xml2
-rw-r--r--clustercontroller-apputil/pom.xml2
-rw-r--r--clustercontroller-core/pom.xml2
-rw-r--r--clustercontroller-utils/pom.xml2
-rw-r--r--config-model/pom.xml2
-rwxr-xr-xconfig/pom.xml2
-rw-r--r--docker-api/pom.xml30
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/docker/api/docker/DockerApi.java56
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/docker/api/docker/package-info.java5
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Container.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/Container.java)2
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerInfoImpl.java33
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerName.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/ContainerName.java)2
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/Docker.java)28
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImage.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImage.java)2
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImpl.java)251
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ProcessResult.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/ProcessResult.java)2
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/StartContainerCommandImpl.java139
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/VespaSSLConfig.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/VespaSSLConfig.java)3
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/package-info.java (renamed from node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/package-info.java)2
-rw-r--r--docker-api/src/main/resources/configdefinitions/docker.def (renamed from node-admin/src/main/resources/configdefinitions/docker.def)2
-rw-r--r--docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerNameTest.java (renamed from node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/ContainerNameTest.java)9
-rw-r--r--docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImplTest.java (renamed from node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImplTest.java)53
-rw-r--r--docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerTest.java (renamed from node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerTest.java)18
-rw-r--r--docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ProcessResultTest.java (renamed from node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/ProcessResultTest.java)4
-rw-r--r--node-admin/pom.xml54
-rw-r--r--node-admin/src/main/application/services.xml2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/ContainerNodeSpec.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceScheduler.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceSchedulerImpl.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java7
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperations.java190
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsInterface.java25
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAttributes.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProviderImpl.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/PrefixLogger.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/maintenance/Maintainer.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ComponentsProviderWithMocks.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerFailTest.java16
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java79
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MaintenanceSchedulerMock.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MultiDockerTest.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeStateTest.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ResumeTest.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java8
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsTest.java50
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java213
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/maintenance/MaintainerTest.java2
54 files changed, 717 insertions, 634 deletions
diff --git a/application-model/pom.xml b/application-model/pom.xml
index 388d20ea885..f68deb83993 100644
--- a/application-model/pom.xml
+++ b/application-model/pom.xml
@@ -80,6 +80,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:rawtypes</arg>
<arg>-Xlint:unchecked</arg>
diff --git a/clustercontroller-apps/pom.xml b/clustercontroller-apps/pom.xml
index 8830c00f89d..07a5a27c9d4 100644
--- a/clustercontroller-apps/pom.xml
+++ b/clustercontroller-apps/pom.xml
@@ -69,6 +69,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:rawtypes</arg>
<arg>-Xlint:unchecked</arg>
diff --git a/clustercontroller-apputil/pom.xml b/clustercontroller-apputil/pom.xml
index 5dc2c2a83d9..78e0b0ea3f4 100644
--- a/clustercontroller-apputil/pom.xml
+++ b/clustercontroller-apputil/pom.xml
@@ -55,6 +55,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:unchecked</arg>
<arg>-Xlint:deprecation</arg>
diff --git a/clustercontroller-core/pom.xml b/clustercontroller-core/pom.xml
index 93317a54dec..053f4efd2a7 100644
--- a/clustercontroller-core/pom.xml
+++ b/clustercontroller-core/pom.xml
@@ -81,6 +81,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:all</arg>
<arg>-Xlint:-serial</arg>
diff --git a/clustercontroller-utils/pom.xml b/clustercontroller-utils/pom.xml
index 7cf7cf0fdde..3c03fe754e7 100644
--- a/clustercontroller-utils/pom.xml
+++ b/clustercontroller-utils/pom.xml
@@ -51,6 +51,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:unchecked</arg>
<arg>-Xlint:deprecation</arg>
diff --git a/config-model/pom.xml b/config-model/pom.xml
index e526b12c10c..42497224b0b 100644
--- a/config-model/pom.xml
+++ b/config-model/pom.xml
@@ -321,6 +321,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:rawtypes</arg>
<arg>-Xlint:unchecked</arg>
diff --git a/config/pom.xml b/config/pom.xml
index ace03e11555..73c8cd5b5ad 100755
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -183,6 +183,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:all</arg>
<arg>-Werror</arg>
diff --git a/docker-api/pom.xml b/docker-api/pom.xml
index c8aa213d375..6c06481564d 100644
--- a/docker-api/pom.xml
+++ b/docker-api/pom.xml
@@ -24,6 +24,12 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>application-model</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.0.3</version>
@@ -43,6 +49,28 @@
<artifactId>lz4</artifactId>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>application</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-junit</artifactId>
+ <version>2.0.0.0</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@@ -56,6 +84,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:all</arg>
<arg>-Werror</arg>
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/docker/api/docker/DockerApi.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/docker/api/docker/DockerApi.java
deleted file mode 100644
index f114adfb988..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/docker/api/docker/DockerApi.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.yahoo.vespa.hosted.docker.api.docker;
-
-import com.github.dockerjava.core.DefaultDockerClientConfig;
-import com.github.dockerjava.core.DockerClientImpl;
-import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory;
-
-import com.github.dockerjava.api.DockerClient;
-import com.yahoo.component.AbstractComponent;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A class wrapping the DockerJava library for OSGI to avoid dependency problem between this library and Vespa.
- * @author dybdahl
- */
-public class DockerApi extends AbstractComponent {
- private static final String LABEL_NAME_MANAGEDBY = "com.yahoo.vespa.managedby";
- private static final String LABEL_VALUE_MANAGEDBY = "node-admin";
- private static final Map<String, String> CONTAINER_LABELS = new HashMap<>();
-
- private static final int DOCKER_MAX_PER_ROUTE_CONNECTIONS = 10;
- private static final int DOCKER_MAX_TOTAL_CONNECTIONS = 100;
- private static final int DOCKER_CONNECT_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(100);
- private static final int DOCKER_READ_TIMEOUT_MILLIS = (int) TimeUnit.MINUTES.toMillis(30);
-
- static {
- CONTAINER_LABELS.put(LABEL_NAME_MANAGEDBY, LABEL_VALUE_MANAGEDBY);
- }
-
- private final DockerClient dockerClient;
-
- public DockerApi() {
- dockerClient = DockerClientImpl.getInstance(new DefaultDockerClientConfig.Builder()
- // Talks HTTP(S) over a TCP port. The docker client library does only support tcp:// and unix://
- .withDockerHost("unix:///host/var/run/docker.sock") // Alternatively, but
- // does not work due to certificate issues as if Aug 18th 2016: config.uri().replace("https", "tcp"))
- .withDockerTlsVerify(false)
- //.withCustomSslConfig(new VespaSSLConfig(config))
- // We can specify which version of the docker remote API to use, otherwise, use latest
- // e.g. .withApiVersion("1.23")
- .build())
- .withDockerCmdExecFactory(
- new JerseyDockerCmdExecFactory()
- .withMaxPerRouteConnections(DOCKER_MAX_PER_ROUTE_CONNECTIONS)
- .withMaxTotalConnections(DOCKER_MAX_TOTAL_CONNECTIONS)
- .withConnectTimeout(DOCKER_CONNECT_TIMEOUT_MILLIS)
- .withReadTimeout(DOCKER_READ_TIMEOUT_MILLIS)
- );
- }
-
- public DockerClient getDockerClient() {
- return dockerClient;
- }
-}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/docker/api/docker/package-info.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/docker/api/docker/package-info.java
deleted file mode 100644
index 004507fb58f..00000000000
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/docker/api/docker/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-@ExportPackage
-package com.yahoo.vespa.hosted.docker.api.docker;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/Container.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Container.java
index 65929447b0e..1dc54311277 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/Container.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Container.java
@@ -1,5 +1,5 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import com.yahoo.vespa.applicationmodel.HostName;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerInfoImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerInfoImpl.java
new file mode 100644
index 00000000000..fcfad041b76
--- /dev/null
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerInfoImpl.java
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. 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.command.InspectContainerResponse;
+
+import java.util.Optional;
+
+class ContainerInfoImpl implements Docker.ContainerInfo {
+
+ private final ContainerName containerName;
+ private final InspectContainerResponse inspectContainerResponse;
+
+ ContainerInfoImpl(ContainerName containerName, InspectContainerResponse inspectContainerResponse) {
+ this.containerName = containerName;
+ this.inspectContainerResponse = inspectContainerResponse;
+ }
+
+ @Override
+ public Optional<Integer> getPid() {
+ InspectContainerResponse.ContainerState state = inspectContainerResponse.getState();
+ Integer containerPid = -1;
+ if (state.getRunning()) {
+ containerPid = state.getPid();
+ if (containerPid == null) {
+ throw new RuntimeException("PID of running container " + containerName + " is null");
+ }
+
+ return Optional.of(containerPid);
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/ContainerName.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerName.java
index 3784c8fcf9b..3ccd71bee6e 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/ContainerName.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerName.java
@@ -1,5 +1,5 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import java.util.Objects;
import java.util.regex.Pattern;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/Docker.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java
index 113784568ce..f8e636c87c0 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/Docker.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java
@@ -1,5 +1,5 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import com.yahoo.vespa.applicationmodel.HostName;
@@ -10,11 +10,24 @@ import java.util.Set;
import java.util.concurrent.CompletableFuture;
/**
- * @author stiankri
+ * API to simplify the com.github.dockerjava API for clients,
+ * and to avoid OSGi exporting those classes.
*/
public interface Docker {
+ interface StartContainerCommand {
+ StartContainerCommand withLabel(String name, String value);
+ StartContainerCommand withEnvironment(String name, String value);
+ StartContainerCommand withVolume(String path, String volumePath);
+ StartContainerCommand withMemoryInMb(long megaBytes);
+ StartContainerCommand withNetworkMode(String mode);
+ StartContainerCommand withIpv6Address(String address);
+ void start();
+ }
- void startContainer(DockerImage dockerImage, HostName hostName, ContainerName containerName, InetAddress nodeInetAddress, double minCpuCores, double minDiskAvailableGb, double minMainMemoryAvailableGb);
+ StartContainerCommand createStartContainerCommand(
+ DockerImage dockerImage,
+ ContainerName containerName,
+ HostName hostName);
void stopContainer(ContainerName containerName);
@@ -28,8 +41,6 @@ public interface Docker {
boolean imageIsDownloaded(DockerImage image);
- String getVespaVersion(ContainerName containerName);
-
void deleteImage(DockerImage dockerImage);
/**
@@ -44,4 +55,11 @@ public interface Docker {
* @throws RuntimeException (or some subclass thereof) on failure, including docker failure, command failure
*/
ProcessResult executeInContainer(ContainerName containerName, String... args);
+
+ interface ContainerInfo {
+ /** returns Optional.empty() if not running. */
+ Optional<Integer> getPid();
+ }
+
+ ContainerInfo inspectContainer(ContainerName containerName);
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImage.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImage.java
index f7cd6f76d3e..231eac84b38 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImage.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImage.java
@@ -1,5 +1,5 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import java.util.Objects;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java
index 915bd3b71f5..464c4fc8cac 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImpl.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java
@@ -1,16 +1,13 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import com.github.dockerjava.api.DockerClient;
-import com.github.dockerjava.api.command.CreateContainerCmd;
-import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.ExecStartCmd;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.command.InspectExecResponse;
import com.github.dockerjava.api.exception.DockerClientException;
import com.github.dockerjava.api.exception.DockerException;
-import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
@@ -18,25 +15,12 @@ import com.github.dockerjava.core.RemoteApiVersion;
import com.github.dockerjava.core.command.ExecStartResultCallback;
import com.github.dockerjava.core.command.PullImageResultCallback;
import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory;
-import com.google.common.base.Joiner;
-import com.google.common.io.CharStreams;
import com.google.inject.Inject;
-import com.yahoo.nodeadmin.docker.DockerConfig;
+import com.yahoo.log.LogLevel;
import com.yahoo.vespa.applicationmodel.HostName;
-import static com.yahoo.vespa.defaults.Defaults.getDefaults;
-
-import com.yahoo.vespa.hosted.node.admin.nodeagent.DockerOperations;
-import com.yahoo.vespa.hosted.node.admin.util.Environment;
-import com.yahoo.vespa.hosted.node.admin.util.PrefixLogger;
-import com.yahoo.vespa.hosted.node.maintenance.Maintainer;
import javax.annotation.concurrent.GuardedBy;
import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
@@ -47,64 +31,36 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-/**
- * @author stiankri
- */
-public class DockerImpl implements Docker {
- private static final PrefixLogger NODE_ADMIN_LOGGER = PrefixLogger.getNodeAdminLogger(DockerImpl.class);
+class DockerImpl implements Docker {
- private static final int SECONDS_TO_WAIT_BEFORE_KILLING = 10;
- private static final String FRAMEWORK_CONTAINER_PREFIX = "/";
- private static final Pattern VESPA_VERSION_PATTERN = Pattern.compile("^(\\S*)$", Pattern.MULTILINE);
+ private static final Logger logger = Logger.getLogger(DockerImpl.class.getName());
private static final String LABEL_NAME_MANAGEDBY = "com.yahoo.vespa.managedby";
private static final String LABEL_VALUE_MANAGEDBY = "node-admin";
- private static final Map<String, String> CONTAINER_LABELS = new HashMap<>();
+
+ private static final int SECONDS_TO_WAIT_BEFORE_KILLING = 10;
+ private static final String FRAMEWORK_CONTAINER_PREFIX = "/";
private static final int DOCKER_MAX_PER_ROUTE_CONNECTIONS = 10;
private static final int DOCKER_MAX_TOTAL_CONNECTIONS = 100;
private static final int DOCKER_CONNECT_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(100);
private static final int DOCKER_READ_TIMEOUT_MILLIS = (int) TimeUnit.MINUTES.toMillis(30);
- static final String DOCKER_CUSTOM_IP6_NETWORK_NAME = "habla";
- static {
- CONTAINER_LABELS.put(LABEL_NAME_MANAGEDBY, LABEL_VALUE_MANAGEDBY);
- }
-
- private static final List<String> DIRECTORIES_TO_MOUNT = Arrays.asList(
- getDefaults().underVespaHome("logs"),
- getDefaults().underVespaHome("var/cache"),
- getDefaults().underVespaHome("var/crash"),
- getDefaults().underVespaHome("var/db/jdisc"),
- getDefaults().underVespaHome("var/db/vespa"),
- getDefaults().underVespaHome("var/jdisc_container"),
- getDefaults().underVespaHome("var/jdisc_core"),
- getDefaults().underVespaHome("var/logstash-forwarder"),
- getDefaults().underVespaHome("var/maven"),
- getDefaults().underVespaHome("var/scoreboards"),
- getDefaults().underVespaHome("var/service"),
- getDefaults().underVespaHome("var/share"),
- getDefaults().underVespaHome("var/spool"),
- getDefaults().underVespaHome("var/vespa"),
- getDefaults().underVespaHome("var/yca"),
- getDefaults().underVespaHome("var/ycore++"),
- getDefaults().underVespaHome("var/zookeeper"));
-
- final DockerClient docker;
+ private static final String DOCKER_CUSTOM_IP6_NETWORK_NAME = "habla";
private final Object monitor = new Object();
-
@GuardedBy("monitor")
private final Map<DockerImage, CompletableFuture<DockerImage>> scheduledPulls = new HashMap<>();
+ final DockerClient dockerClient;
+
DockerImpl(final DockerClient dockerClient) {
- this.docker = dockerClient;
+ this.dockerClient = dockerClient;
}
@Inject
@@ -119,20 +75,20 @@ public class DockerImpl implements Docker {
try {
remoteApiVersion = RemoteApiVersion.parseConfig(DockerClientImpl.getInstance()
.withDockerCmdExecFactory(dockerFactory).versionCmd().exec().getApiVersion());
- NODE_ADMIN_LOGGER.info("Found version of remote docker API: "+ remoteApiVersion);
+ logger.info("Found version of remote docker API: "+ remoteApiVersion);
// From version 1.24 a field was removed which causes trouble with the current docker java code.
// When this is fixed, we can remove this and do not specify version.
if (remoteApiVersion.isGreaterOrEqual(RemoteApiVersion.VERSION_1_24)) {
remoteApiVersion = RemoteApiVersion.VERSION_1_23;
- NODE_ADMIN_LOGGER.info("Found version 1.24 or newer of remote API, using 1.23.");
+ logger.info("Found version 1.24 or newer of remote API, using 1.23.");
}
} catch (Exception e) {
- NODE_ADMIN_LOGGER.error("Failed when trying to figure out remote API version of docker, using 1.23", e);
+ logger.log(LogLevel.ERROR, "Failed when trying to figure out remote API version of docker, using 1.23", e);
remoteApiVersion = RemoteApiVersion.VERSION_1_23;
}
// DockerClientImpl.getInstance().infoCmd().exec().getServerVersion();
- this.docker = DockerClientImpl.getInstance(new DefaultDockerClientConfig.Builder()
+ this.dockerClient = DockerClientImpl.getInstance(new DefaultDockerClientConfig.Builder()
.withDockerHost(config.uri())
.withApiVersion(remoteApiVersion)
.build())
@@ -149,7 +105,7 @@ public class DockerImpl implements Docker {
completionListener = new CompletableFuture<>();
scheduledPulls.put(image, completionListener);
}
- docker.pullImageCmd(image.asString()).exec(new ImagePullCallback(image));
+ dockerClient.pullImageCmd(image.asString()).exec(new ImagePullCallback(image));
return completionListener;
}
@@ -165,7 +121,7 @@ public class DockerImpl implements Docker {
@Override
public boolean imageIsDownloaded(final DockerImage dockerImage) {
try {
- List<Image> images = docker.listImagesCmd().withShowAll(true).exec();
+ List<Image> images = dockerClient.listImagesCmd().withShowAll(true).exec();
return images.stream().
flatMap(image -> Arrays.stream(image.getRepoTags())).
anyMatch(tag -> tag.equals(dockerImage.asString()));
@@ -175,82 +131,16 @@ public class DockerImpl implements Docker {
}
@Override
- public void startContainer(
- final DockerImage dockerImage,
- final HostName hostName,
- final ContainerName containerName,
- final InetAddress nodeInetAddress,
- double minCpuCores,
- double minDiskAvailableGb,
- double minMainMemoryAvailableGb) {
- try {
- String nodeIpAddress = nodeInetAddress.getHostAddress();
- final boolean isRunningIPv6 = nodeInetAddress instanceof Inet6Address;
- final double GIGA = Math.pow(2.0, 30.0);
-
- NODE_ADMIN_LOGGER.info("--- Starting up " + hostName.s() + " with node IP: " + nodeIpAddress +
- ", running IPv6: " + isRunningIPv6);
-
- CreateContainerCmd containerConfigBuilder = docker.createContainerCmd(dockerImage.asString())
- .withName(containerName.asString())
- .withLabels(CONTAINER_LABELS)
- .withEnv(new String[]{"CONFIG_SERVER_ADDRESS=" + Joiner.on(',').join(Environment.getConfigServerHosts())})
- .withHostName(hostName.s())
- .withBinds(applicationStorageToMount(containerName));
-
- // TODO: Enforce disk constraints
- // TODO: Consider if CPU shares or quoata should be set. For now we are just assuming they are
- // nicely controlled by docker.
- if (minMainMemoryAvailableGb > 0.00001) {
- containerConfigBuilder.withMemory((long) (GIGA * minMainMemoryAvailableGb));
- }
-
- //If container's IP address is IPv6, use our custom network mode and let docker handle networking.
- //If container's IP address is IPv4, set up networking manually as before. In the future docker should
- //set up the IPv4 network as well.
- if (isRunningIPv6) {
- containerConfigBuilder.withNetworkMode(DOCKER_CUSTOM_IP6_NETWORK_NAME).withIpv6Address(nodeIpAddress);
- } else {
- containerConfigBuilder.withNetworkMode("none");
- }
-
- CreateContainerResponse response = containerConfigBuilder.exec();
- docker.startContainerCmd(response.getId()).exec();
-
- if (! isRunningIPv6) {
- setupContainerIPv4Networking(containerName, hostName);
- }
- } catch (IOException | DockerException e) {
- throw new RuntimeException("Failed to start container " + containerName.asString(), e);
- }
- }
-
- @Override
- public String getVespaVersion(final ContainerName containerName) {
- ProcessResult result = executeInContainer(containerName, DockerOperations.GET_VESPA_VERSION_COMMAND);
- if (!result.isSuccess()) {
- throw new RuntimeException("Container " + containerName.asString() + ": Command "
- + Arrays.toString(DockerOperations.GET_VESPA_VERSION_COMMAND) + " failed: " + result);
- }
- return parseVespaVersion(result.getOutput())
- .orElseThrow(() -> new RuntimeException(
- "Container " + containerName.asString() + ": Failed to parse vespa version from "
- + result.getOutput()));
- }
-
- // Returns empty if vespa version cannot be parsed.
- static Optional<String> parseVespaVersion(final String rawVespaVersion) {
- if (rawVespaVersion == null) return Optional.empty();
-
- final Matcher matcher = VESPA_VERSION_PATTERN.matcher(rawVespaVersion.trim());
- return matcher.find() ? Optional.of(matcher.group(1)) : Optional.empty();
+ public StartContainerCommand createStartContainerCommand(DockerImage image, ContainerName name, HostName hostName) {
+ return new StartContainerCommandImpl(dockerClient, image, name, hostName)
+ .withLabel(LABEL_NAME_MANAGEDBY, LABEL_VALUE_MANAGEDBY);
}
@Override
public ProcessResult executeInContainer(ContainerName containerName, String... args) {
assert args.length >= 1;
try {
- final ExecCreateCmdResponse response = docker.execCreateCmd(containerName.asString())
+ final ExecCreateCmdResponse response = dockerClient.execCreateCmd(containerName.asString())
.withCmd(args)
.withAttachStdout(true)
.withAttachStderr(true)
@@ -258,10 +148,10 @@ public class DockerImpl implements Docker {
ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayOutputStream errors = new ByteArrayOutputStream();
- ExecStartCmd execStartCmd = docker.execStartCmd(response.getId());
+ ExecStartCmd execStartCmd = dockerClient.execStartCmd(response.getId());
execStartCmd.exec(new ExecStartResultCallback(output, errors)).awaitCompletion();
- final InspectExecResponse state = docker.inspectExecCmd(execStartCmd.getExecId()).exec();
+ final InspectExecResponse state = dockerClient.inspectExecCmd(execStartCmd.getExecId()).exec();
assert !state.isRunning();
Integer exitCode = state.getExitCode();
assert exitCode != null;
@@ -273,72 +163,10 @@ public class DockerImpl implements Docker {
}
}
- private void setupContainerIPv4Networking(ContainerName containerName,
- HostName hostName) throws UnknownHostException {
- PrefixLogger logger = PrefixLogger.getNodeAgentLogger(DockerImpl.class, containerName);
-
- InspectContainerResponse containerInfo = docker.inspectContainerCmd(containerName.asString()).exec();
- InspectContainerResponse.ContainerState state = containerInfo.getState();
- Integer containerPid = -1;
- if (state.getRunning()) {
- containerPid = state.getPid();
- if (containerPid == null) {
- throw new RuntimeException("PID of running container for host " + hostName + " is null");
- }
- }
-
- InetAddress inetAddress = InetAddress.getByName(hostName.s());
- String ipAddress = inetAddress.getHostAddress();
-
- final List<String> command = new LinkedList<>();
- command.add("sudo");
- command.add(getDefaults().underVespaHome("libexec/vespa/node-admin/configure-container-networking.py"));
-
- Environment.NetworkType networkType = Environment.networkType();
- if (networkType != Environment.NetworkType.normal) {
- command.add("--" + networkType);
- }
- command.add(containerPid.toString());
- command.add(ipAddress);
-
- for (int retry = 0; retry < 30; ++retry) {
- try {
- runCommand(command);
- logger.info("Done setting up network");
- return;
- } catch (Exception e) {
- final int sleepSecs = 3;
- logger.warning("Failed to configure network with command " + command
- + ", will retry in " + sleepSecs + " seconds", e);
- try {
- Thread.sleep(sleepSecs * 1000);
- } catch (InterruptedException e1) {
- logger.warning("Sleep interrupted", e1);
- }
- }
- }
- }
-
- private void runCommand(final List<String> command) throws Exception {
- ProcessBuilder builder = new ProcessBuilder(command);
- builder.redirectErrorStream(true);
- Process process = builder.start();
-
- String output = CharStreams.toString(new InputStreamReader(process.getInputStream()));
- int resultCode = process.waitFor();
- if (resultCode != 0) {
- throw new Exception("Command " + Joiner.on(' ').join(command) + " failed: " + output);
- }
- }
-
- static List<Bind> applicationStorageToMount(ContainerName containerName) {
- return Stream.concat(
- Stream.of("/etc/hosts:/etc/hosts"),
- DIRECTORIES_TO_MOUNT.stream().map(directory ->
- Maintainer.pathInHostFromPathInNode(containerName, directory).toString() +
- ":" + directory))
- .map(Bind::parse)
- .collect(Collectors.toList());
+ @Override
+ public ContainerInfo inspectContainer(ContainerName containerName) {
+ InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerName.asString()).exec();
+ return new ContainerInfoImpl(containerName, containerInfo);
}
@Override
@@ -346,7 +174,7 @@ public class DockerImpl implements Docker {
Optional<com.github.dockerjava.api.model.Container> dockerContainer = getContainerFromName(containerName, true);
if (dockerContainer.isPresent()) {
try {
- docker.stopContainerCmd(dockerContainer.get().getId()).withTimeout(SECONDS_TO_WAIT_BEFORE_KILLING).exec();
+ dockerClient.stopContainerCmd(dockerContainer.get().getId()).withTimeout(SECONDS_TO_WAIT_BEFORE_KILLING).exec();
} catch (DockerException e) {
throw new RuntimeException("Failed to stop container", e);
}
@@ -358,7 +186,7 @@ public class DockerImpl implements Docker {
Optional<com.github.dockerjava.api.model.Container> dockerContainer = getContainerFromName(containerName, true);
if (dockerContainer.isPresent()) {
try {
- docker.removeContainerCmd(dockerContainer.get().getId()).exec();
+ dockerClient.removeContainerCmd(dockerContainer.get().getId()).exec();
} catch (DockerException e) {
throw new RuntimeException("Failed to delete container", e);
}
@@ -368,7 +196,7 @@ public class DockerImpl implements Docker {
@Override
public List<Container> getAllManagedContainers() {
try {
- return docker.listContainersCmd().withShowAll(true).exec().stream()
+ return dockerClient.listContainersCmd().withShowAll(true).exec().stream()
.filter(this::isManaged)
.flatMap(this::asContainer)
.collect(Collectors.toList());
@@ -387,7 +215,7 @@ public class DockerImpl implements Docker {
private Stream<Container> asContainer(com.github.dockerjava.api.model.Container dockerClientContainer) {
try {
- final InspectContainerResponse response = docker.inspectContainerCmd(dockerClientContainer.getId()).exec();
+ final InspectContainerResponse response = dockerClient.inspectContainerCmd(dockerClientContainer.getId()).exec();
return Stream.of(new Container(
new HostName(response.getConfig().getHostName()),
new DockerImage(dockerClientContainer.getImage()),
@@ -403,7 +231,7 @@ public class DockerImpl implements Docker {
private Optional<com.github.dockerjava.api.model.Container> getContainerFromName(
final ContainerName containerName, final boolean alsoGetStoppedContainers) {
try {
- return docker.listContainersCmd().withShowAll(alsoGetStoppedContainers).exec().stream()
+ return dockerClient.listContainersCmd().withShowAll(alsoGetStoppedContainers).exec().stream()
.filter(this::isManaged)
.filter(container -> matchName(container, containerName.asString()))
.findFirst();
@@ -412,12 +240,12 @@ public class DockerImpl implements Docker {
}
}
-
private boolean isManaged(final com.github.dockerjava.api.model.Container container) {
final Map<String, String> labels = container.getLabels();
if (labels == null) {
return false;
}
+
return LABEL_VALUE_MANAGEDBY.equals(labels.get(LABEL_NAME_MANAGEDBY));
}
@@ -431,12 +259,7 @@ public class DockerImpl implements Docker {
@Override
public void deleteImage(final DockerImage dockerImage) {
- try {
- NODE_ADMIN_LOGGER.info("Deleting docker image " + dockerImage);
- docker.removeImageCmd(dockerImage.asString()).exec();
- } catch (DockerException e) {
- NODE_ADMIN_LOGGER.warning("Could not delete docker image " + dockerImage, e);
- }
+ dockerClient.removeImageCmd(dockerImage.asString()).exec();
}
@Override
@@ -456,7 +279,7 @@ public class DockerImpl implements Docker {
final Map<String, String> dependencies = new HashMap<>();
// Populate maps with images (including tags) and their dependencies (parents).
- for (Image image : docker.listImagesCmd().withShowAll(true).exec()) {
+ for (Image image : dockerClient.listImagesCmd().withShowAll(true).exec()) {
objects.put(image.getId(), new DockerObject(image.getId(), DockerObjectType.IMAGE));
if (image.getParentId() != null && !image.getParentId().isEmpty()) {
dependencies.put(image.getId(), image.getParentId());
@@ -468,7 +291,7 @@ public class DockerImpl implements Docker {
}
// Populate maps with containers and their dependency to the image they run on.
- for (com.github.dockerjava.api.model.Container container : docker.listContainersCmd().withShowAll(true).exec()) {
+ for (com.github.dockerjava.api.model.Container container : dockerClient.listContainersCmd().withShowAll(true).exec()) {
objects.put(container.getId(), new DockerObject(container.getId(), DockerObjectType.CONTAINER));
dependencies.put(container.getId(), container.getImage());
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/ProcessResult.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ProcessResult.java
index 95d71997b14..52796a694ce 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/ProcessResult.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ProcessResult.java
@@ -1,5 +1,5 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import java.util.Objects;
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/StartContainerCommandImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/StartContainerCommandImpl.java
new file mode 100644
index 00000000000..e03bab805fe
--- /dev/null
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/StartContainerCommandImpl.java
@@ -0,0 +1,139 @@
+// Copyright 2016 Yahoo Inc. 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.command.CreateContainerResponse;
+import com.github.dockerjava.api.exception.DockerException;
+import com.github.dockerjava.api.model.Bind;
+import com.yahoo.vespa.applicationmodel.HostName;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+class StartContainerCommandImpl implements Docker.StartContainerCommand {
+
+ private final DockerClient docker;
+ private final DockerImage dockerImage;
+ private final ContainerName containerName;
+ private final HostName hostName;
+ private final Map<String, String> labels = new HashMap<>();
+ private final List<String> environmentAssignments = new ArrayList<>();
+ private final List<String> volumeBindSpecs = new ArrayList<>();
+
+ private Optional<Long> memoryInB = Optional.empty();
+ private Optional<String> networkMode = Optional.empty();
+ private Optional<String> ipv6Address = Optional.empty();
+
+ StartContainerCommandImpl(DockerClient docker,
+ DockerImage dockerImage,
+ ContainerName containerName,
+ HostName hostName) {
+ this.docker = docker;
+ this.dockerImage = dockerImage;
+ this.containerName = containerName;
+ this.hostName = hostName;
+ }
+
+ @Override
+ public Docker.StartContainerCommand withLabel(String name, String value) {
+ assert name.indexOf("=") == -1;
+ labels.put(name, value);
+ return this;
+ }
+
+ @Override
+ public Docker.StartContainerCommand withEnvironment(String name, String value) {
+ assert name.indexOf('=') == -1;
+ environmentAssignments.add(name + "=" + value);
+ return this;
+ }
+
+ @Override
+ public Docker.StartContainerCommand withVolume(String path, String volumePath) {
+ assert path.indexOf(':') == -1;
+ volumeBindSpecs.add(path + ":" + volumePath);
+ return this;
+ }
+
+ @Override
+ public Docker.StartContainerCommand withMemoryInMb(long megaBytes) {
+ memoryInB = Optional.of(megaBytes * 1024 * 1024);
+ return this;
+ }
+
+ @Override
+ public Docker.StartContainerCommand withNetworkMode(String mode) {
+ networkMode = Optional.of(mode);
+ return this;
+ }
+
+ @Override
+ public Docker.StartContainerCommand withIpv6Address(String address) {
+ ipv6Address = Optional.of(address);
+ return this;
+ }
+
+ @Override
+ public void start() {
+ CreateContainerCmd command = createCreateContainerCmd();
+
+ try {
+ CreateContainerResponse response = command.exec();
+ docker.startContainerCmd(response.getId()).exec();
+ } catch (DockerException e) {
+ throw new RuntimeException("Failed to start container " + containerName.asString(), e);
+ }
+ }
+
+ private CreateContainerCmd createCreateContainerCmd() {
+ List<Bind> volumeBinds = volumeBindSpecs.stream().map(Bind::parse).collect(Collectors.toList());
+
+ CreateContainerCmd containerCmd = docker
+ .createContainerCmd(dockerImage.asString())
+ .withName(containerName.asString())
+ .withHostName(hostName.s())
+ .withLabels(labels)
+ .withEnv(environmentAssignments)
+ .withBinds(volumeBinds);
+
+ if (memoryInB.isPresent()) containerCmd = containerCmd.withMemory(memoryInB.get());
+ if (networkMode.isPresent()) containerCmd = containerCmd.withNetworkMode(networkMode.get());
+ if (ipv6Address.isPresent()) containerCmd = containerCmd.withIpv6Address(ipv6Address.get());
+
+ return containerCmd;
+ }
+
+ /** Maps ("--env", {"A", "B", "C"}) to "--env A --env B --env C ". */
+ private String toRepeatedOption(String option, List<String> optionValues) {
+ StringBuilder builder = new StringBuilder();
+ optionValues.stream().forEach(optionValue -> builder.append(option + " " + optionValue + " "));
+ return builder.toString();
+ }
+
+ private String toOptionalOption(String option, Optional<?> value) {
+ return value.isPresent() ? option + " " + value.get() + " " : "";
+ }
+
+ /** 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());
+
+ return "--name " + containerName + " "
+ + "--hostname " + hostName + " "
+ + toRepeatedOption("--label", labelList)
+ + toRepeatedOption("--env", environmentAssignments)
+ + toRepeatedOption("--volume", volumeBindSpecs)
+ + toOptionalOption("--memory", memoryInB)
+ + toOptionalOption("--net", networkMode)
+ + toOptionalOption("--ip6", ipv6Address)
+ + dockerImage;
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/VespaSSLConfig.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/VespaSSLConfig.java
index d2df5f03c57..84b528f6750 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/VespaSSLConfig.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/VespaSSLConfig.java
@@ -1,8 +1,7 @@
-package com.yahoo.vespa.hosted.node.admin.util;
+package com.yahoo.vespa.hosted.dockerapi;
import com.github.dockerjava.api.exception.DockerClientException;
import com.github.dockerjava.core.SSLConfig;
-import com.yahoo.nodeadmin.docker.DockerConfig;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/package-info.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/package-info.java
index 9ee1fe58916..08a10f3e70d 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/package-info.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/package-info.java
@@ -1,5 +1,5 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/node-admin/src/main/resources/configdefinitions/docker.def b/docker-api/src/main/resources/configdefinitions/docker.def
index c0b2e6131c4..c0173d1530b 100644
--- a/node-admin/src/main/resources/configdefinitions/docker.def
+++ b/docker-api/src/main/resources/configdefinitions/docker.def
@@ -1,5 +1,5 @@
# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=nodeadmin.docker
+namespace=vespa.hosted.dockerapi
caCertPath string
clientCertPath string
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/ContainerNameTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerNameTest.java
index bf157779c43..0522f14a06b 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/ContainerNameTest.java
+++ b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ContainerNameTest.java
@@ -1,9 +1,8 @@
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import org.junit.Test;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
/**
* @author valerijf
@@ -13,14 +12,14 @@ public class ContainerNameTest {
public void testAlphanumericalContainerName() {
String name = "container123";
ContainerName containerName = new ContainerName(name);
- assertThat(containerName.asString(), is(name));
+ assertEquals(containerName.asString(), name);
}
@Test
public void testAlphanumericalWithDashContainerName() {
String name = "container-123";
ContainerName containerName = new ContainerName(name);
- assertThat(containerName.asString(), is(name));
+ assertEquals(containerName.asString(), name);
}
@Test(expected=IllegalArgumentException.class)
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImplTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImplTest.java
index fcae057d42d..d400147c169 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerImplTest.java
+++ b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerImplTest.java
@@ -1,5 +1,5 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -12,10 +12,8 @@ import com.github.dockerjava.api.command.InspectExecCmd;
import com.github.dockerjava.api.command.InspectExecResponse;
import com.github.dockerjava.api.command.ListContainersCmd;
import com.github.dockerjava.api.command.ListImagesCmd;
-import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.core.command.ExecStartResultCallback;
-import com.yahoo.vespa.defaults.Defaults;
import org.junit.Test;
import org.mockito.Matchers;
@@ -24,65 +22,20 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
-import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* @author tonytv
*/
public class DockerImplTest {
@Test
- public void data_directories_are_mounted_in_from_the_host() {
- List<Bind> binds = DockerImpl.applicationStorageToMount(new ContainerName("my-container"));
-
- String dataDirectory = Defaults.getDefaults().vespaHome() + "logs";
- String directoryOnHost = "/home/docker/container-storage/my-container" + dataDirectory;
- assertThat(binds, hasItem(Bind.parse(directoryOnHost + ":" + dataDirectory)));
- }
-
- @Test
- public void vespaVersionIsParsed() {
- assertThat(DockerImpl.parseVespaVersion("5.119.53"), is(Optional.of("5.119.53")));
- }
-
- @Test
- public void vespaVersionIsParsedWithSpacesAndNewlines() {
- assertThat(DockerImpl.parseVespaVersion("5.119.53\n"), is(Optional.of("5.119.53")));
- assertThat(DockerImpl.parseVespaVersion(" 5.119.53 \n"), is(Optional.of("5.119.53")));
- assertThat(DockerImpl.parseVespaVersion("\n 5.119.53 \n"), is(Optional.of("5.119.53")));
- }
-
- @Test
- public void vespaVersionIsParsedWithIrregularVersionScheme() {
- assertThat(DockerImpl.parseVespaVersion("7.2"), is(Optional.of("7.2")));
- assertThat(DockerImpl.parseVespaVersion("8.0-beta"), is(Optional.of("8.0-beta")));
- assertThat(DockerImpl.parseVespaVersion("foo"), is(Optional.of("foo")));
- assertThat(DockerImpl.parseVespaVersion("119"), is(Optional.of("119")));
- }
-
- @Test
- public void vespaVersionIsNotParsedFromNull() {
- assertThat(DockerImpl.parseVespaVersion(null), is(Optional.empty()));
- }
-
- @Test
- public void vespaVersionIsNotParsedFromEmptyString() {
- assertThat(DockerImpl.parseVespaVersion(""), is(Optional.empty()));
- }
-
- @Test
- public void vespaVersionIsNotParsedFromUnexpectedContent() {
- assertThat(DockerImpl.parseVespaVersion("No such command 'vespanodectl'"), is(Optional.empty()));
- }
-
- @Test
public void testExecuteCompletes() throws Exception {
final String containerId = "container-id";
final String[] command = new String[] {"/bin/ls", "-l"};
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerTest.java
index bccd9b79070..50c8492d0b4 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/DockerTest.java
+++ b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/DockerTest.java
@@ -1,8 +1,7 @@
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import com.github.dockerjava.api.model.Network;
import com.github.dockerjava.core.command.BuildImageResultCallback;
-import com.yahoo.nodeadmin.docker.DockerConfig;
import com.yahoo.vespa.applicationmodel.HostName;
import org.junit.After;
import org.junit.Before;
@@ -56,7 +55,7 @@ public class DockerTest {
docker.pullImageAsync(dockerImage).get();
// Translate the human readable ID to sha256-hash ID that is returned by getUnusedDockerImages()
- DockerImage targetImage = new DockerImage(docker.docker.inspectImageCmd(dockerImage.asString()).exec().getId());
+ DockerImage targetImage = new DockerImage(docker.dockerClient.inspectImageCmd(dockerImage.asString()).exec().getId());
assertTrue("Image: " + dockerImage + " should be unused", docker.getUnusedDockerImages().contains(targetImage));
// Remove the image
@@ -74,8 +73,9 @@ public class DockerTest {
InetAddress inetAddress1 = Inet6Address.getByName("fe80::10");
InetAddress inetAddress2 = Inet6Address.getByName("fe80::11");
- docker.startContainer(dockerImage, hostName1, containerName1, inetAddress1, 0, 0, 0);
- docker.startContainer(dockerImage, hostName2, containerName2, inetAddress2, 0, 0, 0);
+ // TODO: Use new Docker API.
+ // docker.startContainer(dockerImage, hostName1, containerName1, inetAddress1, 0, 0, 0);
+ // docker.startContainer(dockerImage, hostName2, containerName2, inetAddress2, 0, 0, 0);
try {
testReachabilityFromHost(containerName1, inetAddress1);
@@ -156,20 +156,22 @@ public class DockerTest {
public void setup() throws IOException, ExecutionException, InterruptedException {
// Build the image locally
File dockerFilePath = new File("src/test/resources/simple-ipv6-server");
- docker.docker
+ docker.dockerClient
.buildImageCmd(dockerFilePath)
.withTag(dockerImage.asString()).exec(new BuildImageResultCallback()).awaitCompletion();
// Create a temporary network
Network.Ipam ipam = new Network.Ipam().withConfig(new Network.Ipam.Config()
.withSubnet("fe80::1/16").withGateway(getLocalIPv6Address()));
- docker.docker.createNetworkCmd().withDriver("bridge").withName(DockerImpl.DOCKER_CUSTOM_IP6_NETWORK_NAME)
+ // TODO: This needs to match the network name in DockerOperations!?
+ docker.dockerClient.createNetworkCmd().withDriver("bridge").withName("habla")
.withIpam(ipam).exec();
}
@After
public void shutdown() {
// Remove the network we created earlier
- docker.docker.removeNetworkCmd(DockerImpl.DOCKER_CUSTOM_IP6_NETWORK_NAME).exec();
+ // TODO: This needs to match the network name in DockerOperations!?
+ docker.dockerClient.removeNetworkCmd("habla").exec();
}
}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/ProcessResultTest.java b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ProcessResultTest.java
index 60ac91025ee..b28d3f3c29a 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/docker/ProcessResultTest.java
+++ b/docker-api/src/test/java/com/yahoo/vespa/hosted/dockerapi/ProcessResultTest.java
@@ -1,10 +1,8 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.docker;
+package com.yahoo.vespa.hosted.dockerapi;
import org.junit.Test;
-import java.io.IOException;
-
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
diff --git a/node-admin/pom.xml b/node-admin/pom.xml
index 319aceea23c..2658826616c 100644
--- a/node-admin/pom.xml
+++ b/node-admin/pom.xml
@@ -52,58 +52,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
- <dependency> <!-- Remove once code uses docker-api bundle -->
- <groupId>com.github.docker-java</groupId>
- <artifactId>docker-java</artifactId>
- <version>3.0.2</version>
- <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>
- </exclusions>
- </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
@@ -171,6 +119,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgs>
<arg>-Xlint:all</arg>
<arg>-Werror</arg>
diff --git a/node-admin/src/main/application/services.xml b/node-admin/src/main/application/services.xml
index fa7307fc5eb..39ca1e23d9a 100644
--- a/node-admin/src/main/application/services.xml
+++ b/node-admin/src/main/application/services.xml
@@ -6,7 +6,7 @@
<binding>http://*/rest/*</binding>
</handler>
<component id="node-admin" class="com.yahoo.vespa.hosted.node.admin.provider.ComponentsProviderImpl" bundle="node-admin"/>
- <component id="docker" class="com.yahoo.vespa.hosted.node.admin.docker.DockerImpl" bundle="node-admin"/>
+ <component id="docker" class="com.yahoo.vespa.hosted.dockerapi.DockerImpl" bundle="node-admin"/>
<config name='nodeadmin.docker.docker'>
<caCertPath>/host/docker/certs/ca_cert.pem</caCertPath>
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/ContainerNodeSpec.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/ContainerNodeSpec.java
index b0c10d8d803..7f7514a44fd 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/ContainerNodeSpec.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/ContainerNodeSpec.java
@@ -2,8 +2,8 @@
package com.yahoo.vespa.hosted.node.admin;
import com.yahoo.vespa.applicationmodel.HostName;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeState;
import java.util.Objects;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceScheduler.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceScheduler.java
index f30fc04699c..ac45e3d66c4 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceScheduler.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceScheduler.java
@@ -1,6 +1,6 @@
package com.yahoo.vespa.hosted.node.admin.maintenance;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import java.io.IOException;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceSchedulerImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceSchedulerImpl.java
index 0eb5458a515..6374a23a3ba 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceSchedulerImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/MaintenanceSchedulerImpl.java
@@ -1,7 +1,7 @@
package com.yahoo.vespa.hosted.node.admin.maintenance;
import com.yahoo.io.IOUtils;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import com.yahoo.vespa.hosted.node.admin.util.PrefixLogger;
import com.yahoo.vespa.hosted.node.maintenance.DeleteOldAppData;
import com.yahoo.vespa.hosted.node.maintenance.Maintainer;
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 909608d9edf..684e74beafb 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
@@ -4,9 +4,9 @@ package com.yahoo.vespa.hosted.node.admin.nodeadmin;
import com.yahoo.collections.Pair;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.Container;
-import com.yahoo.vespa.hosted.node.admin.docker.Docker;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.Container;
+import com.yahoo.vespa.hosted.dockerapi.Docker;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.maintenance.MaintenanceScheduler;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.util.PrefixLogger;
@@ -129,6 +129,7 @@ public class NodeAdminImpl implements NodeAdmin {
// Delete images that have been eligible for some time.
firstTimeEligibleForGC.forEach((dockerImage, timestamp) -> {
if (currentTime - timestamp > MIN_AGE_IMAGE_GC_MILLIS) {
+ logger.info("Deleting Docker image " + dockerImage);
docker.deleteImage(dockerImage);
}
});
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperations.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperations.java
index 917bd44585a..71f0a7ee588 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperations.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperations.java
@@ -1,54 +1,106 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.nodeagent;
+import com.google.common.base.Joiner;
+import com.google.common.io.CharStreams;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.defaults.Defaults;
+import com.yahoo.vespa.hosted.dockerapi.Container;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.Docker;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.Container;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.Docker;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
-import com.yahoo.vespa.hosted.node.admin.docker.ProcessResult;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeState;
import com.yahoo.vespa.hosted.node.admin.orchestrator.Orchestrator;
import com.yahoo.vespa.hosted.node.admin.orchestrator.OrchestratorException;
+import com.yahoo.vespa.hosted.node.admin.util.Environment;
import com.yahoo.vespa.hosted.node.admin.util.PrefixLogger;
+import com.yahoo.vespa.hosted.node.maintenance.Maintainer;
+import java.io.InputStreamReader;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static com.yahoo.vespa.defaults.Defaults.getDefaults;
/**
* Class that wraps the Docker class and have some tools related to running programs in docker.
* @author dybis
*/
-public class DockerOperations {
+public class DockerOperations implements DockerOperationsInterface {
static final String NODE_PROGRAM = Defaults.getDefaults().vespaHome() + "bin/vespa-nodectl";
public static final String[] GET_VESPA_VERSION_COMMAND = new String[]{NODE_PROGRAM, "vespa-version"};
private static final String[] RESUME_NODE_COMMAND = new String[] {NODE_PROGRAM, "resume"};
private static final String[] SUSPEND_NODE_COMMAND = new String[] {NODE_PROGRAM, "suspend"};
+ private static final Pattern VESPA_VERSION_PATTERN = Pattern.compile("^(\\S*)$", Pattern.MULTILINE);
+
+ private static final List<String> DIRECTORIES_TO_MOUNT = Arrays.asList(
+ getDefaults().underVespaHome("logs"),
+ getDefaults().underVespaHome("var/cache"),
+ getDefaults().underVespaHome("var/crash"),
+ getDefaults().underVespaHome("var/db/jdisc"),
+ getDefaults().underVespaHome("var/db/vespa"),
+ getDefaults().underVespaHome("var/jdisc_container"),
+ getDefaults().underVespaHome("var/jdisc_core"),
+ getDefaults().underVespaHome("var/logstash-forwarder"),
+ getDefaults().underVespaHome("var/maven"),
+ getDefaults().underVespaHome("var/scoreboards"),
+ getDefaults().underVespaHome("var/service"),
+ getDefaults().underVespaHome("var/share"),
+ getDefaults().underVespaHome("var/spool"),
+ getDefaults().underVespaHome("var/vespa"),
+ getDefaults().underVespaHome("var/yca"),
+ getDefaults().underVespaHome("var/ycore++"),
+ getDefaults().underVespaHome("var/zookeeper"));
+
private final Docker docker;
public DockerOperations(Docker docker) {
this.docker = docker;
}
- // Returns null on problems
+ @Override
public String getVespaVersionOrNull(ContainerName containerName) {
- try {
- return docker.getVespaVersion(containerName);
- } catch (RuntimeException e) {
- PrefixLogger logger = PrefixLogger.getNodeAgentLogger(DockerOperations.class, containerName);
- logger.warning("Ignoring failure", e);
+ PrefixLogger logger = PrefixLogger.getNodeAgentLogger(DockerOperations.class, containerName);
+
+ ProcessResult result = docker.executeInContainer(containerName, DockerOperations.GET_VESPA_VERSION_COMMAND);
+ if (!result.isSuccess()) {
+ logger.warning("Container " + containerName.asString() + ": Command "
+ + Arrays.toString(DockerOperations.GET_VESPA_VERSION_COMMAND) + " failed: " + result);
+ return null;
+ }
+ Optional<String> vespaVersion = parseVespaVersion(result.getOutput());
+ if (vespaVersion.isPresent()) {
+ return vespaVersion.get();
+ } else {
+ logger.warning("Container " + containerName.asString() + ": Failed to parse vespa version from "
+ + result.getOutput());
return null;
}
}
+ // Returns empty if vespa version cannot be parsed.
+ static Optional<String> parseVespaVersion(final String rawVespaVersion) {
+ if (rawVespaVersion == null) return Optional.empty();
+
+ final Matcher matcher = VESPA_VERSION_PATTERN.matcher(rawVespaVersion.trim());
+ return matcher.find() ? Optional.of(matcher.group(1)) : Optional.empty();
+ }
+
// Returns true if started
+ @Override
public boolean startContainerIfNeeded(final ContainerNodeSpec nodeSpec) {
final Optional<Container> existingContainer = docker.getContainer(nodeSpec.hostname);
if (!existingContainer.isPresent()) {
@@ -60,10 +112,12 @@ public class DockerOperations {
}
// Returns true if scheduling download
+ @Override
public boolean shouldScheduleDownloadOfImage(final DockerImage dockerImage) {
return !docker.imageIsDownloaded(dockerImage);
}
+ @Override
public boolean removeContainerIfNeeded(ContainerNodeSpec nodeSpec, HostName hostname, Orchestrator orchestrator)
throws Exception {
Optional<Container> existingContainer = docker.getContainer(hostname);
@@ -144,26 +198,111 @@ public class DockerOperations {
}
}
- void startContainer(final ContainerNodeSpec nodeSpec) {
+ @Override
+ public void startContainer(final ContainerNodeSpec nodeSpec) {
PrefixLogger logger = PrefixLogger.getNodeAgentLogger(DockerOperations.class, nodeSpec.containerName);
logger.info("Starting container " + nodeSpec.containerName);
- InetAddress nodeInetAddress = null;
try {
- nodeInetAddress = InetAddress.getByName(nodeSpec.hostname.s());
+ InetAddress nodeInetAddress = InetAddress.getByName(nodeSpec.hostname.s());
+ String nodeIpAddress = nodeInetAddress.getHostAddress();
+ final boolean isRunningIPv6 = nodeInetAddress instanceof Inet6Address;
+
+ String configServers = Environment.getConfigServerHosts().stream().map(HostName::toString).collect(Collectors.joining(","));
+ Docker.StartContainerCommand command = docker.createStartContainerCommand(
+ nodeSpec.wantedDockerImage.get(),
+ nodeSpec.containerName,
+ nodeSpec.hostname);
+ command.withEnvironment("CONFIG_SERVER_ADDRESS", configServers);
+
+ command.withVolume("/etc/hosts", "/etc/hosts");
+ for (String pathInNode : DIRECTORIES_TO_MOUNT) {
+ String pathInHost = Maintainer.pathInHostFromPathInNode(nodeSpec.containerName, pathInNode).toString();
+ command = command.withVolume(pathInHost, pathInNode);
+ }
+
+ // TODO: Enforce disk constraints
+ // TODO: Consider if CPU shares or quoata should be set. For now we are just assuming they are
+ // nicely controlled by docker.
+ if (nodeSpec.minMainMemoryAvailableGb.isPresent()) {
+ long minMainMemoryAvailableMb = (long) (nodeSpec.minMainMemoryAvailableGb.get() * 1024);
+ if (minMainMemoryAvailableMb > 0) {
+ command.withMemoryInMb(minMainMemoryAvailableMb);
+ }
+ }
+
+ //If container's IP address is IPv6, use our custom network mode and let docker handle networking.
+ //If container's IP address is IPv4, set up networking manually as before. In the future docker should
+ //set up the IPv4 network as well.
+ if (isRunningIPv6) {
+ // TODO: What's "habla"? Can we change to anything here, or it seems "none" is reserved.
+ command.withNetworkMode("habla").withIpv6Address(nodeIpAddress);
+ } else {
+ command.withNetworkMode("none");
+ }
+
+ logger.info("Starting new container with args: " + command);
+ command.start();
+
+ if (!isRunningIPv6) {
+ setupContainerIPv4Networking(nodeSpec.containerName, nodeSpec.hostname, nodeIpAddress);
+ }
} catch (UnknownHostException e) {
- logger.error("Failed to get IP address to " + nodeSpec.hostname);
+ throw new RuntimeException("Failed to start container " + nodeSpec.containerName.asString(), e);
+ }
+ }
+
+ private void setupContainerIPv4Networking(ContainerName containerName,
+ HostName hostName,
+ String ipAddress) {
+ PrefixLogger logger = PrefixLogger.getNodeAgentLogger(DockerOperations.class, containerName);
+
+ Docker.ContainerInfo containerInfo = docker.inspectContainer(containerName);
+ Optional<Integer> containerPid = containerInfo.getPid();
+ if (!containerPid.isPresent()) {
+ throw new RuntimeException("Container " + containerName + " for host "
+ + hostName + " isn't running (pid not found)");
+ }
+
+ final List<String> command = new LinkedList<>();
+ command.add("sudo");
+ command.add(getDefaults().underVespaHome("libexec/vespa/node-admin/configure-container-networking.py"));
+
+ Environment.NetworkType networkType = Environment.networkType();
+ if (networkType != Environment.NetworkType.normal) {
+ command.add("--" + networkType);
+ }
+ command.add(containerPid.get().toString());
+ command.add(ipAddress);
+
+ for (int retry = 0; retry < 30; ++retry) {
+ try {
+ runCommand(command);
+ logger.info("Done setting up network");
+ return;
+ } catch (Exception e) {
+ final int sleepSecs = 3;
+ logger.warning("Failed to configure network with command " + command
+ + ", will retry in " + sleepSecs + " seconds", e);
+ try {
+ Thread.sleep(sleepSecs * 1000);
+ } catch (InterruptedException e1) {
+ logger.warning("Sleep interrupted", e1);
+ }
+ }
}
+ }
- // TODO: Properly handle absent min* values
- docker.startContainer(
- nodeSpec.wantedDockerImage.get(),
- nodeSpec.hostname,
- nodeSpec.containerName,
- nodeInetAddress,
- nodeSpec.minCpuCores.get(),
- nodeSpec.minDiskAvailableGb.get(),
- nodeSpec.minMainMemoryAvailableGb.get());
+ private void runCommand(final List<String> command) throws Exception {
+ ProcessBuilder builder = new ProcessBuilder(command);
+ builder.redirectErrorStream(true);
+ Process process = builder.start();
+
+ String output = CharStreams.toString(new InputStreamReader(process.getInputStream()));
+ int resultCode = process.waitFor();
+ if (resultCode != 0) {
+ throw new Exception("Command " + Joiner.on(' ').join(command) + " failed: " + output);
+ }
}
void scheduleDownloadOfImage(final ContainerNodeSpec nodeSpec, Runnable callback) {
@@ -225,6 +364,7 @@ public class DockerOperations {
}
+ @Override
public void executeResume(ContainerName containerName) {
Optional<ProcessResult> result = executeOptionalProgram(containerName, RESUME_NODE_COMMAND);
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsInterface.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsInterface.java
new file mode 100644
index 00000000000..8606efc854f
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsInterface.java
@@ -0,0 +1,25 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.nodeagent;
+
+import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
+import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
+import com.yahoo.vespa.hosted.node.admin.orchestrator.Orchestrator;
+
+public interface DockerOperationsInterface {
+ String getVespaVersionOrNull(ContainerName containerName);
+
+ // Returns true if started
+ boolean startContainerIfNeeded(ContainerNodeSpec nodeSpec);
+
+ // Returns true if scheduling download
+ boolean shouldScheduleDownloadOfImage(DockerImage dockerImage);
+
+ boolean removeContainerIfNeeded(ContainerNodeSpec nodeSpec, HostName hostname, Orchestrator orchestrator)
+ throws Exception;
+
+ void startContainer(ContainerNodeSpec nodeSpec);
+
+ void executeResume(ContainerName containerName);
+}
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 d88831f2922..76aa629c76c 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
@@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.node.admin.nodeagent;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.maintenance.MaintenanceScheduler;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepositoryImpl;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAttributes.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAttributes.java
index b848b8c8e6a..3656b8b59d1 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAttributes.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAttributes.java
@@ -1,7 +1,7 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.nodeagent;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import java.util.Objects;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java
index 8829c3d4487..164ec9379f7 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java
@@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.node.admin.noderepository;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import java.io.IOException;
import java.util.List;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java
index b46dda2f7a1..e1059e65cdd 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java
@@ -3,8 +3,8 @@ package com.yahoo.vespa.hosted.node.admin.noderepository;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.noderepository.bindings.GetNodesResponse;
import com.yahoo.vespa.hosted.node.admin.noderepository.bindings.NodeReadyResponse;
import com.yahoo.vespa.hosted.node.admin.noderepository.bindings.UpdateNodeAttributesRequestBody;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProviderImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProviderImpl.java
index 3a21c7d4f0b..bf2f12e0884 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProviderImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProviderImpl.java
@@ -9,7 +9,7 @@ import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl;
-import com.yahoo.vespa.hosted.node.admin.docker.Docker;
+import com.yahoo.vespa.hosted.dockerapi.Docker;
import com.yahoo.vespa.hosted.node.admin.nodeagent.DockerOperations;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepositoryImpl;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/PrefixLogger.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/PrefixLogger.java
index 42b08b6c96b..12d581c0583 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/PrefixLogger.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/PrefixLogger.java
@@ -1,7 +1,7 @@
package com.yahoo.vespa.hosted.node.admin.util;
import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import java.util.logging.Level;
import java.util.logging.Logger;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/maintenance/Maintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/maintenance/Maintainer.java
index 488de969d3c..e57777f37e0 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/maintenance/Maintainer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/maintenance/Maintainer.java
@@ -1,6 +1,6 @@
package com.yahoo.vespa.hosted.node.maintenance;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import io.airlift.airline.Cli;
import io.airlift.airline.Command;
import io.airlift.airline.Help;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ComponentsProviderWithMocks.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ComponentsProviderWithMocks.java
index 37018ff0a0d..004265f3346 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ComponentsProviderWithMocks.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ComponentsProviderWithMocks.java
@@ -7,7 +7,7 @@ import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl;
-import com.yahoo.vespa.hosted.node.admin.docker.Docker;
+import com.yahoo.vespa.hosted.dockerapi.Docker;
import com.yahoo.vespa.hosted.node.admin.nodeagent.DockerOperations;
import com.yahoo.vespa.hosted.node.admin.provider.ComponentsProvider;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerFailTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerFailTest.java
index e97f4acda87..3583054bec7 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerFailTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerFailTest.java
@@ -3,8 +3,8 @@ package com.yahoo.vespa.hosted.node.admin.integrationTests;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
@@ -73,11 +73,10 @@ public class DockerFailTest {
Thread.sleep(10);
}
- while (!DockerMock.getRequests().startsWith("startContainer with DockerImage: DockerImage { imageId=dockerImage }, " +
- "HostName: host1, ContainerName: ContainerName { name=container }, InetAddress: null, minCpuCores: 1.0, minDiskAvailableGb: 1.0, " +
- "minMainMemoryAvailableGb: 1.0\nexecuteInContainer with ContainerName: ContainerName { name=container }, " +
- "args: [/usr/bin/env, test, -x, /opt/vespa/bin/vespa-nodectl]\nexecuteInContainer with ContainerName: " +
- "ContainerName { name=container }, args: [/opt/vespa/bin/vespa-nodectl, resume]\n")) {
+ while (!DockerMock.getRequests().startsWith(
+ "createStartContainerCommand with DockerImage: DockerImage { imageId=dockerImage }, HostName: hostName, ContainerName: ContainerName { name=container }\n" +
+ "executeInContainer with ContainerName: ContainerName { name=container }, args: [/usr/bin/env, test, -x, /opt/vespa/bin/vespa-nodectl]\n" +
+ "executeInContainer with ContainerName: ContainerName { name=container }, args: [/opt/vespa/bin/vespa-nodectl, resume]\n")) {
Thread.sleep(10);
}
}
@@ -92,8 +91,7 @@ public class DockerFailTest {
public void dockerFailTest() throws InterruptedException {
dockerMock.deleteContainer(initialContainerNodeSpec.containerName);
- String goal = "startContainer with DockerImage: DockerImage { imageId=dockerImage }, HostName: host1, " +
- "ContainerName: ContainerName { name=container }, InetAddress: null, minCpuCores: 1.0, minDiskAvailableGb: 1.0, minMainMemoryAvailableGb: 1.0\n" +
+ String goal = "createStartContainerCommand with DockerImage: DockerImage { imageId=dockerImage }, HostName: hostName, ContainerName: ContainerName { name=container }\n" +
"executeInContainer with ContainerName: ContainerName { name=container }, args: [/usr/bin/env, test, -x, /opt/vespa/bin/vespa-nodectl]\n" +
"executeInContainer with ContainerName: ContainerName { name=container }, args: [/opt/vespa/bin/vespa-nodectl, resume]\n" +
"deleteContainer with ContainerName: ContainerName { name=container }\n" +
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java
index 29af705d44a..aa1c53fd59c 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java
@@ -2,13 +2,12 @@
package com.yahoo.vespa.hosted.node.admin.integrationTests;
import com.yahoo.vespa.applicationmodel.HostName;
-import com.yahoo.vespa.hosted.node.admin.docker.Container;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.Docker;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
-import com.yahoo.vespa.hosted.node.admin.docker.ProcessResult;
+import com.yahoo.vespa.hosted.dockerapi.Container;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.Docker;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
-import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -42,15 +41,28 @@ public class DockerMock implements Docker {
}
@Override
- public void startContainer(DockerImage dockerImage, HostName hostName, ContainerName containerName, InetAddress inetAddress,
- double minCpuCores, double minDiskAvailableGb, double minMainMemoryAvailableGb) {
+ public StartContainerCommand createStartContainerCommand(
+ DockerImage dockerImage,
+ ContainerName containerName,
+ HostName hostName) {
synchronized (monitor) {
- requests.append("startContainer with DockerImage: ").append(dockerImage).append(", HostName: ").append(hostName)
- .append(", ContainerName: ").append(containerName).append(", InetAddress: ").append(inetAddress)
- .append(", minCpuCores: ").append(minCpuCores).append(", minDiskAvailableGb: ").append(minDiskAvailableGb)
- .append(", minMainMemoryAvailableGb: ").append(minMainMemoryAvailableGb).append("\n");
+ requests.append("createStartContainerCommand with DockerImage: ")
+ .append(dockerImage).append(", HostName: ").append(hostName)
+ .append(", ContainerName: ").append(containerName).append("\n");
containers.add(new Container(hostName, dockerImage, containerName, true));
}
+
+ return new StartContainerCommandMock();
+ }
+
+ @Override
+ public ContainerInfo inspectContainer(ContainerName containerName) {
+ return new ContainerInfo() {
+ @Override
+ public Optional<Integer> getPid() {
+ return Optional.of(2);
+ }
+ };
}
@Override
@@ -102,11 +114,6 @@ public class DockerMock implements Docker {
}
@Override
- public String getVespaVersion(ContainerName containerName) {
- return null;
- }
-
- @Override
public void deleteImage(DockerImage dockerImage) {
}
@@ -136,5 +143,41 @@ public class DockerMock implements Docker {
requests = new StringBuilder();
}
}
-}
+ public static class StartContainerCommandMock implements StartContainerCommand {
+ @Override
+ public StartContainerCommand withLabel(String name, String value) {
+ return this;
+ }
+
+ @Override
+ public StartContainerCommand withEnvironment(String name, String value) {
+ return this;
+ }
+
+ @Override
+ public StartContainerCommand withVolume(String path, String volumePath) {
+ return this;
+ }
+
+ @Override
+ public StartContainerCommand withMemoryInMb(long megaBytes) {
+ return this;
+ }
+
+ @Override
+ public StartContainerCommand withNetworkMode(String mode) {
+ return this;
+ }
+
+ @Override
+ public StartContainerCommand withIpv6Address(String address) {
+ return this;
+ }
+
+ @Override
+ public void start() {
+
+ }
+ }
+}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MaintenanceSchedulerMock.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MaintenanceSchedulerMock.java
index a6c4ed63439..c634cd2aa05 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MaintenanceSchedulerMock.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MaintenanceSchedulerMock.java
@@ -1,6 +1,6 @@
package com.yahoo.vespa.hosted.node.admin.integrationTests;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import com.yahoo.vespa.hosted.node.admin.maintenance.MaintenanceScheduler;
import java.io.IOException;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MultiDockerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MultiDockerTest.java
index da658e462a9..1f776a4b911 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MultiDockerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/MultiDockerTest.java
@@ -4,8 +4,8 @@ package com.yahoo.vespa.hosted.node.admin.integrationTests;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java
index ecdcf5e21e6..0e493cf9219 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java
@@ -3,8 +3,8 @@ package com.yahoo.vespa.hosted.node.admin.integrationTests;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeState;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeStateTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeStateTest.java
index 7dc0a449913..af1b447b1d2 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeStateTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeStateTest.java
@@ -3,8 +3,8 @@ package com.yahoo.vespa.hosted.node.admin.integrationTests;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ResumeTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ResumeTest.java
index 229f10bfe64..c7489fc28aa 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ResumeTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ResumeTest.java
@@ -8,8 +8,8 @@ import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.nodeagent.DockerOperations;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeState;
import org.junit.After;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java
index 312db45c790..2de470f403c 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
@@ -4,10 +4,10 @@ package com.yahoo.vespa.hosted.node.admin.nodeadmin;
import com.yahoo.collections.Pair;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.Container;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.Docker;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.Container;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.Docker;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.maintenance.MaintenanceScheduler;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java
index 9c9cdf7b5f7..5b3ce94614c 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java
@@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.node.admin.nodeadmin;
import com.yahoo.prelude.semantics.RuleBaseException;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import com.yahoo.vespa.hosted.node.admin.integrationTests.OrchestratorMock;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeState;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsTest.java
index c9cd8963617..8f3850dc569 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/DockerOperationsTest.java
@@ -1,20 +1,25 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.nodeagent;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.Docker;
-import com.yahoo.vespa.hosted.node.admin.docker.ProcessResult;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.Docker;
+import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
+import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.mockito.InOrder;
import java.util.Optional;
import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyVararg;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class DockerOperationsTest {
private final Docker docker = mock(Docker.class);
@@ -80,4 +85,39 @@ public class DockerOperationsTest {
assertThat(result.isPresent(), is(true));
assertThat(result.get(), is(actualResult));
}
+
+ @Test
+ public void vespaVersionIsParsed() {
+ assertThat(DockerOperations.parseVespaVersion("5.119.53"), CoreMatchers.is(Optional.of("5.119.53")));
+ }
+
+ @Test
+ public void vespaVersionIsParsedWithSpacesAndNewlines() {
+ assertThat(DockerOperations.parseVespaVersion("5.119.53\n"), CoreMatchers.is(Optional.of("5.119.53")));
+ assertThat(DockerOperations.parseVespaVersion(" 5.119.53 \n"), CoreMatchers.is(Optional.of("5.119.53")));
+ assertThat(DockerOperations.parseVespaVersion("\n 5.119.53 \n"), CoreMatchers.is(Optional.of("5.119.53")));
+ }
+
+ @Test
+ public void vespaVersionIsParsedWithIrregularVersionScheme() {
+ assertThat(DockerOperations.parseVespaVersion("7.2"), CoreMatchers.is(Optional.of("7.2")));
+ assertThat(DockerOperations.parseVespaVersion("8.0-beta"), CoreMatchers.is(Optional.of("8.0-beta")));
+ assertThat(DockerOperations.parseVespaVersion("foo"), CoreMatchers.is(Optional.of("foo")));
+ assertThat(DockerOperations.parseVespaVersion("119"), CoreMatchers.is(Optional.of("119")));
+ }
+
+ @Test
+ public void vespaVersionIsNotParsedFromNull() {
+ assertThat(DockerOperations.parseVespaVersion(null), CoreMatchers.is(Optional.empty()));
+ }
+
+ @Test
+ public void vespaVersionIsNotParsedFromEmptyString() {
+ assertThat(DockerOperations.parseVespaVersion(""), CoreMatchers.is(Optional.empty()));
+ }
+
+ @Test
+ public void vespaVersionIsNotParsedFromUnexpectedContent() {
+ assertThat(DockerOperations.parseVespaVersion("No such command 'vespanodectl'"), CoreMatchers.is(Optional.empty()));
+ }
} \ No newline at end of file
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 c50d25d5b67..d2d71c7b0be 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
@@ -2,12 +2,13 @@
package com.yahoo.vespa.hosted.node.admin.nodeagent;
import com.yahoo.vespa.applicationmodel.HostName;
+import com.yahoo.vespa.hosted.dockerapi.Container;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.Docker;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.Container;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.Docker;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
-import com.yahoo.vespa.hosted.node.admin.docker.ProcessResult;
+import com.yahoo.vespa.hosted.node.admin.integrationTests.DockerMock;
import com.yahoo.vespa.hosted.node.admin.maintenance.MaintenanceScheduler;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeState;
@@ -22,7 +23,6 @@ import java.util.concurrent.CompletableFuture;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyVararg;
@@ -76,7 +76,8 @@ public class NodeAgentImplTest {
when(docker.imageIsDownloaded(dockerImage)).thenReturn(true);
when(docker.executeInContainer(eq(containerName), anyVararg())).thenReturn(NODE_PROGRAM_DOESNT_EXIST);
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
when(nodeRepository.getContainerNodeSpec(hostName)).thenReturn(Optional.of(nodeSpec));
when(docker.getContainer(hostName)).thenReturn(Optional.of(existingContainer));
@@ -85,14 +86,10 @@ public class NodeAgentImplTest {
verify(orchestrator, never()).suspend(any(HostName.class));
verify(docker, never()).stopContainer(any(ContainerName.class));
verify(docker, never()).deleteContainer(any(ContainerName.class));
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(docker, times(1)).executeInContainer(any(), anyVararg());
final InOrder inOrder = inOrder(orchestrator, nodeRepository);
inOrder.verify(nodeRepository).updateNodeAttributes(hostName, restartGeneration, dockerImage, vespaVersion);
@@ -120,12 +117,11 @@ public class NodeAgentImplTest {
final String vespaVersion = "7.8.9";
when(nodeRepository.getContainerNodeSpec(hostName)).thenReturn(Optional.of(nodeSpec));
when(docker.getContainer(hostName)).thenReturn(Optional.of(existingContainer)).thenReturn(Optional.empty());
- ;
-
when(docker.imageIsDownloaded(dockerImage)).thenReturn(true);
when(docker.executeInContainer(eq(containerName), anyVararg())).thenReturn(NODE_PROGRAM_DOESNT_EXIST);
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
when(orchestrator.suspend(any(HostName.class))).thenReturn(true);
nodeAgent.tick();
@@ -135,14 +131,10 @@ public class NodeAgentImplTest {
inOrder.verify(docker, times(1)).executeInContainer(any(), anyVararg());
inOrder.verify(docker).stopContainer(containerName);
inOrder.verify(docker).deleteContainer(containerName);
- inOrder.verify(docker).startContainer(
- eq(nodeSpec.wantedDockerImage.get()),
- eq(nodeSpec.hostname),
- eq(nodeSpec.containerName),
- any(InetAddress.class),
- eq(nodeSpec.minCpuCores.get()),
- eq(nodeSpec.minDiskAvailableGb.get()),
- eq(nodeSpec.minMainMemoryAvailableGb.get()));
+ inOrder.verify(docker).createStartContainerCommand(
+ nodeSpec.wantedDockerImage.get(),
+ nodeSpec.containerName,
+ nodeSpec.hostname);
inOrder.verify(docker, times(1)).executeInContainer(any(), anyVararg());
inOrder.verify(nodeRepository).updateNodeAttributes(hostName, wantedRestartGeneration, dockerImage, vespaVersion);
@@ -172,7 +164,8 @@ public class NodeAgentImplTest {
when(docker.imageIsDownloaded(wantedDockerImage)).thenReturn(true);
when(docker.executeInContainer(eq(containerName), anyVararg())).thenReturn(NODE_PROGRAM_DOESNT_EXIST);
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
when(orchestrator.suspend(any(HostName.class))).thenReturn(true);
when(docker.getContainer(hostName)).thenReturn(Optional.of(existingContainer)).thenReturn(Optional.empty());
@@ -184,14 +177,10 @@ public class NodeAgentImplTest {
inOrder.verify(orchestrator).suspend(hostName);
inOrder.verify(docker).stopContainer(containerName);
inOrder.verify(docker).deleteContainer(containerName);
- inOrder.verify(docker).startContainer(
- eq(nodeSpec.wantedDockerImage.get()),
- eq(nodeSpec.hostname),
- eq(nodeSpec.containerName),
- any(InetAddress.class),
- eq(nodeSpec.minCpuCores.get()),
- eq(nodeSpec.minDiskAvailableGb.get()),
- eq(nodeSpec.minMainMemoryAvailableGb.get()));
+ inOrder.verify(docker).createStartContainerCommand(
+ nodeSpec.wantedDockerImage.get(),
+ nodeSpec.containerName,
+ nodeSpec.hostname);
inOrder.verify(docker, times(1)).executeInContainer(any(), anyVararg());
inOrder.verify(nodeRepository).updateNodeAttributes(hostName, restartGeneration, wantedDockerImage, vespaVersion);
inOrder.verify(orchestrator).resume(hostName);
@@ -252,8 +241,13 @@ public class NodeAgentImplTest {
when(docker.imageIsDownloaded(dockerImage)).thenReturn(true);
when(docker.executeInContainer(eq(containerName), anyVararg())).thenReturn(NODE_PROGRAM_DOESNT_EXIST);
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
when(orchestrator.suspend(any(HostName.class))).thenReturn(true);
+ when(docker.createStartContainerCommand(
+ nodeSpec.wantedDockerImage.get(),
+ nodeSpec.containerName,
+ nodeSpec.hostname)).thenReturn(new DockerMock.StartContainerCommandMock());
nodeAgent.tick();
@@ -262,14 +256,10 @@ public class NodeAgentImplTest {
verify(docker, times(1)).executeInContainer(any(), anyVararg());
final InOrder inOrder = inOrder(orchestrator, docker, nodeRepository);
inOrder.verify(docker).deleteContainer(containerName);
- inOrder.verify(docker).startContainer(
- eq(nodeSpec.wantedDockerImage.get()),
- eq(nodeSpec.hostname),
- eq(nodeSpec.containerName),
- any(InetAddress.class),
- eq(nodeSpec.minCpuCores.get()),
- eq(nodeSpec.minDiskAvailableGb.get()),
- eq(nodeSpec.minMainMemoryAvailableGb.get()));
+ inOrder.verify(docker).createStartContainerCommand(
+ nodeSpec.wantedDockerImage.get(),
+ nodeSpec.containerName,
+ nodeSpec.hostname);
inOrder.verify(nodeRepository).updateNodeAttributes(hostName, restartGeneration, dockerImage, vespaVersion);
inOrder.verify(orchestrator).resume(hostName);
}
@@ -294,7 +284,8 @@ public class NodeAgentImplTest {
when(docker.imageIsDownloaded(dockerImage)).thenReturn(true);
when(docker.executeInContainer(eq(containerName), anyVararg())).thenReturn(NODE_PROGRAM_DOESNT_EXIST);
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
when(orchestrator.suspend(any(HostName.class))).thenReturn(true);
when(nodeRepository.getContainerNodeSpec(hostName)).thenReturn(Optional.of(nodeSpec));
when(docker.getContainer(hostName)).thenReturn(Optional.empty());
@@ -306,14 +297,10 @@ public class NodeAgentImplTest {
verify(docker, times(1)).executeInContainer(any(), anyVararg());
verify(orchestrator, never()).suspend(any(HostName.class));
final InOrder inOrder = inOrder(orchestrator, docker, nodeRepository);
- inOrder.verify(docker).startContainer(
- eq(nodeSpec.wantedDockerImage.get()),
- eq(nodeSpec.hostname),
- eq(nodeSpec.containerName),
- any(InetAddress.class),
- eq(nodeSpec.minCpuCores.get()),
- eq(nodeSpec.minDiskAvailableGb.get()),
- eq(nodeSpec.minMainMemoryAvailableGb.get()));
+ inOrder.verify(docker).createStartContainerCommand(
+ nodeSpec.wantedDockerImage.get(),
+ nodeSpec.containerName,
+ nodeSpec.hostname);
inOrder.verify(nodeRepository).updateNodeAttributes(hostName, restartGeneration, dockerImage, vespaVersion);
inOrder.verify(orchestrator).resume(hostName);
}
@@ -351,14 +338,10 @@ public class NodeAgentImplTest {
verify(orchestrator).suspend(hostName);
verify(docker, never()).stopContainer(any(ContainerName.class));
verify(docker, never()).deleteContainer(any(ContainerName.class));
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
any(HostName.class), anyLong(), any(DockerImage.class), anyString());
@@ -393,14 +376,10 @@ public class NodeAgentImplTest {
final InOrder inOrder = inOrder(orchestrator, docker);
inOrder.verify(docker).stopContainer(containerName);
inOrder.verify(docker).deleteContainer(containerName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(maintenanceScheduler, never()).deleteContainerStorage(any(ContainerName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
@@ -435,14 +414,10 @@ public class NodeAgentImplTest {
verify(orchestrator, never()).suspend(any(HostName.class));
verify(docker, never()).stopContainer(any(ContainerName.class));
verify(docker).deleteContainer(containerName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(maintenanceScheduler, never()).deleteContainerStorage(any(ContainerName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
@@ -475,14 +450,10 @@ public class NodeAgentImplTest {
verify(orchestrator, never()).suspend(any(HostName.class));
verify(docker, never()).stopContainer(any(ContainerName.class));
verify(docker, never()).deleteContainer(containerName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(maintenanceScheduler, never()).deleteContainerStorage(any(ContainerName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
@@ -518,14 +489,10 @@ public class NodeAgentImplTest {
final InOrder inOrder = inOrder(orchestrator, docker);
inOrder.verify(docker).stopContainer(containerName);
inOrder.verify(docker).deleteContainer(containerName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(maintenanceScheduler, never()).deleteContainerStorage(any(ContainerName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
@@ -562,14 +529,10 @@ public class NodeAgentImplTest {
verify(orchestrator, never()).suspend(any(HostName.class));
verify(docker, never()).stopContainer(any(ContainerName.class));
verify(docker).deleteContainer(containerName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(maintenanceScheduler, never()).deleteContainerStorage(any(ContainerName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
@@ -605,14 +568,10 @@ public class NodeAgentImplTest {
verify(orchestrator, never()).suspend(any(HostName.class));
verify(docker, never()).stopContainer(any(ContainerName.class));
verify(docker, never()).deleteContainer(any(ContainerName.class));
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(maintenanceScheduler, never()).deleteContainerStorage(any(ContainerName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
@@ -652,14 +611,10 @@ public class NodeAgentImplTest {
inOrder.verify(docker).deleteContainer(containerName);
inOrder.verify(maintenanceScheduler).deleteContainerStorage(containerName);
inOrder.verify(nodeRepository).markAsReady(hostName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
any(HostName.class), anyLong(), any(DockerImage.class), anyString());
@@ -698,14 +653,10 @@ public class NodeAgentImplTest {
inOrder.verify(docker).deleteContainer(containerName);
inOrder.verify(maintenanceScheduler).deleteContainerStorage(containerName);
inOrder.verify(nodeRepository).markAsReady(hostName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
any(HostName.class), anyLong(), any(DockerImage.class), anyString());
@@ -741,14 +692,10 @@ public class NodeAgentImplTest {
final InOrder inOrder = inOrder(docker, nodeRepository, maintenanceScheduler);
inOrder.verify(maintenanceScheduler).deleteContainerStorage(containerName);
inOrder.verify(nodeRepository).markAsReady(hostName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
any(HostName.class), anyLong(), any(DockerImage.class), anyString());
@@ -785,14 +732,10 @@ public class NodeAgentImplTest {
final InOrder inOrder = inOrder(docker, nodeRepository, maintenanceScheduler);
inOrder.verify(maintenanceScheduler).deleteContainerStorage(containerName);
inOrder.verify(nodeRepository).markAsReady(hostName);
- verify(docker, never()).startContainer(
+ verify(docker, never()).createStartContainerCommand(
any(DockerImage.class),
- any(HostName.class),
any(ContainerName.class),
- any(InetAddress.class),
- anyDouble(),
- anyDouble(),
- anyDouble());
+ any(HostName.class));
verify(orchestrator, never()).resume(any(HostName.class));
verify(nodeRepository, never()).updateNodeAttributes(
any(HostName.class), anyLong(), any(DockerImage.class), anyString());
@@ -831,7 +774,8 @@ public class NodeAgentImplTest {
when(docker.imageIsDownloaded(any(DockerImage.class))).thenReturn(true);
when(docker.executeInContainer(eq(containerName), anyVararg())).thenReturn(NODE_PROGRAM_DOESNT_EXIST);
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
when(orchestrator.suspend(any(HostName.class))).thenReturn(true);
final InOrder inOrder = inOrder(nodeRepository, docker);
@@ -908,7 +852,8 @@ public class NodeAgentImplTest {
when(docker.imageIsDownloaded(any(DockerImage.class))).thenReturn(true);
when(docker.executeInContainer(eq(containerName), anyVararg())).thenReturn(NODE_PROGRAM_DOESNT_EXIST);
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
when(orchestrator.suspend(any(HostName.class))).thenReturn(true);
doThrow(new IOException()).doNothing().when(nodeRepository).updateNodeAttributes(
any(HostName.class), anyLong(), any(DockerImage.class), anyString());
@@ -967,7 +912,8 @@ public class NodeAgentImplTest {
.thenReturn(new ProcessResult(0, "node program exists", ""))
.thenReturn(new ProcessResult(0, "node program succeeds 3rd time", ""));
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
final InOrder inOrder = inOrder(orchestrator, docker);
@@ -980,14 +926,10 @@ public class NodeAgentImplTest {
fail("Expected to throw an exception");
} catch (Exception e) {
}
- inOrder.verify(docker).startContainer(
- eq(nodeSpec.wantedDockerImage.get()),
- eq(nodeSpec.hostname),
- eq(nodeSpec.containerName),
- any(InetAddress.class),
- eq(nodeSpec.minCpuCores.get()),
- eq(nodeSpec.minDiskAvailableGb.get()),
- eq(nodeSpec.minMainMemoryAvailableGb.get()));
+ inOrder.verify(docker).createStartContainerCommand(
+ nodeSpec.wantedDockerImage.get(),
+ nodeSpec.containerName,
+ nodeSpec.hostname);
inOrder.verify(docker, times(2)).executeInContainer(any(), anyVararg());
inOrder.verifyNoMoreInteractions();
@@ -1064,7 +1006,8 @@ public class NodeAgentImplTest {
.thenReturn(new ProcessResult(0, "output", "")); // resuming succeeds
break;
}
- when(docker.getVespaVersion(containerName)).thenReturn(vespaVersion);
+ when(docker.executeInContainer(eq(containerName), eq(DockerOperations.GET_VESPA_VERSION_COMMAND)))
+ .thenReturn(new ProcessResult(0, vespaVersion, ""));
when(orchestrator.suspend(any(HostName.class))).thenReturn(true);
when(docker.getContainer(hostName)).thenReturn(Optional.of(existingContainer)).thenReturn(Optional.empty());
@@ -1087,14 +1030,10 @@ public class NodeAgentImplTest {
inOrder.verify(docker).stopContainer(containerName);
inOrder.verify(docker).deleteContainer(containerName);
- inOrder.verify(docker).startContainer(
- eq(nodeSpec.wantedDockerImage.get()),
- eq(nodeSpec.hostname),
- eq(nodeSpec.containerName),
- any(InetAddress.class),
- eq(nodeSpec.minCpuCores.get()),
- eq(nodeSpec.minDiskAvailableGb.get()),
- eq(nodeSpec.minMainMemoryAvailableGb.get()));
+ inOrder.verify(docker).createStartContainerCommand(
+ nodeSpec.wantedDockerImage.get(),
+ nodeSpec.containerName,
+ nodeSpec.hostname);
switch (scenario) {
case EXCEPTION:
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java
index 5c707c56bc8..16e41d601fb 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java
@@ -7,8 +7,8 @@ import com.yahoo.application.Networking;
import com.yahoo.application.container.JDisc;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
-import com.yahoo.vespa.hosted.node.admin.docker.DockerImage;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.provision.testutils.ContainerConfig;
import org.junit.After;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/maintenance/MaintainerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/maintenance/MaintainerTest.java
index 0c46f4876cc..979b23dbfcb 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/maintenance/MaintainerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/maintenance/MaintainerTest.java
@@ -1,6 +1,6 @@
package com.yahoo.vespa.hosted.node.maintenance;
-import com.yahoo.vespa.hosted.node.admin.docker.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import org.junit.Test;
import static org.junit.Assert.assertEquals;