diff options
author | Håkon Hallingstad <hakon@yahoo-inc.com> | 2016-08-31 13:14:13 +0200 |
---|---|---|
committer | Håkon Hallingstad <hakon@yahoo-inc.com> | 2016-09-01 12:48:00 +0200 |
commit | c8d9fb3e150cfdcffd14d96df0040c0c6a616736 (patch) | |
tree | 35ffcc06752ba7d47997f368b4e17214853efd9f | |
parent | da7a0474414dbb50733180ac0ac52f4b1f9811b5 (diff) |
Need to figure out what to do with the tests using DockerOperations
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; |