summaryrefslogtreecommitdiffstats
path: root/node-repository/src/main
diff options
context:
space:
mode:
authorValerij Fredriksen <valerij92@gmail.com>2019-03-13 21:54:40 +0100
committerValerij Fredriksen <valerij92@gmail.com>2019-03-13 21:54:40 +0100
commit2abad9dee6bd19ac3df3faa0fd89537093f33b97 (patch)
tree5e42df1733ca95e50ab06d7911de02d29ba7724b /node-repository/src/main
parent4deb8e5c8af23b55b2138b3d9d8b9341379099df (diff)
Set docker image prefix using /nodes/v2/upgrade API
Diffstat (limited to 'node-repository/src/main')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java10
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerImages.java80
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java14
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/UpgradeResponse.java9
5 files changed, 109 insertions, 8 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
index 41077e3b4ec..7cff20a20d1 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
@@ -28,6 +28,7 @@ import com.yahoo.vespa.hosted.provision.node.filter.StateFilter;
import com.yahoo.vespa.hosted.provision.persistence.CuratorDatabaseClient;
import com.yahoo.vespa.hosted.provision.persistence.DnsNameResolver;
import com.yahoo.vespa.hosted.provision.persistence.NameResolver;
+import com.yahoo.vespa.hosted.provision.provisioning.DockerImages;
import com.yahoo.vespa.hosted.provision.provisioning.FirmwareChecks;
import com.yahoo.vespa.hosted.provision.provisioning.OsVersions;
import com.yahoo.vespa.hosted.provision.restapi.v2.NotFoundException;
@@ -83,9 +84,9 @@ public class NodeRepository extends AbstractComponent {
private final Zone zone;
private final NodeFlavors flavors;
private final NameResolver nameResolver;
- private final DockerImage dockerImage;
private final OsVersions osVersions;
private final FirmwareChecks firmwareChecks;
+ private final DockerImages dockerImages;
private final Flags flags;
/**
@@ -108,9 +109,9 @@ public class NodeRepository extends AbstractComponent {
this.clock = clock;
this.flavors = flavors;
this.nameResolver = nameResolver;
- this.dockerImage = dockerImage;
this.osVersions = new OsVersions(this.db);
this.firmwareChecks = new FirmwareChecks(db, clock);
+ this.dockerImages = new DockerImages(db, dockerImage);
this.flags = new Flags(this.db);
// read and write all nodes to make sure they are stored in the latest version of the serialized format
@@ -122,7 +123,7 @@ public class NodeRepository extends AbstractComponent {
public CuratorDatabaseClient database() { return db; }
/** Returns the Docker image to use for nodes in this */
- public DockerImage dockerImage() { return dockerImage; }
+ public DockerImage dockerImage(NodeType nodeType) { return dockerImages.dockerImageFor(nodeType); }
/** @return The name resolver used to resolve hostname and ip addresses */
public NameResolver nameResolver() { return nameResolver; }
@@ -133,6 +134,9 @@ public class NodeRepository extends AbstractComponent {
/** Returns the status of firmware checks for hosts managed by this. */
public FirmwareChecks firmwareChecks() { return firmwareChecks; }
+ /** Returns the docker images to use for nodes in this. */
+ public DockerImages dockerImages() { return dockerImages; }
+
/** Returns feature flags of this node repository */
public Flags flags() {
return flags;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerImages.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerImages.java
new file mode 100644
index 00000000000..5e0e12dd62c
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerImages.java
@@ -0,0 +1,80 @@
+package com.yahoo.vespa.hosted.provision.provisioning;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.config.provision.DockerImage;
+import com.yahoo.config.provision.NodeType;
+import com.yahoo.vespa.curator.Lock;
+import com.yahoo.vespa.hosted.provision.persistence.CuratorDatabaseClient;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+/**
+ * Multithread safe class to get and set docker images for given host types.
+ *
+ * @author freva
+ */
+public class DockerImages {
+
+ private static final Duration defaultCacheTtl = Duration.ofMinutes(1);
+ private static final Logger log = Logger.getLogger(DockerImages.class.getName());
+
+ private final CuratorDatabaseClient db;
+ private final DockerImage defaultImage;
+ private final Duration cacheTtl;
+
+ /**
+ * Docker image is read on every request to /nodes/v2/node/[fqdn]. Cache current getDockerImages to avoid
+ * unnecessary ZK reads. When getDockerImages change, some nodes may need to wait for TTL until they see the new target,
+ * this is fine.
+ */
+ private volatile Supplier<Map<NodeType, DockerImage>> dockerImages;
+
+ public DockerImages(CuratorDatabaseClient db, DockerImage defaultImage) {
+ this(db, defaultImage, defaultCacheTtl);
+ }
+
+ DockerImages(CuratorDatabaseClient db, DockerImage defaultImage, Duration cacheTtl) {
+ this.db = db;
+ this.defaultImage = defaultImage;
+ this.cacheTtl = cacheTtl;
+ createCache();
+ }
+
+ private void createCache() {
+ this.dockerImages = Suppliers.memoizeWithExpiration(() -> Collections.unmodifiableMap(db.readDockerImages()),
+ cacheTtl.toMillis(), TimeUnit.MILLISECONDS);
+ }
+
+ /** Returns the current docker images for each node type */
+ public Map<NodeType, DockerImage> getDockerImages() {
+ return dockerImages.get();
+ }
+
+ /** Returns the current docker image for given node type, or default */
+ public DockerImage dockerImageFor(NodeType type) {
+ return getDockerImages().getOrDefault(type, defaultImage);
+ }
+
+ /** Set the docker image for nodes of given type */
+ public void setDockerImage(NodeType nodeType, Optional<DockerImage> dockerImage) {
+ if (nodeType.isDockerHost()) {
+ throw new IllegalArgumentException("Setting docker image for " + nodeType + " nodes is unsupported");
+ }
+ try (Lock lock = db.lockDockerImages()) {
+ Map<NodeType, DockerImage> dockerImages = db.readDockerImages();
+
+ dockerImage.ifPresentOrElse(image -> dockerImages.put(nodeType, image), () -> dockerImages.remove(nodeType));
+ db.writeDockerImages(dockerImages);
+ createCache(); // Throw away current cache
+ log.info("Set docker image for " + nodeType + " nodes to " + dockerImage.map(DockerImage::asString).orElse(null));
+ }
+ }
+
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java
index 3ce24f73b2c..9c3e83b9f5a 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.restapi.v2;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.HostFilter;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeFlavors;
@@ -106,7 +107,7 @@ public class NodesApiHandler extends LoggingRequestHandler {
if (path.startsWith("/nodes/v2/acl/")) return new NodeAclResponse(request, nodeRepository);
if (path.equals( "/nodes/v2/command/")) return ResourcesResponse.fromStrings(request.getUri(), "restart", "reboot");
if (path.equals( "/nodes/v2/maintenance/")) return new JobsResponse(maintenance.jobControl());
- if (path.equals( "/nodes/v2/upgrade/")) return new UpgradeResponse(maintenance.infrastructureVersions(), nodeRepository.osVersions());
+ if (path.equals( "/nodes/v2/upgrade/")) return new UpgradeResponse(maintenance.infrastructureVersions(), nodeRepository.osVersions(), nodeRepository.dockerImages());
if (path.equals( "/nodes/v2/flags/")) return new FlagsResponse(nodeRepository.flags().list());
throw new NotFoundException("Nothing at path '" + path + "'");
}
@@ -308,6 +309,7 @@ public class NodesApiHandler extends LoggingRequestHandler {
boolean force = inspector.field("force").asBool();
Inspector versionField = inspector.field("version");
Inspector osVersionField = inspector.field("osVersion");
+ Inspector dockerImageField = inspector.field("dockerImage");
if (versionField.valid()) {
Version version = Version.fromString(versionField.asString());
@@ -327,8 +329,16 @@ public class NodesApiHandler extends LoggingRequestHandler {
}
}
+ if (dockerImageField.valid()) {
+ Optional<DockerImage> dockerImage = Optional.of(dockerImageField.asString())
+ .filter(s -> !s.isEmpty())
+ .map(DockerImage::fromString);
+ nodeRepository.dockerImages().setDockerImage(nodeType, dockerImage);
+ messageParts.add("docker image to " + dockerImage.map(DockerImage::asString).orElse(null));
+ }
+
if (messageParts.isEmpty()) {
- throw new IllegalArgumentException("At least one of 'version' and 'osVersion' must be set");
+ throw new IllegalArgumentException("At least one of 'version', 'osVersion' or 'dockerImage' must be set");
}
return new MessageResponse("Set " + String.join(", ", messageParts) +
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java
index a0ecb063618..dfb6004e8c8 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java
@@ -160,7 +160,7 @@ class NodesResponse extends HttpResponse {
toSlime(allocation.membership(), object.setObject("membership"));
object.setLong("restartGeneration", allocation.restartGeneration().wanted());
object.setLong("currentRestartGeneration", allocation.restartGeneration().current());
- object.setString("wantedDockerImage", nodeRepository.dockerImage().withTag(allocation.membership().cluster().vespaVersion()).asString());
+ object.setString("wantedDockerImage", nodeRepository.dockerImage(node.type()).withTag(allocation.membership().cluster().vespaVersion()).asString());
object.setString("wantedVespaVersion", allocation.membership().cluster().vespaVersion().toFullString());
allocation.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray("networkPorts")));
orchestrator.apply(new HostName(node.hostname()))
@@ -219,7 +219,7 @@ class NodesResponse extends HttpResponse {
.or(() -> Optional.of(node)
.filter(n -> n.type().isDockerHost())
.flatMap(n -> n.status().vespaVersion()
- .map(version -> nodeRepository.dockerImages().dockerImage().withTag(version))));
+ .map(version -> nodeRepository.dockerImages().dockerImageFor(n.type()).withTag(version))));
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/UpgradeResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/UpgradeResponse.java
index 392cba7baa9..87d7944f040 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/UpgradeResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/UpgradeResponse.java
@@ -6,6 +6,7 @@ import com.yahoo.slime.Cursor;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.provision.maintenance.InfrastructureVersions;
+import com.yahoo.vespa.hosted.provision.provisioning.DockerImages;
import com.yahoo.vespa.hosted.provision.provisioning.OsVersions;
import java.io.IOException;
@@ -20,11 +21,13 @@ public class UpgradeResponse extends HttpResponse {
private final InfrastructureVersions infrastructureVersions;
private final OsVersions osVersions;
+ private final DockerImages dockerImages;
- public UpgradeResponse(InfrastructureVersions infrastructureVersions, OsVersions osVersions) {
+ public UpgradeResponse(InfrastructureVersions infrastructureVersions, OsVersions osVersions, DockerImages dockerImages) {
super(200);
this.infrastructureVersions = infrastructureVersions;
this.osVersions = osVersions;
+ this.dockerImages = dockerImages;
}
@Override
@@ -38,6 +41,10 @@ public class UpgradeResponse extends HttpResponse {
Cursor osVersionsObject = root.setObject("osVersions");
osVersions.targets().forEach((nodeType, version) -> osVersionsObject.setString(nodeType.name(), version.toFullString()));
+
+ Cursor dockerImagesObject = root.setObject("dockerImages");
+ dockerImages.getDockerImages().forEach((nodeType, image) -> dockerImagesObject.setString(nodeType.name(), image.asString()));
+
new JsonFormat(true).encode(stream, slime);
}