summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2020-12-02 11:16:47 +0100
committerMartin Polden <mpolden@mpolden.no>2020-12-02 12:17:02 +0100
commit3d33f25b291d4a4d7ba44847da85ef9ad1f5b62f (patch)
tree8750cbf8be4c4829f3aa9d45429de58df84e8d5d /controller-server
parent015ef9e6753931c0edffe80721e6647119950e9b (diff)
Add host switch updater
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/HostSwitchUpdater.java52
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java16
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/HostSwitchUpdaterTest.java73
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json3
6 files changed, 149 insertions, 3 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
index 82f37c9bc93..be4c889cd75 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java
@@ -52,6 +52,7 @@ public class ControllerMaintenance extends AbstractComponent {
private final ApplicationMetaDataGarbageCollector applicationMetaDataGarbageCollector;
private final HostRepairMaintainer hostRepairMaintainer;
private final ContainerImageExpirer containerImageExpirer;
+ private final HostSwitchUpdater hostSwitchUpdater;
@Inject
@SuppressWarnings("unused") // instantiated by Dependency Injection
@@ -81,6 +82,7 @@ public class ControllerMaintenance extends AbstractComponent {
applicationMetaDataGarbageCollector = new ApplicationMetaDataGarbageCollector(controller, intervals.applicationMetaDataGarbageCollector);
hostRepairMaintainer = new HostRepairMaintainer(controller, intervals.hostRepairMaintainer);
containerImageExpirer = new ContainerImageExpirer(controller, intervals.containerImageExpirer);
+ hostSwitchUpdater = new HostSwitchUpdater(controller, intervals.hostSwitchUpdater);
}
public Upgrader upgrader() { return upgrader; }
@@ -111,6 +113,7 @@ public class ControllerMaintenance extends AbstractComponent {
applicationMetaDataGarbageCollector.close();
hostRepairMaintainer.close();
containerImageExpirer.close();
+ hostSwitchUpdater.close();
}
/** Create one OS upgrader per cloud found in the zone registry of controller */
@@ -148,6 +151,7 @@ public class ControllerMaintenance extends AbstractComponent {
private final Duration applicationMetaDataGarbageCollector;
private final Duration hostRepairMaintainer;
private final Duration containerImageExpirer;
+ private final Duration hostSwitchUpdater;
public Intervals(SystemName system) {
this.system = Objects.requireNonNull(system);
@@ -170,6 +174,7 @@ public class ControllerMaintenance extends AbstractComponent {
this.applicationMetaDataGarbageCollector = duration(12, HOURS);
this.hostRepairMaintainer = duration(12, HOURS);
this.containerImageExpirer = duration(2, HOURS);
+ this.hostSwitchUpdater = duration(12, HOURS);
}
private Duration duration(long amount, TemporalUnit unit) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/HostSwitchUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/HostSwitchUpdater.java
new file mode 100644
index 00000000000..35b1407441e
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/HostSwitchUpdater.java
@@ -0,0 +1,52 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.maintenance;
+
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository;
+import com.yahoo.vespa.hosted.controller.api.integration.entity.NodeEntity;
+import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * Ensures that the switch information for all hosts is up to date.
+ *
+ * @author mpolden
+ */
+public class HostSwitchUpdater extends ControllerMaintainer {
+
+ private final NodeRepository nodeRepository;
+
+ public HostSwitchUpdater(Controller controller, Duration interval) {
+ super(controller, interval);
+ this.nodeRepository = controller.serviceRegistry().configServer().nodeRepository();
+ }
+
+ @Override
+ protected boolean maintain() {
+ Map<String, NodeEntity> nodeEntities = controller().serviceRegistry().entityService().listNodes().stream()
+ .collect(Collectors.toMap(NodeEntity::hostname,
+ Function.identity()));
+ for (var zone : controller().zoneRegistry().zones().controllerUpgraded().all().ids()) {
+ for (var node : nodeRepository.list(zone)) {
+ NodeEntity nodeEntity = nodeEntities.get(node.hostname().value());
+ if (!shouldUpdate(node, nodeEntity)) continue;
+
+ NodeRepositoryNode updatedNode = new NodeRepositoryNode();
+ updatedNode.setSwitchHostname(nodeEntity.switchHostname().orElse(null));
+ nodeRepository.patchNode(zone, node.hostname().value(), updatedNode);
+ }
+ }
+ return true;
+ }
+
+ private static boolean shouldUpdate(Node node, NodeEntity nodeEntity) {
+ if (nodeEntity == null) return false;
+ return !node.switchHostname().equals(nodeEntity.switchHostname());
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
index 61bd7858da2..ca478905893 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
@@ -41,6 +41,8 @@ public class NodeRepositoryMock implements NodeRepository {
private final Map<ZoneId, TargetVersions> targetVersions = new HashMap<>();
private final Map<Integer, Duration> osUpgradeBudgets = new HashMap<>();
+ private boolean allowPatching = false;
+
/** Add or update given nodes in zone */
public void putNodes(ZoneId zone, List<Node> nodes) {
nodeRepository.putIfAbsent(zone, new HashMap<>());
@@ -230,7 +232,14 @@ public class NodeRepositoryMock implements NodeRepository {
@Override
public void patchNode(ZoneId zoneId, String hostName, NodeRepositoryNode node) {
- throw new UnsupportedOperationException();
+ if (!allowPatching) throw new UnsupportedOperationException();
+ List<Node> existing = list(zoneId, List.of(HostName.from(hostName)));
+ if (existing.size() != 1) throw new IllegalArgumentException("Node " + hostName + " not found in " + zoneId);
+
+ // Note: Only supports switchHostname
+ Node newNode = new Node.Builder(existing.get(0)).switchHostname(node.getSwitchHostname())
+ .build();
+ putNodes(zoneId, newNode);
}
@Override
@@ -280,4 +289,9 @@ public class NodeRepositoryMock implements NodeRepository {
nodeRepository.get(zoneId).get(hostName).reports().put(reportId, report);
}
+ public NodeRepositoryMock allowPatching(boolean allowPatching) {
+ this.allowPatching = allowPatching;
+ return this;
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index ea31667d249..96eb4b39510 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -15,7 +15,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.aws.ResourceTagger;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingController;
import com.yahoo.vespa.hosted.controller.api.integration.billing.MockBillingController;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMock;
-import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
import com.yahoo.vespa.hosted.controller.api.integration.dns.MemoryNameService;
import com.yahoo.vespa.hosted.controller.api.integration.entity.MemoryEntityService;
import com.yahoo.vespa.hosted.controller.api.integration.organization.MockContactRetriever;
@@ -81,7 +80,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
}
@Override
- public ConfigServer configServer() {
+ public ConfigServerMock configServer() {
return configServerMock;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/HostSwitchUpdaterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/HostSwitchUpdaterTest.java
new file mode 100644
index 00000000000..1688141fe2d
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/HostSwitchUpdaterTest.java
@@ -0,0 +1,73 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.maintenance;
+
+import com.yahoo.vespa.hosted.controller.ControllerTester;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
+import com.yahoo.vespa.hosted.controller.api.integration.entity.NodeEntity;
+import org.junit.Test;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * @author mpolden
+ */
+public class HostSwitchUpdaterTest {
+
+ @Test
+ public void maintain() {
+ ControllerTester tester = new ControllerTester();
+ tester.serviceRegistry().configServer().nodeRepository().allowPatching(true);
+ addNodeEntities(tester);
+
+ // First iteration patches all hosts
+ HostSwitchUpdater maintainer = new HostSwitchUpdater(tester.controller(), Duration.ofDays(1));
+ maintainer.maintain();
+ List<Node> nodes = allNodes(tester);
+ assertFalse(nodes.isEmpty());
+ for (var node : nodes) {
+ assertEquals("Node " + node.hostname().value() + (node.type().isHost() ? " has" : " does not have")
+ + " switch hostname", node.type().isHost(), node.switchHostname().isPresent());
+ if (node.type().isHost()) {
+ assertEquals("tor-" + node.hostname().value(), node.switchHostname().get());
+ }
+ }
+
+ // Second iteration does not patch anything as all switch information is current
+ tester.serviceRegistry().configServer().nodeRepository().allowPatching(false);
+ maintainer.maintain();
+
+ // One host is moved to a different switch
+ Node host = allNodes(tester).stream().filter(node -> node.type().isHost()).findFirst().get();
+ String newSwitch = "tor2-" + host.hostname().value();
+ NodeEntity nodeEntity = new NodeEntity(host.hostname().value(), "", "", newSwitch);
+ tester.serviceRegistry().entityService().addNodeEntity(nodeEntity);
+
+ // Host is updated
+ tester.serviceRegistry().configServer().nodeRepository().allowPatching(true);
+ maintainer.maintain();
+ Node updatedHost = allNodes(tester).stream().filter(node -> node.hostname().equals(host.hostname())).findFirst().get();
+ assertEquals(newSwitch, updatedHost.switchHostname().get());
+ }
+
+ private static List<Node> allNodes(ControllerTester tester) {
+ List<Node> nodes = new ArrayList<>();
+ for (var zone : tester.zoneRegistry().zones().controllerUpgraded().all().ids()) {
+ nodes.addAll(tester.serviceRegistry().configServer().nodeRepository().list(zone));
+ }
+ return nodes;
+ }
+
+ private static void addNodeEntities(ControllerTester tester) {
+ for (var node : allNodes(tester)) {
+ if (!node.type().isHost()) continue;
+ NodeEntity nodeEntity = new NodeEntity(node.hostname().value(), "", "", "tor-" + node.hostname().value());
+ tester.serviceRegistry().entityService().addNodeEntity(nodeEntity);
+ }
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
index c1ee1489cd4..5ab087aab17 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
@@ -34,6 +34,9 @@
"name": "HostRepairMaintainer"
},
{
+ "name": "HostSwitchUpdater"
+ },
+ {
"name": "JobRunner"
},
{