summaryrefslogtreecommitdiffstats
path: root/node-repository/src/main
diff options
context:
space:
mode:
authorHÃ¥kon Hallingstad <hakon.hallingstad@gmail.com>2023-02-27 09:21:27 +0100
committerGitHub <noreply@github.com>2023-02-27 09:21:27 +0100
commit538d6baedd5f716e83e7a7f2ada96edfdc3fa08c (patch)
tree89c90a21a4cdd99400da07a86b2ebd2a9d4806a7 /node-repository/src/main
parent04e33a7499b3d263ed73fcc6569a7c011402aafe (diff)
parent60b28199f48d150fb0734cde4a20fd2f7a7fab0b (diff)
Merge pull request #26178 from vespa-engine/freva/archive-uris
Allow patching account archive URIs in node-repo
Diffstat (limited to 'node-repository/src/main')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/archive/ArchiveUriManager.java88
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/archive/ArchiveUris.java45
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ArchiveUriSerializer.java56
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java13
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/TenantArchiveUriSerializer.java45
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ArchiveUris.java94
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ArchiveResponse.java20
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java14
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java8
11 files changed, 232 insertions, 161 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 fd75644d914..510c4041efb 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,7 +28,7 @@ import com.yahoo.vespa.hosted.provision.persistence.CuratorDb;
import com.yahoo.vespa.hosted.provision.persistence.DnsNameResolver;
import com.yahoo.vespa.hosted.provision.persistence.JobControlFlags;
import com.yahoo.vespa.hosted.provision.persistence.NameResolver;
-import com.yahoo.vespa.hosted.provision.provisioning.ArchiveUris;
+import com.yahoo.vespa.hosted.provision.archive.ArchiveUriManager;
import com.yahoo.vespa.hosted.provision.provisioning.ContainerImages;
import com.yahoo.vespa.hosted.provision.provisioning.FirmwareChecks;
import com.yahoo.vespa.hosted.provision.provisioning.HostResourcesCalculator;
@@ -57,7 +57,7 @@ public class NodeRepository extends AbstractComponent {
private final InfrastructureVersions infrastructureVersions;
private final FirmwareChecks firmwareChecks;
private final ContainerImages containerImages;
- private final ArchiveUris archiveUris;
+ private final ArchiveUriManager archiveUriManager;
private final JobControl jobControl;
private final Applications applications;
private final LoadBalancers loadBalancers;
@@ -134,7 +134,7 @@ public class NodeRepository extends AbstractComponent {
this.infrastructureVersions = new InfrastructureVersions(db);
this.firmwareChecks = new FirmwareChecks(db, clock);
this.containerImages = new ContainerImages(containerImage, tenantContainerImage, tenantGpuContainerImage);
- this.archiveUris = new ArchiveUris(db, zone);
+ this.archiveUriManager = new ArchiveUriManager(db, zone);
this.jobControl = new JobControl(new JobControlFlags(db, flagSource));
this.loadBalancers = new LoadBalancers(db);
this.metricsDb = metricsDb;
@@ -166,7 +166,7 @@ public class NodeRepository extends AbstractComponent {
public ContainerImages containerImages() { return containerImages; }
/** Returns the archive URIs to use for nodes in this. */
- public ArchiveUris archiveUris() { return archiveUris; }
+ public ArchiveUriManager archiveUriManager() { return archiveUriManager; }
/** Returns the status of maintenance jobs managed by this. */
public JobControl jobControl() { return jobControl; }
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/archive/ArchiveUriManager.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/archive/ArchiveUriManager.java
new file mode 100644
index 00000000000..27488e4027c
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/archive/ArchiveUriManager.java
@@ -0,0 +1,88 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.archive;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.CloudAccount;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.lang.CachedSupplier;
+import com.yahoo.vespa.curator.Lock;
+import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.persistence.CuratorDb;
+
+import java.time.Duration;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.logging.Logger;
+
+/**
+ * Thread safe class to get and set archive URI for given account and tenants.
+ *
+ * @author freva
+ */
+public class ArchiveUriManager {
+
+ private static final Logger log = Logger.getLogger(ArchiveUriManager.class.getName());
+ private static final Duration cacheTtl = Duration.ofMinutes(1);
+
+ private final CuratorDb db;
+ private final Zone zone;
+ private final CachedSupplier<ArchiveUris> archiveUris;
+
+ public ArchiveUriManager(CuratorDb db, Zone zone) {
+ this.db = db;
+ this.zone = zone;
+ this.archiveUris = new CachedSupplier<>(db::readArchiveUris, cacheTtl);
+ }
+
+ public ArchiveUris archiveUris() {
+ return archiveUris.get();
+ }
+
+ /** Returns the archive URI to use for given node */
+ public Optional<String> archiveUriFor(Node node) {
+ if (node.allocation().isEmpty()) return Optional.empty();
+ ApplicationId app = node.allocation().get().owner();
+
+ return Optional.ofNullable(node.cloudAccount().isEnclave(zone) ?
+ archiveUris.get().accountArchiveUris().get(node.cloudAccount()) :
+ archiveUris.get().tenantArchiveUris().get(app.tenant()))
+ .map(uri -> {
+ StringBuilder sb = new StringBuilder(100).append(uri)
+ .append(app.application().value()).append('/')
+ .append(app.instance().value()).append('/')
+ .append(node.allocation().get().membership().cluster().id().value()).append('/');
+
+ for (char c: node.hostname().toCharArray()) {
+ if (c == '.') break;
+ sb.append(c);
+ }
+
+ return sb.append('/').toString();
+ });
+ }
+
+ /** Set (or remove, if archiveURI is empty) archive URI to use for given tenant */
+ public void setArchiveUri(TenantName tenant, Optional<String> archiveUri) {
+ setArchiveUri(au -> au.with(tenant, archiveUri));
+ }
+
+ /** Set (or remove, if archiveURI is empty) archive URI to use for given account */
+ public void setArchiveUri(CloudAccount account, Optional<String> archiveUri) {
+ if (!account.isEnclave(zone) || account.isUnspecified())
+ throw new IllegalArgumentException("Cannot set archive URI for non-enclave account: " + account);
+ setArchiveUri(au -> au.with(account, archiveUri));
+ }
+
+ private void setArchiveUri(Function<ArchiveUris, ArchiveUris> mapper) {
+ try (Lock lock = db.lockArchiveUris()) {
+ ArchiveUris archiveUris = db.readArchiveUris();
+ ArchiveUris updated = mapper.apply(archiveUris);
+ if (archiveUris.equals(updated)) return; // No change
+
+ db.writeArchiveUris(updated);
+ this.archiveUris.invalidate(); // Throw away current cache
+ }
+ }
+
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/archive/ArchiveUris.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/archive/ArchiveUris.java
new file mode 100644
index 00000000000..7ec9bd36744
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/archive/ArchiveUris.java
@@ -0,0 +1,45 @@
+package com.yahoo.vespa.hosted.provision.archive;
+
+import com.yahoo.config.provision.CloudAccount;
+import com.yahoo.config.provision.TenantName;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+public record ArchiveUris(Map<TenantName, String> tenantArchiveUris, Map<CloudAccount, String> accountArchiveUris) {
+ private static final Pattern validUriPattern = Pattern.compile("[a-z0-9]+://(?:(?:[a-z0-9]+(?:[-_][a-z0-9.]+)*)+/)+");
+
+ public ArchiveUris(Map<TenantName, String> tenantArchiveUris, Map<CloudAccount, String> accountArchiveUris) {
+ this.tenantArchiveUris = Map.copyOf(tenantArchiveUris);
+ this.accountArchiveUris = Map.copyOf(accountArchiveUris);
+ }
+
+ public ArchiveUris with(TenantName tenant, Optional<String> archiveUri) {
+ Map<TenantName, String> updated = updateIfChanged(tenantArchiveUris, tenant, archiveUri);
+ if (updated == tenantArchiveUris) return this;
+ return new ArchiveUris(updated, accountArchiveUris);
+ }
+
+ public ArchiveUris with(CloudAccount account, Optional<String> archiveUri) {
+ Map<CloudAccount, String> updated = updateIfChanged(accountArchiveUris, account, archiveUri);
+ if (updated == accountArchiveUris) return this;
+ return new ArchiveUris(tenantArchiveUris, updated);
+ }
+
+ private static <T> Map<T, String> updateIfChanged(Map<T, String> current, T key, Optional<String> archiveUri) {
+ archiveUri = archiveUri.map(ArchiveUris::normalizeUri);
+ if (Optional.ofNullable(current.get(key)).equals(archiveUri)) return current;
+ Map<T, String> updated = new HashMap<>(current);
+ archiveUri.ifPresentOrElse(uri -> updated.put(key, uri), () -> updated.remove(key));
+ return updated;
+ }
+
+ static String normalizeUri(String uri) {
+ if (!uri.endsWith("/")) uri = uri + "/";
+ if (!validUriPattern.matcher(uri).matches())
+ throw new IllegalArgumentException("Invalid archive URI: " + uri);
+ return uri;
+ }
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ArchiveUriSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ArchiveUriSerializer.java
new file mode 100644
index 00000000000..3bf37816dd4
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ArchiveUriSerializer.java
@@ -0,0 +1,56 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.persistence;
+
+import com.yahoo.config.provision.CloudAccount;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.ObjectTraverser;
+import com.yahoo.slime.Slime;
+import com.yahoo.slime.SlimeUtils;
+import com.yahoo.vespa.hosted.provision.archive.ArchiveUris;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Serializer for archive URIs that are set per tenant and per account.
+ *
+ * @author freva
+ */
+public class ArchiveUriSerializer {
+
+ private ArchiveUriSerializer() {}
+
+ public static byte[] toJson(ArchiveUris archiveUris) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+
+ Cursor tenantObject = root.setObject("tenant");
+ archiveUris.tenantArchiveUris().forEach((tenant, uri) -> tenantObject.setString(tenant.value(), uri));
+
+ Cursor accountObject = root.setObject("account");
+ archiveUris.accountArchiveUris().forEach((account, uri) -> accountObject.setString(account.value(), uri));
+
+ try {
+ return SlimeUtils.toJsonBytes(slime);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public static ArchiveUris fromJson(byte[] data) {
+ Inspector inspector = SlimeUtils.jsonToSlime(data).get();
+
+ Map<TenantName, String> tenantArchiveUris = new HashMap<>();
+ inspector.field("tenant").traverse((ObjectTraverser) (key, value) -> tenantArchiveUris.put(TenantName.from(key), value.asString()));
+
+ Map<CloudAccount, String> accountArchiveUris = new HashMap<>();
+ inspector.field("account").traverse((ObjectTraverser) (key, value) -> accountArchiveUris.put(CloudAccount.from(key), value.asString()));
+
+ return new ArchiveUris(tenantArchiveUris, accountArchiveUris);
+ }
+
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
index c25dfe9f1e2..5aea6858f60 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
@@ -10,7 +10,6 @@ import com.yahoo.config.provision.ApplicationTransaction;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.config.provision.TenantName;
import com.yahoo.path.Path;
import com.yahoo.transaction.NestedTransaction;
import com.yahoo.transaction.Transaction;
@@ -21,6 +20,7 @@ import com.yahoo.vespa.curator.transaction.CuratorOperations;
import com.yahoo.vespa.curator.transaction.CuratorTransaction;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.applications.Application;
+import com.yahoo.vespa.hosted.provision.archive.ArchiveUris;
import com.yahoo.vespa.hosted.provision.lb.LoadBalancer;
import com.yahoo.vespa.hosted.provision.lb.LoadBalancerId;
import com.yahoo.vespa.hosted.provision.node.Agent;
@@ -70,7 +70,7 @@ public class CuratorDb {
private static final Path infrastructureVersionsPath = root.append("infrastructureVersions");
private static final Path osVersionsPath = root.append("osVersions");
private static final Path firmwareCheckPath = root.append("firmwareCheck");
- private static final Path archiveUrisPath = root.append("archiveUris");
+ private static final Path archiveUrisPath = root.append("archiveUri");
private static final Duration defaultLockTimeout = Duration.ofMinutes(1);
@@ -102,6 +102,7 @@ public class CuratorDb {
db.create(archiveUrisPath);
db.create(loadBalancersPath);
provisionIndexCounter.initialize(100);
+ CuratorOperations.delete(root.append("archiveUris").toString()); // TODO (freva): March 2023
}
/** Adds a set of nodes. Rollbacks/fails transaction if any node is not in the expected state. */
@@ -375,16 +376,16 @@ public class CuratorDb {
// Archive URIs -----------------------------------------------------------
- public void writeArchiveUris(Map<TenantName, String> archiveUris) {
- byte[] data = TenantArchiveUriSerializer.toJson(archiveUris);
+ public void writeArchiveUris(ArchiveUris archiveUris) {
+ byte[] data = ArchiveUriSerializer.toJson(archiveUris);
NestedTransaction transaction = new NestedTransaction();
CuratorTransaction curatorTransaction = db.newCuratorTransactionIn(transaction);
curatorTransaction.add(CuratorOperations.setData(archiveUrisPath.getAbsolute(), data));
transaction.commit();
}
- public Map<TenantName, String> readArchiveUris() {
- return read(archiveUrisPath, TenantArchiveUriSerializer::fromJson).orElseGet(Map::of);
+ public ArchiveUris readArchiveUris() {
+ return read(archiveUrisPath, ArchiveUriSerializer::fromJson).orElseGet(() -> new ArchiveUris(Map.of(), Map.of()));
}
public Lock lockArchiveUris() {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/TenantArchiveUriSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/TenantArchiveUriSerializer.java
deleted file mode 100644
index d381d25704a..00000000000
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/TenantArchiveUriSerializer.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.provision.persistence;
-
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Inspector;
-import com.yahoo.slime.ObjectTraverser;
-import com.yahoo.slime.Slime;
-import com.yahoo.slime.SlimeUtils;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * Serializer for archive URIs that are set per tenant.
- *
- * @author freva
- */
-public class TenantArchiveUriSerializer {
-
- private TenantArchiveUriSerializer() {}
-
- public static byte[] toJson(Map<TenantName, String> archiveUrisByTenantName) {
- Slime slime = new Slime();
- Cursor object = slime.setObject();
- archiveUrisByTenantName.forEach((tenantName, archiveUri) ->
- object.setString(tenantName.value(), archiveUri));
- try {
- return SlimeUtils.toJsonBytes(slime);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public static Map<TenantName, String> fromJson(byte[] data) {
- Map<TenantName, String> archiveUrisByTenantName = new TreeMap<>(); // Use TreeMap to sort by tenant name
- Inspector inspector = SlimeUtils.jsonToSlime(data).get();
- inspector.traverse((ObjectTraverser) (key, value) ->
- archiveUrisByTenantName.put(TenantName.from(key), value.asString()));
- return archiveUrisByTenantName;
- }
-
-}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ArchiveUris.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ArchiveUris.java
deleted file mode 100644
index 285365a9c6d..00000000000
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ArchiveUris.java
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.provision.provisioning;
-
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.config.provision.Zone;
-import com.yahoo.lang.CachedSupplier;
-import com.yahoo.vespa.curator.Lock;
-import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.node.Allocation;
-import com.yahoo.vespa.hosted.provision.persistence.CuratorDb;
-
-import java.time.Duration;
-import java.util.Map;
-import java.util.Optional;
-import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-/**
- * Thread safe class to get and set archive URI for given tenants. Archive URIs are stored in ZooKeeper so that
- * nodes within the same tenant have the same archive URI from all the config servers.
- *
- * @author freva
- */
-public class ArchiveUris {
-
- private static final Logger log = Logger.getLogger(ArchiveUris.class.getName());
- private static final Pattern validUriPattern = Pattern.compile("[a-z0-9]+://(?:(?:[a-z0-9]+(?:[-_][a-z0-9.]+)*)+/)+");
- private static final Duration cacheTtl = Duration.ofMinutes(1);
-
- private final CuratorDb db;
- private final CachedSupplier<Map<TenantName, String>> archiveUris;
- private final Zone zone;
-
- public ArchiveUris(CuratorDb db, Zone zone) {
- this.db = db;
- this.archiveUris = new CachedSupplier<>(db::readArchiveUris, cacheTtl);
- this.zone = zone;
- }
-
- /** Returns the current archive URI for each tenant */
- public Map<TenantName, String> getArchiveUris() {
- return archiveUris.get();
- }
-
- /** Returns the archive URI to use for given tenant */
- private Optional<String> archiveUriFor(TenantName tenant) {
- return Optional.ofNullable(archiveUris.get().get(tenant));
- }
-
- /** Returns the archive URI to use for given node */
- public Optional<String> archiveUriFor(Node node) {
- if (node.cloudAccount().isEnclave(zone)) return Optional.empty(); // TODO (freva): Implement for exclave
-
- return node.allocation().map(Allocation::owner)
- .flatMap(app -> archiveUriFor(app.tenant())
- .map(uri -> {
- StringBuilder sb = new StringBuilder(100).append(uri)
- .append(app.application().value()).append('/')
- .append(app.instance().value()).append('/')
- .append(node.allocation().get().membership().cluster().id().value()).append('/');
-
- for (char c: node.hostname().toCharArray()) {
- if (c == '.') break;
- sb.append(c);
- }
-
- return sb.append('/').toString();
- }));
- }
-
- /** Set (or remove, if archiveURI is empty) archive URI to use for given tenant */
- public void setArchiveUri(TenantName tenant, Optional<String> archiveUri) {
- try (Lock lock = db.lockArchiveUris()) {
- Map<TenantName, String> archiveUris = new TreeMap<>(db.readArchiveUris());
- if (Optional.ofNullable(archiveUris.get(tenant)).equals(archiveUri)) return; // No change
-
- archiveUri.map(ArchiveUris::normalizeUri).ifPresentOrElse(uri -> archiveUris.put(tenant, uri),
- () -> archiveUris.remove(tenant));
- db.writeArchiveUris(archiveUris);
- this.archiveUris.invalidate(); // Throw away current cache
- log.log(Level.FINE, () -> archiveUri.map(s -> "Set archive URI for " + tenant + " to " + s)
- .orElseGet(() -> "Remove archive URI for " + tenant));
- }
- }
-
- static String normalizeUri(String uri) {
- if (!uri.endsWith("/")) uri = uri + "/";
- if (!validUriPattern.matcher(uri).matches())
- throw new IllegalArgumentException("Invalid archive URI: " + uri);
- return uri;
- }
-}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ArchiveResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ArchiveResponse.java
index 3cff3c5e05f..84c82d314c9 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ArchiveResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ArchiveResponse.java
@@ -4,6 +4,9 @@ package com.yahoo.vespa.hosted.provision.restapi;
import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.slime.Cursor;
import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.archive.ArchiveUris;
+
+import java.util.Map;
/**
* Returns tenant archive URIs.
@@ -13,11 +16,22 @@ import com.yahoo.vespa.hosted.provision.NodeRepository;
public class ArchiveResponse extends SlimeJsonResponse {
public ArchiveResponse(NodeRepository nodeRepository) {
+ ArchiveUris archiveUris = nodeRepository.archiveUriManager().archiveUris();
Cursor archivesArray = slime.setObject().setArray("archives");
- nodeRepository.archiveUris().getArchiveUris().forEach((tenant, uri) -> {
+
+ archiveUris.tenantArchiveUris().entrySet().stream()
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(entry -> {
+ Cursor archiveObject = archivesArray.addObject();
+ archiveObject.setString("tenant", entry.getKey().value());
+ archiveObject.setString("uri", entry.getValue());
+ });
+ archiveUris.accountArchiveUris().entrySet().stream()
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(entry -> {
Cursor archiveObject = archivesArray.addObject();
- archiveObject.setString("tenant", tenant.value());
- archiveObject.setString("uri", uri);
+ archiveObject.setString("account", entry.getKey().value());
+ archiveObject.setString("uri", entry.getValue());
});
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
index 49e6265b6da..6bf07077c81 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
@@ -185,7 +185,7 @@ class NodesResponse extends SlimeJsonResponse {
node.reports().toSlime(object, "reports");
node.modelName().ifPresent(modelName -> object.setString("modelName", modelName));
node.switchHostname().ifPresent(switchHostname -> object.setString("switchHostname", switchHostname));
- nodeRepository.archiveUris().archiveUriFor(node).ifPresent(uri -> object.setString("archiveUri", uri));
+ nodeRepository.archiveUriManager().archiveUriFor(node).ifPresent(uri -> object.setString("archiveUri", uri));
trustedCertsToSlime(node.trustedCertificates(), object);
if (!node.cloudAccount().isUnspecified()) {
object.setString("cloudAccount", node.cloudAccount().value());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
index 543901621d0..0fbe912812e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
@@ -186,9 +186,9 @@ public class NodesV2ApiHandler extends ThreadedHttpRequestHandler {
return new MessageResponse("Updated " + patcher.application());
}
}
- else if (path.matches("/nodes/v2/archive/{tenant}")) {
+ else if (path.matches("/nodes/v2/archive/account/{key}") || path.matches("/nodes/v2/archive/tenant/{key}") || path.matches("/nodes/v2/archive/{key}") /* TODO (freva): Remove March 2023 */) {
String uri = requiredField(toSlime(request), "uri", Inspector::asString);
- return setTenantArchiveUri(path.get("tenant"), Optional.of(uri));
+ return setArchiveUri(path.get("key"), Optional.of(uri), !path.getPath().segments().get(3).equals("account"));
}
else if (path.matches("/nodes/v2/upgrade/{nodeType}")) {
return setTargetVersions(path.get("nodeType"), toSlime(request));
@@ -229,7 +229,8 @@ public class NodesV2ApiHandler extends ThreadedHttpRequestHandler {
private HttpResponse handleDELETE(HttpRequest request) {
Path path = new Path(request.getUri());
if (path.matches("/nodes/v2/node/{hostname}")) return deleteNode(path.get("hostname"));
- if (path.matches("/nodes/v2/archive/{tenant}")) return setTenantArchiveUri(path.get("tenant"), Optional.empty());
+ if (path.matches("/nodes/v2/archive/account/{key}") || path.matches("/nodes/v2/archive/tenant/{key}") || path.matches("/nodes/v2/archive/{key}") /* TODO (freva): Remove March 2023) */)
+ return setArchiveUri(path.get("key"), Optional.empty(), !path.getPath().segments().get(3).equals("account"));
if (path.matches("/nodes/v2/upgrade/firmware")) return cancelFirmwareCheckResponse();
throw new NotFoundException("Nothing at path '" + request.getUri().getPath() + "'");
@@ -422,9 +423,10 @@ public class NodesV2ApiHandler extends ThreadedHttpRequestHandler {
return new MessageResponse("Will request firmware checks on all hosts.");
}
- private HttpResponse setTenantArchiveUri(String tenant, Optional<String> archiveUri) {
- nodeRepository.archiveUris().setArchiveUri(TenantName.from(tenant), archiveUri);
- return new MessageResponse(archiveUri.map(a -> "Updated").orElse("Removed") + " archive URI for " + tenant);
+ private HttpResponse setArchiveUri(String key, Optional<String> archiveUri, boolean isTenant) {
+ if (isTenant) nodeRepository.archiveUriManager().setArchiveUri(TenantName.from(key), archiveUri);
+ else nodeRepository.archiveUriManager().setArchiveUri(CloudAccount.from(key), archiveUri);
+ return new MessageResponse(archiveUri.map(a -> "Updated").orElse("Removed") + " archive URI for " + key);
}
private static String hostnamesAsString(List<Node> nodes) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
index f0f85b6523f..4f88a10dff0 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.provision.testutils;
import com.yahoo.config.provision.CloudAccount;
+import com.yahoo.config.provision.SystemName;
/**
* For running NodeRepository API with some mocked data.
@@ -11,12 +12,15 @@ import com.yahoo.config.provision.CloudAccount;
*/
public class ContainerConfig {
- public static String servicesXmlV2(int port, CloudAccount cloudAccount) {
+ public static String servicesXmlV2(int port, SystemName systemName, CloudAccount cloudAccount) {
return """
<container version='1.0'>
<config name="container.handler.threadpool">
<maxthreads>20</maxthreads>
</config>
+ <config name="cloud.config.configserver">
+ <system>%s</system>
+ </config>
<config name="config.provisioning.cloud">
<account>%s</account>
</config>
@@ -47,7 +51,7 @@ public class ContainerConfig {
<server id='myServer' port='%s'/>
</http>
</container>
- """.formatted(cloudAccount.value(), port);
+ """.formatted(systemName.value(), cloudAccount.value(), port);
}
}