summaryrefslogtreecommitdiffstats
path: root/node-admin
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-06-28 11:52:26 +0200
committerMartin Polden <mpolden@mpolden.no>2021-06-29 10:13:02 +0200
commitbef662b9743999cce4e71afa3f331b2b9bdfa5be (patch)
tree6df1a98073944e02ee8c5fb419ab3985a7925d66 /node-admin
parent85ef3c6c5e1e9d8ef4216d9ff6954a9abba3ea53 (diff)
Remove docker-api
Diffstat (limited to 'node-admin')
-rw-r--r--node-admin/pom.xml6
-rw-r--r--node-admin/src/main/application/services.xml3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/Container.java80
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerId.java36
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerName.java54
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerOperations.java7
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResources.java130
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerStats.java231
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResult.java48
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentials.java57
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentialsProvider.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Counter.java28
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/DimensionMetrics.java76
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Dimensions.java55
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Gauge.java24
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricValue.java9
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Metrics.java128
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/package-info.java5
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java8
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContext.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentContextImpl.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java26
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerNameTest.java45
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResourcesTest.java49
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResultTest.java24
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricsTest.java99
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerFailTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerOperationsMock.java14
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/ContainerTester.java6
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/MultiContainerTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RebootTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integration/RestartTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java4
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java2
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java17
40 files changed, 1223 insertions, 76 deletions
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/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/Container.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/Container.java
new file mode 100644
index 00000000000..7aeb43e44f4
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/Container.java
@@ -0,0 +1,80 @@
+// 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.container;
+
+import com.yahoo.config.provision.DockerImage;
+
+import java.util.Objects;
+
+/**
+ * @author stiankri
+ */
+public class Container {
+ private final ContainerId id;
+ public final String hostname;
+ public final DockerImage image;
+ public final ContainerResources resources;
+ public final ContainerName name;
+ public final State state;
+ public final int pid;
+
+ public Container(
+ final ContainerId id,
+ final String hostname,
+ final DockerImage image,
+ final ContainerResources resources,
+ final ContainerName containerName,
+ final State state,
+ final int pid) {
+ this.id = id;
+ this.hostname = hostname;
+ this.image = image;
+ this.resources = resources;
+ this.name = containerName;
+ this.state = state;
+ this.pid = pid;
+ }
+
+ public ContainerId id() {
+ return id;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (!(obj instanceof Container)) {
+ return false;
+ }
+ final Container other = (Container) obj;
+ return Objects.equals(id, other.id)
+ && Objects.equals(hostname, other.hostname)
+ && Objects.equals(image, other.image)
+ && Objects.equals(resources, other.resources)
+ && Objects.equals(name, other.name)
+ && Objects.equals(pid, other.pid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(hostname, image, resources, name, pid);
+ }
+
+ @Override
+ public String toString() {
+ return "Container {"
+ + " id=" + id
+ + " hostname=" + hostname
+ + " image=" + image
+ + " resources=" + resources
+ + " name=" + name
+ + " state=" + state
+ + " pid=" + pid
+ + "}";
+ }
+
+ public enum State {
+ CREATED, RESTARTING, RUNNING, REMOVING, PAUSED, EXITED, DEAD, UNKNOWN, CONFIGURED, STOPPED;
+
+ public boolean isRunning() {
+ return this == RUNNING;
+ }
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerId.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerId.java
new file mode 100644
index 00000000000..0e6a4835abf
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerId.java
@@ -0,0 +1,36 @@
+// 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 java.util.Objects;
+
+/**
+ * The ID of a container.
+ *
+ * @author hakon
+ */
+public class ContainerId {
+ private final String id;
+
+ public ContainerId(String id) {
+ this.id = Objects.requireNonNull(id, "id cannot be null");
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ContainerId that = (ContainerId) o;
+ return id.equals(that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerName.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerName.java
new file mode 100644
index 00000000000..49dacc44335
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerName.java
@@ -0,0 +1,54 @@
+// 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.container;
+
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * Type-safe value wrapper for docker container names.
+ *
+ * @author bakksjo
+ */
+public class ContainerName {
+ private static final Pattern LEGAL_CONTAINER_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9-]+$");
+ private final String name;
+
+ public ContainerName(final String name) {
+ this.name = Objects.requireNonNull(name);
+ if (! LEGAL_CONTAINER_NAME_PATTERN.matcher(name).matches()) {
+ throw new IllegalArgumentException("Illegal container name: " + name + ". Must match " +
+ LEGAL_CONTAINER_NAME_PATTERN.toString());
+ }
+ }
+
+ public String asString() {
+ return name;
+ }
+
+ public static ContainerName fromHostname(final String hostName) {
+ return new ContainerName(hostName.split("\\.", 2)[0]);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof ContainerName)) {
+ return false;
+ }
+
+ final ContainerName other = (ContainerName) o;
+
+ return Objects.equals(name, other.name);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " {"
+ + " name=" + 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/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResources.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResources.java
new file mode 100644
index 00000000000..f01527f58d1
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResources.java
@@ -0,0 +1,130 @@
+// 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.container;
+
+import java.util.Objects;
+
+/**
+ * @author valerijf
+ */
+public class ContainerResources {
+
+ public static final ContainerResources UNLIMITED = ContainerResources.from(0, 0, 0);
+ public static final int CPU_PERIOD_US = 100_000; // 100 ms
+
+ /**
+ * Hard limit on container's CPU usage: Implemented using Completely Fair Scheduler (CFS) by allocating a given
+ * time within a given period, Container's processes are not bound to any specific CPU, which may create significant
+ * performance degradation as processes are scheduled on another CPU after exhausting the quota.
+ */
+ private final double cpus;
+
+ /**
+ * Soft limit on container's CPU usage: When plenty of CPU cycles are available, all containers use as much
+ * CPU as they need. It prioritizes container CPU resources for the available CPU cycles.
+ * It does not guarantee or reserve any specific CPU access.
+ */
+ private final int cpuShares;
+
+ /** The maximum amount, in bytes, of memory the container can use. */
+ private final long memoryBytes;
+
+ public ContainerResources(double cpus, int cpuShares, long memoryBytes) {
+ this.cpus = cpus;
+ this.cpuShares = cpuShares;
+ this.memoryBytes = memoryBytes;
+
+ if (cpus < 0)
+ throw new IllegalArgumentException("CPUs must be a positive number or 0 for unlimited, was " + cpus);
+ if (cpuShares != 0 && (cpuShares < 2 || cpuShares > 262_144))
+ throw new IllegalArgumentException("CPU shares must be a positive integer in [2, 262144] or 0 for unlimited, was " + cpuShares);
+ if (memoryBytes < 0)
+ throw new IllegalArgumentException("memoryBytes must be a positive integer or 0 for unlimited, was " + memoryBytes);
+ }
+
+ /**
+ * Create container resources from required fields.
+ *
+ * @param maxVcpu the amount of vcpu that allocation policies should allocate exclusively to this container.
+ * This is a hard upper limit. To allow an unlimited amount use 0.
+ * @param minVcpu the minimal amount of vcpu dedicated to this container.
+ * To avoid dedicating any cpu at all, use 0.
+ * @param memoryGb the amount of memory that allocation policies should allocate to this container.
+ * This is a hard upper limit. To allow the container to allocate an unlimited amount use 0.
+ * @return the container resources encapsulating the parameters
+ */
+ public static ContainerResources from(double maxVcpu, double minVcpu, double memoryGb) {
+ return new ContainerResources(maxVcpu,
+ (int) Math.round(32 * minVcpu),
+ (long) ((1L << 30) * memoryGb));
+ }
+
+ public double cpus() {
+ return cpus;
+ }
+
+ /** Returns the CFS CPU quota per {@link #cpuPeriod()}, or -1 if disabled. */
+ public int cpuQuota() {
+ return cpus > 0 ? (int) (cpus * CPU_PERIOD_US) : -1;
+ }
+
+ /** Duration (in µs) of a single period used as the basis for process scheduling */
+ public int cpuPeriod() {
+ return CPU_PERIOD_US;
+ }
+
+ public int cpuShares() {
+ return cpuShares;
+ }
+
+ public long memoryBytes() {
+ return memoryBytes;
+ }
+
+ /** Returns true iff the memory component(s) of between <code>this</code> and <code>other</code> are equal */
+ public boolean equalsMemory(ContainerResources other) {
+ return memoryBytes == other.memoryBytes;
+ }
+
+ /** Returns true iff the CPU component(s) of between <code>this</code> and <code>other</code> are equal */
+ public boolean equalsCpu(ContainerResources other) {
+ return Math.abs(other.cpus - cpus) < 0.0001 && cpuShares == other.cpuShares;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ContainerResources that = (ContainerResources) o;
+ return equalsMemory(that) && equalsCpu(that);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(cpus, cpuShares, memoryBytes);
+ }
+
+
+ /** Returns only the memory component(s) of {@link #toString()} */
+ public String toStringMemory() {
+ return (memoryBytes > 0 ? memoryBytes + "B" : "unlimited") + " memory";
+ }
+
+ /** Returns only the CPU component(s) of {@link #toString()} */
+ public String toStringCpu() {
+ return (cpus > 0 ? String.format("%.2f", cpus) : "unlimited") +" CPUs, " +
+ (cpuShares > 0 ? cpuShares : "unlimited") + " CPU Shares";
+ }
+
+ @Override
+ public String toString() {
+ return toStringCpu() + ", " + toStringMemory();
+ }
+
+ public ContainerResources withMemoryBytes(long memoryBytes) {
+ return new ContainerResources(cpus, cpuShares, memoryBytes);
+ }
+
+ public ContainerResources withUnlimitedCpus() {
+ return new ContainerResources(0, 0, memoryBytes);
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerStats.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerStats.java
new file mode 100644
index 00000000000..78adcebe31e
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ContainerStats.java
@@ -0,0 +1,231 @@
+// 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.container;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * CPU, memory and network statistics collected from a container.
+ *
+ * @author freva
+ */
+public class ContainerStats {
+
+ private final Map<String, NetworkStats> networkStatsByInterface;
+ private final MemoryStats memoryStats;
+ private final CpuStats cpuStats;
+
+ public ContainerStats(Map<String, NetworkStats> networkStatsByInterface, MemoryStats memoryStats, CpuStats cpuStats) {
+ this.networkStatsByInterface = new LinkedHashMap<>(Objects.requireNonNull(networkStatsByInterface));
+ this.memoryStats = Objects.requireNonNull(memoryStats);
+ this.cpuStats = Objects.requireNonNull(cpuStats);
+ }
+
+ public Map<String, NetworkStats> getNetworks() {
+ return Collections.unmodifiableMap(networkStatsByInterface);
+ }
+
+ public MemoryStats getMemoryStats() {
+ return memoryStats;
+ }
+
+ public CpuStats getCpuStats() {
+ return cpuStats;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ContainerStats that = (ContainerStats) o;
+ return networkStatsByInterface.equals(that.networkStatsByInterface) && memoryStats.equals(that.memoryStats) && cpuStats.equals(that.cpuStats);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkStatsByInterface, memoryStats, cpuStats);
+ }
+
+ /** Statistics for network usage */
+ public static class NetworkStats {
+
+ private final long rxBytes;
+ private final long rxDropped;
+ private final long rxErrors;
+ private final long txBytes;
+ private final long txDropped;
+ private final long txErrors;
+
+ public NetworkStats(long rxBytes, long rxDropped, long rxErrors, long txBytes, long txDropped, long txErrors) {
+ this.rxBytes = rxBytes;
+ this.rxDropped = rxDropped;
+ this.rxErrors = rxErrors;
+ this.txBytes = txBytes;
+ this.txDropped = txDropped;
+ this.txErrors = txErrors;
+ }
+
+ /** Returns received bytes */
+ public long getRxBytes() { return this.rxBytes; }
+
+ /** Returns received bytes, which was dropped */
+ public long getRxDropped() { return this.rxDropped; }
+
+ /** Returns received errors */
+ public long getRxErrors() { return this.rxErrors; }
+
+ /** Returns transmitted bytes */
+ public long getTxBytes() { return this.txBytes; }
+
+ /** Returns transmitted bytes, which was dropped */
+ public long getTxDropped() { return this.txDropped; }
+
+ /** Returns transmission errors */
+ public long getTxErrors() { return this.txErrors; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NetworkStats that = (NetworkStats) o;
+ return rxBytes == that.rxBytes && rxDropped == that.rxDropped && rxErrors == that.rxErrors && txBytes == that.txBytes && txDropped == that.txDropped && txErrors == that.txErrors;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rxBytes, rxDropped, rxErrors, txBytes, txDropped, txErrors);
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkStats{" +
+ "rxBytes=" + rxBytes +
+ ", rxDropped=" + rxDropped +
+ ", rxErrors=" + rxErrors +
+ ", txBytes=" + txBytes +
+ ", txDropped=" + txDropped +
+ ", txErrors=" + txErrors +
+ '}';
+ }
+
+ }
+
+ /** Statistics for memory usage */
+ public static class MemoryStats {
+
+ private final long cache;
+ private final long usage;
+ private final long limit;
+
+ public MemoryStats(long cache, long usage, long limit) {
+ this.cache = cache;
+ this.usage = usage;
+ this.limit = limit;
+ }
+
+ /** Returns memory used by cache in bytes */
+ public long getCache() { return this.cache; }
+
+ /** Returns memory usage in bytes */
+ public long getUsage() { return this.usage; }
+
+ /** Returns memory limit in bytes */
+ public long getLimit() { return this.limit; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MemoryStats that = (MemoryStats) o;
+ return cache == that.cache && usage == that.usage && limit == that.limit;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(cache, usage, limit);
+ }
+
+ @Override
+ public String toString() {
+ return "MemoryStats{" +
+ "cache=" + cache +
+ ", usage=" + usage +
+ ", limit=" + limit +
+ '}';
+ }
+
+ }
+
+ /** Statistics for CPU usage */
+ public static class CpuStats {
+
+ private final int onlineCpus;
+ private final long systemCpuUsage;
+ private final long totalUsage;
+ private final long usageInKernelMode;
+ private final long throttledTime;
+ private final long throttlingActivePeriods;
+ private final long throttledPeriods;
+
+ public CpuStats(int onlineCpus, long systemCpuUsage, long totalUsage, long usageInKernelMode,
+ long throttledTime, long throttlingActivePeriods, long throttledPeriods) {
+ this.onlineCpus = onlineCpus;
+ this.systemCpuUsage = systemCpuUsage;
+ this.totalUsage = totalUsage;
+ this.usageInKernelMode = usageInKernelMode;
+ this.throttledTime = throttledTime;
+ this.throttlingActivePeriods = throttlingActivePeriods;
+ this.throttledPeriods = throttledPeriods;
+ }
+
+ public int getOnlineCpus() { return this.onlineCpus; }
+
+ /** Total CPU time (in ns) spent executing all the processes on this host */
+ public long getSystemCpuUsage() { return this.systemCpuUsage; }
+
+ /** Total CPU time (in ns) spent running all the processes in this container */
+ public long getTotalUsage() { return totalUsage; }
+
+ /** Total CPU time (in ns) spent in kernel mode while executing processes in this container */
+ public long getUsageInKernelMode() { return usageInKernelMode; }
+
+ /** Total CPU time (in ns) processes in this container were throttled for */
+ public long getThrottledTime() { return throttledTime; }
+
+ /** Number of periods with throttling enabled for this container */
+ public long getThrottlingActivePeriods() { return throttlingActivePeriods; }
+
+ /** Number of periods this container hit the throttling limit */
+ public long getThrottledPeriods() { return throttledPeriods; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CpuStats cpuStats = (CpuStats) o;
+ return onlineCpus == cpuStats.onlineCpus && systemCpuUsage == cpuStats.systemCpuUsage && totalUsage == cpuStats.totalUsage && usageInKernelMode == cpuStats.usageInKernelMode && throttledTime == cpuStats.throttledTime && throttlingActivePeriods == cpuStats.throttlingActivePeriods && throttledPeriods == cpuStats.throttledPeriods;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(onlineCpus, systemCpuUsage, totalUsage, usageInKernelMode, throttledTime, throttlingActivePeriods, throttledPeriods);
+ }
+
+ @Override
+ public String toString() {
+ return "CpuStats{" +
+ "onlineCpus=" + onlineCpus +
+ ", systemCpuUsage=" + systemCpuUsage +
+ ", totalUsage=" + totalUsage +
+ ", usageInKernelMode=" + usageInKernelMode +
+ ", throttledTime=" + throttledTime +
+ ", throttlingActivePeriods=" + throttlingActivePeriods +
+ ", throttledPeriods=" + throttledPeriods +
+ '}';
+ }
+
+ }
+
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResult.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResult.java
new file mode 100644
index 00000000000..066a65eb409
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResult.java
@@ -0,0 +1,48 @@
+// 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.container;
+
+import java.util.Objects;
+
+// TODO: Replace usages of this with CommandResult
+public class ProcessResult {
+ private final int exitStatus;
+ private final String output;
+ private final String errors;
+
+ public ProcessResult(int exitStatus, String output, String errors) {
+ this.exitStatus = exitStatus;
+ this.output = output;
+ this.errors = errors;
+ }
+
+ public boolean isSuccess() { return exitStatus == 0; }
+ public int getExitStatus() { return exitStatus; }
+
+ public String getOutput() { return output; }
+
+ public String getErrors() { return errors; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProcessResult)) return false;
+ ProcessResult other = (ProcessResult) o;
+ return Objects.equals(exitStatus, other.exitStatus)
+ && Objects.equals(output, other.output)
+ && Objects.equals(errors, other.errors);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(exitStatus, output, errors);
+ }
+
+ @Override
+ public String toString() {
+ return "ProcessResult {"
+ + " exitStatus=" + exitStatus
+ + " output=" + output
+ + " errors=" + errors
+ + " }";
+ }
+
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentials.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentials.java
new file mode 100644
index 00000000000..6437497fa68
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/RegistryCredentials.java
@@ -0,0 +1,57 @@
+// 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 java.util.Objects;
+
+/**
+ * Credentials for a container registry server.
+ *
+ * @author mpolden
+ */
+public class RegistryCredentials {
+
+ public static final RegistryCredentials none = new RegistryCredentials("", "", "");
+
+ private final String username;
+ private final String password;
+ private final String registryAddress;
+
+ public RegistryCredentials(String username, String password, String registryAddress) {
+ this.username = Objects.requireNonNull(username);
+ this.password = Objects.requireNonNull(password);
+ this.registryAddress = Objects.requireNonNull(registryAddress);
+ }
+
+ public String username() {
+ return username;
+ }
+
+ public String password() {
+ return password;
+ }
+
+ public String registryAddress() {
+ return registryAddress;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RegistryCredentials that = (RegistryCredentials) o;
+ return username.equals(that.username) &&
+ password.equals(that.password) &&
+ registryAddress.equals(that.registryAddress);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(username, password, registryAddress);
+ }
+
+ @Override
+ public String toString() {
+ return "registry credentials for " + registryAddress + " [username=" + username + ",password=" + password + "]";
+ }
+
+}
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/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Counter.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Counter.java
new file mode 100644
index 00000000000..9cb26d71275
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Counter.java
@@ -0,0 +1,28 @@
+// 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.node.admin.container.metrics;
+
+/**
+ * @author freva
+ */
+public class Counter implements MetricValue {
+ private final Object lock = new Object();
+
+ private long value = 0;
+
+ public void increment() {
+ add(1L);
+ }
+
+ public void add(long n) {
+ synchronized (lock) {
+ value += n;
+ }
+ }
+
+ @Override
+ public Number getValue() {
+ synchronized (lock) {
+ return value;
+ }
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/DimensionMetrics.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/DimensionMetrics.java
new file mode 100644
index 00000000000..929fcde9492
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/DimensionMetrics.java
@@ -0,0 +1,76 @@
+// 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.container.metrics;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author freva
+ */
+public class DimensionMetrics {
+
+ private final String application;
+ private final Dimensions dimensions;
+ private final Map<String, Number> metrics;
+
+ DimensionMetrics(String application, Dimensions dimensions, Map<String, Number> metrics) {
+ this.application = Objects.requireNonNull(application);
+ this.dimensions = Objects.requireNonNull(dimensions);
+ this.metrics = metrics.entrySet().stream()
+ .filter(DimensionMetrics::metricIsFinite)
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ public String getApplication() {
+ return application;
+ }
+
+ public Dimensions getDimensions() {
+ return dimensions;
+ }
+
+ public Map<String, Number> getMetrics() {
+ return metrics;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DimensionMetrics that = (DimensionMetrics) o;
+ return application.equals(that.application) &&
+ dimensions.equals(that.dimensions) &&
+ metrics.equals(that.metrics);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(application, dimensions, metrics);
+ }
+
+ private static boolean metricIsFinite(Map.Entry<String, Number> metric) {
+ return ! (metric.getValue() instanceof Double) || Double.isFinite((double) metric.getValue());
+ }
+
+ public static class Builder {
+ private final String application;
+ private final Dimensions dimensions;
+ private final Map<String, Number> metrics = new HashMap<>();
+
+ public Builder(String application, Dimensions dimensions) {
+ this.application = application;
+ this.dimensions = dimensions;
+ }
+
+ public Builder withMetric(String metricName, Number metricValue) {
+ metrics.put(metricName, metricValue);
+ return this;
+ }
+
+ public DimensionMetrics build() {
+ return new DimensionMetrics(application, dimensions, metrics);
+ }
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Dimensions.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Dimensions.java
new file mode 100644
index 00000000000..2999a3c8839
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Dimensions.java
@@ -0,0 +1,55 @@
+// 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.node.admin.container.metrics;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author freva
+ */
+public class Dimensions {
+
+ public static final Dimensions NONE = new Dimensions(Map.of());
+
+ private final Map<String, String> dimensionsMap;
+
+ public Dimensions(Map<String, String> dimensionsMap) {
+ this.dimensionsMap = Map.copyOf(dimensionsMap);
+ }
+
+ public Map<String, String> asMap() {
+ return dimensionsMap;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Dimensions that = (Dimensions) o;
+ return dimensionsMap.equals(that.dimensionsMap);
+ }
+
+ @Override
+ public int hashCode() {
+ return dimensionsMap.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return dimensionsMap.toString();
+ }
+
+ public static class Builder {
+ private final Map<String, String> dimensionsMap = new HashMap<>();
+
+ public Dimensions.Builder add(String dimensionName, String dimensionValue) {
+ dimensionsMap.put(dimensionName, dimensionValue);
+ return this;
+ }
+
+ public Dimensions build() {
+ return new Dimensions(dimensionsMap);
+ }
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Gauge.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Gauge.java
new file mode 100644
index 00000000000..d3a7072b08b
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Gauge.java
@@ -0,0 +1,24 @@
+// 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.node.admin.container.metrics;
+
+/**
+ * @author freva
+ */
+public class Gauge implements MetricValue {
+ private final Object lock = new Object();
+
+ private double value;
+
+ public void sample(double x) {
+ synchronized (lock) {
+ this.value = x;
+ }
+ }
+
+ @Override
+ public Number getValue() {
+ synchronized (lock) {
+ return value;
+ }
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricValue.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricValue.java
new file mode 100644
index 00000000000..07a6ffd9d22
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricValue.java
@@ -0,0 +1,9 @@
+// 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.node.admin.container.metrics;
+
+/**
+ * @author freva
+ */
+public interface MetricValue {
+ Number getValue();
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Metrics.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Metrics.java
new file mode 100644
index 00000000000..ec3b2d347e5
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/Metrics.java
@@ -0,0 +1,128 @@
+// 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.container.metrics;
+
+import com.google.inject.Inject;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Stores the latest metric for the given application, name, dimension triplet in memory
+ *
+ * @author freva
+ */
+public class Metrics {
+ // Application names used
+ public static final String APPLICATION_HOST = "vespa.host";
+ public static final String APPLICATION_NODE = "vespa.node";
+
+ private final Object monitor = new Object();
+ private final Map<DimensionType, Map<String, ApplicationMetrics>> metrics = new HashMap<>();
+
+ @Inject
+ public Metrics() { }
+
+ /**
+ * Creates a counter metric under vespa.host application, with no dimensions and default dimension type
+ * See {@link #declareCounter(String, String, Dimensions, DimensionType)}
+ */
+ public Counter declareCounter(String name) {
+ return declareCounter(name, Dimensions.NONE);
+ }
+
+ /**
+ * Creates a counter metric under vespa.host application, with the given dimensions and default dimension type
+ * See {@link #declareCounter(String, String, Dimensions, DimensionType)}
+ */
+ public Counter declareCounter(String name, Dimensions dimensions) {
+ return declareCounter(APPLICATION_HOST, name, dimensions, DimensionType.DEFAULT);
+ }
+
+ /** Creates a counter metric. This method is idempotent. */
+ public Counter declareCounter(String application, String name, Dimensions dimensions, DimensionType type) {
+ synchronized (monitor) {
+ return (Counter) getOrCreateApplicationMetrics(application, type)
+ .computeIfAbsent(dimensions, d -> new HashMap<>())
+ .computeIfAbsent(name, n -> new Counter());
+ }
+ }
+
+ /**
+ * Creates a gauge metric under vespa.host application, with no dimensions and default dimension type
+ * See {@link #declareGauge(String, String, Dimensions, DimensionType)}
+ */
+ public Gauge declareGauge(String name) {
+ return declareGauge(name, Dimensions.NONE);
+ }
+
+ /**
+ * Creates a gauge metric under vespa.host application, with the given dimensions and default dimension type
+ * See {@link #declareGauge(String, String, Dimensions, DimensionType)}
+ */
+ public Gauge declareGauge(String name, Dimensions dimensions) {
+ return declareGauge(APPLICATION_HOST, name, dimensions, DimensionType.DEFAULT);
+ }
+
+ /** Creates a gauge metric. This method is idempotent */
+ public Gauge declareGauge(String application, String name, Dimensions dimensions, DimensionType type) {
+ synchronized (monitor) {
+ return (Gauge) getOrCreateApplicationMetrics(application, type)
+ .computeIfAbsent(dimensions, d -> new HashMap<>())
+ .computeIfAbsent(name, n -> new Gauge());
+ }
+ }
+
+ public List<DimensionMetrics> getDefaultMetrics() {
+ return getMetricsByType(DimensionType.DEFAULT);
+ }
+
+ public List<DimensionMetrics> getMetricsByType(DimensionType type) {
+ synchronized (monitor) {
+ List<DimensionMetrics> dimensionMetrics = new ArrayList<>();
+ metrics.getOrDefault(type, Map.of())
+ .forEach((application, applicationMetrics) -> applicationMetrics.metricsByDimensions().entrySet().stream()
+ .map(entry -> new DimensionMetrics(application, entry.getKey(),
+ entry.getValue().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, value -> value.getValue().getValue()))))
+ .forEach(dimensionMetrics::add));
+ return dimensionMetrics;
+ }
+ }
+
+ public void deleteMetricByDimension(String name, Dimensions dimensionsToRemove, DimensionType type) {
+ synchronized (monitor) {
+ Optional.ofNullable(metrics.get(type))
+ .map(m -> m.get(name))
+ .map(ApplicationMetrics::metricsByDimensions)
+ .ifPresent(m -> m.remove(dimensionsToRemove));
+ }
+ }
+
+ Map<Dimensions, Map<String, MetricValue>> getOrCreateApplicationMetrics(String application, DimensionType type) {
+ return metrics.computeIfAbsent(type, m -> new HashMap<>())
+ .computeIfAbsent(application, app -> new ApplicationMetrics())
+ .metricsByDimensions();
+ }
+
+ // "Application" is the monitoring application, not Vespa application
+ private static class ApplicationMetrics {
+ private final Map<Dimensions, Map<String, MetricValue>> metricsByDimensions = new LinkedHashMap<>();
+
+ Map<Dimensions, Map<String, MetricValue>> metricsByDimensions() {
+ return metricsByDimensions;
+ }
+ }
+
+ // Used to distinguish whether metrics have been populated with all tag vaules
+ public enum DimensionType {
+ /** Default metrics get added default dimensions set in check config */
+ DEFAULT,
+
+ /** Pretagged metrics will only get the dimensions explicitly set when creating the counter/gauge */
+ PRETAGGED
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/package-info.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/package-info.java
new file mode 100644
index 00000000000..3f514f96f84
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/container/metrics/package-info.java
@@ -0,0 +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.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/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerNameTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerNameTest.java
new file mode 100644
index 00000000000..f98c78c740a
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerNameTest.java
@@ -0,0 +1,45 @@
+// 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.container;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author freva
+ */
+public class ContainerNameTest {
+ @Test
+ public void testAlphanumericalContainerName() {
+ String name = "container123";
+ ContainerName containerName = new ContainerName(name);
+ assertEquals(containerName.asString(), name);
+ }
+
+ @Test
+ public void testAlphanumericalWithDashContainerName() {
+ String name = "container-123";
+ ContainerName containerName = new ContainerName(name);
+ assertEquals(containerName.asString(), name);
+ }
+
+ @Test
+ public void testContainerNameFromHostname() {
+ assertEquals(new ContainerName("container-123"), ContainerName.fromHostname("container-123.sub.domain.tld"));
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testAlphanumericalWithSlashContainerName() {
+ new ContainerName("container/123");
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testEmptyContainerName() {
+ new ContainerName("");
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void testNullContainerName() {
+ new ContainerName(null);
+ }
+}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResourcesTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResourcesTest.java
new file mode 100644
index 00000000000..d2ff57e5c09
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ContainerResourcesTest.java
@@ -0,0 +1,49 @@
+// 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.node.admin.container;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author freva
+ */
+public class ContainerResourcesTest {
+
+ @Test
+ public void verify_unlimited() {
+ assertEquals(-1, ContainerResources.UNLIMITED.cpuQuota());
+ assertEquals(100_000, ContainerResources.UNLIMITED.cpuPeriod());
+ assertEquals(0, ContainerResources.UNLIMITED.cpuShares());
+ }
+
+ @Test
+ public void validate_shares() {
+ new ContainerResources(0, 0, 0);
+ new ContainerResources(0, 2, 0);
+ new ContainerResources(0, 2048, 0);
+ new ContainerResources(0, 262_144, 0);
+
+ assertThrows(IllegalArgumentException.class, () -> new ContainerResources(0, -1, 0)); // Negative shares not allowed
+ assertThrows(IllegalArgumentException.class, () -> new ContainerResources(0, 1, 0)); // 1 share not allowed
+ assertThrows(IllegalArgumentException.class, () -> new ContainerResources(0, 262_145, 0));
+ }
+
+ @Test
+ public void cpu_shares_scaling() {
+ ContainerResources resources = ContainerResources.from(5.3, 2.5, 0);
+ assertEquals(530_000, resources.cpuQuota());
+ assertEquals(100_000, resources.cpuPeriod());
+ assertEquals(80, resources.cpuShares());
+ }
+
+ private static void assertThrows(Class<? extends Throwable> clazz, Runnable runnable) {
+ try {
+ runnable.run();
+ fail("Expected " + clazz);
+ } catch (Throwable e) {
+ if (!clazz.isInstance(e)) throw e;
+ }
+ }
+}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResultTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResultTest.java
new file mode 100644
index 00000000000..f7b832bd566
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/ProcessResultTest.java
@@ -0,0 +1,24 @@
+// 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.container;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ProcessResultTest {
+ @Test
+ public void testBasicProperties() {
+ ProcessResult processResult = new ProcessResult(0, "foo", "bar");
+ assertEquals(0, processResult.getExitStatus());
+ assertEquals("foo", processResult.getOutput());
+ assertTrue(processResult.isSuccess());
+ }
+
+ @Test
+ public void testSuccessFails() {
+ ProcessResult processResult = new ProcessResult(1, "foo", "bar");
+ assertFalse(processResult.isSuccess());
+ }
+}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricsTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricsTest.java
new file mode 100644
index 00000000000..6c48fe65142
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/container/metrics/MetricsTest.java
@@ -0,0 +1,99 @@
+// 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.container.metrics;
+
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+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;
+
+/**
+ * @author freva
+ */
+public class MetricsTest {
+ private static final Dimensions hostDimension = new Dimensions.Builder().add("host", "abc.yahoo.com").build();
+ private final Metrics metrics = new Metrics();
+
+ @Test
+ public void testDefaultValue() {
+ metrics.declareCounter("some.name", hostDimension);
+
+ assertEquals(getMetricsForDimension(hostDimension).get("some.name"), 0L);
+ }
+
+ @Test
+ public void testSimpleIncrementMetric() {
+ Counter counter = metrics.declareCounter("a_counter.value", hostDimension);
+
+ counter.add(5);
+ counter.add(8);
+
+ Map<String, Number> latestMetrics = getMetricsForDimension(hostDimension);
+ assertEquals("Expected only 1 metric value to be set", 1, latestMetrics.size());
+ assertEquals(latestMetrics.get("a_counter.value"), 13L); // 5 + 8
+ }
+
+ @Test
+ public void testSimpleGauge() {
+ Gauge gauge = metrics.declareGauge("test.gauge", hostDimension);
+
+ gauge.sample(42);
+ gauge.sample(-342.23);
+
+ Map<String, Number> latestMetrics = getMetricsForDimension(hostDimension);
+ assertEquals("Expected only 1 metric value to be set", 1, latestMetrics.size());
+ assertEquals(latestMetrics.get("test.gauge"), -342.23);
+ }
+
+ @Test
+ public void testRedeclaringSameGauge() {
+ Gauge gauge = metrics.declareGauge("test.gauge", hostDimension);
+ gauge.sample(42);
+
+ // Same as hostDimension, but new instance.
+ Dimensions newDimension = new Dimensions.Builder().add("host", "abc.yahoo.com").build();
+ Gauge newGauge = metrics.declareGauge("test.gauge", newDimension);
+ newGauge.sample(56);
+
+ assertEquals(getMetricsForDimension(hostDimension).get("test.gauge"), 56.);
+ }
+
+ @Test
+ public void testSameMetricNameButDifferentDimensions() {
+ Gauge gauge = metrics.declareGauge("test.gauge", hostDimension);
+ gauge.sample(42);
+
+ // Not the same as hostDimension.
+ Dimensions newDimension = new Dimensions.Builder().add("host", "abcd.yahoo.com").build();
+ Gauge newGauge = metrics.declareGauge("test.gauge", newDimension);
+ newGauge.sample(56);
+
+ assertEquals(getMetricsForDimension(hostDimension).get("test.gauge"), 42.);
+ assertEquals(getMetricsForDimension(newDimension).get("test.gauge"), 56.);
+ }
+
+ @Test
+ public void testDeletingMetric() {
+ metrics.declareGauge("test.gauge", hostDimension);
+
+ Dimensions differentDimension = new Dimensions.Builder().add("host", "abcd.yahoo.com").build();
+ metrics.declareGauge("test.gauge", differentDimension);
+
+ assertEquals(2, metrics.getMetricsByType(DEFAULT).size());
+ metrics.deleteMetricByDimension(APPLICATION_HOST, differentDimension, DEFAULT);
+ assertEquals(1, metrics.getMetricsByType(DEFAULT).size());
+ assertEquals(getMetricsForDimension(hostDimension).size(), 1);
+ assertEquals(getMetricsForDimension(differentDimension).size(), 0);
+ }
+
+ private Map<String, Number> getMetricsForDimension(Dimensions dimensions) {
+ return metrics.getOrCreateApplicationMetrics(APPLICATION_HOST, DEFAULT)
+ .getOrDefault(dimensions, Map.of())
+ .entrySet()
+ .stream()
+ .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getValue()));
+ }
+}
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());