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