summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2019-01-25 14:45:21 +0100
committerMartin Polden <mpolden@mpolden.no>2019-01-25 14:45:21 +0100
commit8d90a1fd5a0e6b08ab03ab9497e2508a8e15fcae (patch)
tree6b109f250b0237a1e35ad6c1e0f47b1c4ae47a69 /controller-server
parent01c8dd3582c3c86a7fab4699617ef9d1eec13681 (diff)
Simplify DNS record cleanup
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAlias.java93
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/loadbalancer/LoadBalancerName.java52
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainer.java147
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerMaintainer.java208
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java31
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializer.java52
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerNameSerializer.java56
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAliasTest.java33
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainerTest.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerMaintainerTest.java)87
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializerTest.java35
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerNameSerializerTest.java33
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json2
14 files changed, 425 insertions, 420 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
index 5d0b71a809b..130519335ce 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
@@ -10,6 +10,7 @@ import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
import com.yahoo.vespa.hosted.controller.api.integration.MetricsService.ApplicationMetrics;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId;
@@ -27,10 +28,12 @@ import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import java.time.Instant;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
+import java.util.stream.Collectors;
/**
* An application that has been locked for modification. Provides methods for modifying an application's fields.
@@ -248,8 +251,11 @@ public class LockedApplication {
outstandingChange, ownershipIssueId, owner, majorVersion, metrics, rotation, rotationStatus);
}
- public LockedApplication withDeploymentLoadBalancers(ZoneId zoneId, Map<ClusterSpec.Id, HostName> loadBalancers) {
- return with(deployments.get(zoneId).withLoadBalancers(loadBalancers));
+ public LockedApplication withLoadBalancersIn(ZoneId zoneId, List<LoadBalancer> loadBalancers) {
+ Map<ClusterSpec.Id, HostName> loadBalancersByCluster = loadBalancers.stream()
+ .collect(Collectors.toUnmodifiableMap(LoadBalancer::cluster,
+ LoadBalancer::hostname));
+ return with(deployments.get(zoneId).withLoadBalancers(loadBalancersByCluster));
}
/** Don't expose non-leaf sub-objects. */
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAlias.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAlias.java
new file mode 100644
index 00000000000..4e1c79248a9
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAlias.java
@@ -0,0 +1,93 @@
+// 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.controller.application;
+
+import com.google.common.base.Strings;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.HostName;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Represents a DNS alias for a load balancer.
+ *
+ * @author mortent
+ */
+public class LoadBalancerAlias {
+
+ private static final String ignoredEndpointPart = "default";
+ private final ApplicationId owner;
+ private final String id;
+ private final HostName alias;
+ private final HostName canonicalName;
+
+ public LoadBalancerAlias(ApplicationId owner, String id, HostName alias, HostName canonicalName) {
+ this.owner = Objects.requireNonNull(owner, "owner must be non-null");
+ this.id = Objects.requireNonNull(id, "id must be non-null");
+ this.alias = Objects.requireNonNull(alias, "alias must be non-null");
+ this.canonicalName = Objects.requireNonNull(canonicalName, "canonicalName must be non-null");
+ }
+
+ /** The application owning this */
+ public ApplicationId owner() {
+ return owner;
+ }
+
+ /** The ID of the DNS record represented by this */
+ public String id() {
+ return id;
+ }
+
+ /** This alias (lhs of the CNAME record) */
+ public HostName alias() {
+ return alias;
+ }
+
+ /** The canonical name of this (rhs of the CNAME record) */
+ public HostName canonicalName() {
+ return canonicalName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ LoadBalancerAlias that = (LoadBalancerAlias) o;
+ return Objects.equals(owner, that.owner) &&
+ Objects.equals(id, that.id) &&
+ Objects.equals(alias, that.alias) &&
+ Objects.equals(canonicalName, that.canonicalName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(owner, id, alias, canonicalName);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s: %s -> %s, owned by %s", id, alias, canonicalName, owner.toShortString());
+ }
+
+ public static String createAlias(ClusterSpec.Id clusterId, ApplicationId applicationId, ZoneId zoneId) {
+ List<String> parts = Arrays.asList(ignorePartIfDefault(clusterId.value()),
+ ignorePartIfDefault(applicationId.instance().value()),
+ applicationId.application().value(),
+ applicationId.tenant().value(),
+ zoneId.value(),
+ "vespa.oath.cloud"
+ );
+ return parts.stream()
+ .filter(s -> !Strings.isNullOrEmpty((s)))
+ .collect(Collectors.joining("--"));
+ }
+
+ private static String ignorePartIfDefault(String s) {
+ return ignoredEndpointPart.equalsIgnoreCase(s) ? "" : s;
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/loadbalancer/LoadBalancerName.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/loadbalancer/LoadBalancerName.java
deleted file mode 100644
index b2963a62eba..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/loadbalancer/LoadBalancerName.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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.controller.loadbalancer;
-
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordId;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
-
-import java.util.Objects;
-
-/**
- * Represents a pair of RecordId and RecordName
- *
- * @author mortent
- */
-public class LoadBalancerName {
- private final RecordId recordId;
- private final RecordName recordName;
-
- public LoadBalancerName(RecordId recordId, RecordName recordName) {
- this.recordId = recordId;
- this.recordName = recordName;
- }
-
- public RecordId recordId() {
- return recordId;
- }
-
- public RecordName recordName() {
- return recordName;
- }
-
- @Override
- public String toString() {
- return "LoadBalancerName{" +
- "recordId=" + recordId +
- ", recordName=" + recordName +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- LoadBalancerName that = (LoadBalancerName) o;
- return recordId.equals(that.recordId) &&
- recordName.equals(that.recordName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(recordId, recordName);
- }
-}
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 132b4a28514..b38f55826d6 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,7 +52,7 @@ public class ControllerMaintenance extends AbstractComponent {
private final JobRunner jobRunner;
private final ContactInformationMaintainer contactInformationMaintainer;
private final CostReportMaintainer costReportMaintainer;
- private final LoadBalancerMaintainer loadbalancerMaintainer;
+ private final LoadBalancerAliasMaintainer loadBalancerAliasMaintainer;
@SuppressWarnings("unused") // instantiated by Dependency Injection
public ControllerMaintenance(MaintainerConfig maintainerConfig, ApiAuthorityConfig apiAuthorityConfig, Controller controller, CuratorDb curator,
@@ -82,7 +82,7 @@ public class ControllerMaintenance extends AbstractComponent {
osVersionStatusUpdater = new OsVersionStatusUpdater(controller, maintenanceInterval, jobControl);
contactInformationMaintainer = new ContactInformationMaintainer(controller, Duration.ofHours(12), jobControl, contactRetriever);
costReportMaintainer = new CostReportMaintainer(controller, Duration.ofHours(2), reportConsumer, jobControl, nodeRepositoryClient, Clock.systemUTC(), selfHostedCostConfig);
- loadbalancerMaintainer = new LoadBalancerMaintainer(controller, Duration.ofMinutes(5), jobControl, nameService, curator);
+ loadBalancerAliasMaintainer = new LoadBalancerAliasMaintainer(controller, Duration.ofMinutes(5), jobControl, nameService, curator);
}
public Upgrader upgrader() { return upgrader; }
@@ -110,7 +110,7 @@ public class ControllerMaintenance extends AbstractComponent {
jobRunner.deconstruct();
contactInformationMaintainer.deconstruct();
costReportMaintainer.deconstruct();
- loadbalancerMaintainer.deconstruct();
+ loadBalancerAliasMaintainer.deconstruct();
}
/** Create one OS upgrader per cloud found in the zone registry of controller */
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainer.java
new file mode 100644
index 00000000000..6c5000e3e3d
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainer.java
@@ -0,0 +1,147 @@
+// 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.controller.maintenance;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.curator.Lock;
+import com.yahoo.vespa.hosted.controller.Application;
+import com.yahoo.vespa.hosted.controller.ApplicationController;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
+import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService;
+import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
+import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData;
+import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordId;
+import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.application.Deployment;
+import com.yahoo.vespa.hosted.controller.application.LoadBalancerAlias;
+import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ * Maintains DNS aliases for all load balancers in this system.
+ *
+ * @author mortent
+ */
+public class LoadBalancerAliasMaintainer extends Maintainer {
+
+ private static final Logger log = Logger.getLogger(LoadBalancerAliasMaintainer.class.getName());
+
+ private final NameService nameService;
+ private final CuratorDb db;
+ private final ApplicationController applications;
+
+ public LoadBalancerAliasMaintainer(Controller controller,
+ Duration interval,
+ JobControl jobControl,
+ NameService nameService,
+ CuratorDb db) {
+ super(controller, interval, jobControl);
+ this.nameService = nameService;
+ this.db = db;
+ this.applications = controller.applications();
+ }
+
+ @Override
+ protected void maintain() {
+ updateDnsRecords();
+ removeObsoleteDnsRecords();
+ }
+
+ /** Create DNS records for all exclusive load balancers */
+ private void updateDnsRecords() {
+ for (Application application : applications.asList()) {
+ for (ZoneId zone : application.deployments().keySet()) {
+ List<LoadBalancer> loadBalancers = findLoadBalancersIn(zone, application.id());
+ if (loadBalancers.isEmpty()) continue;
+
+ applications.lockIfPresent(application.id(), (locked) -> {
+ applications.store(locked.withLoadBalancersIn(zone, loadBalancers));
+ });
+
+ try (Lock lock = db.lockLoadBalancerAliases()) {
+ Set<LoadBalancerAlias> aliases = new LinkedHashSet<>(db.readLoadBalancerAliases(application.id()));
+ for (LoadBalancer loadBalancer : loadBalancers) {
+ try {
+ aliases.add(registerDnsAlias(application.id(), zone, loadBalancer));
+ } catch (Exception e) {
+ log.log(LogLevel.WARNING, "Failed to create or update DNS record for load balancer " +
+ loadBalancer.hostname() + ". Retrying in " + maintenanceInterval(),
+ e);
+ }
+ }
+ db.writeLoadBalancerAliases(application.id(), aliases);
+ }
+ }
+ }
+ }
+
+ /** Register DNS alias for given load balancer */
+ private LoadBalancerAlias registerDnsAlias(ApplicationId application, ZoneId zone, LoadBalancer loadBalancer) {
+ HostName alias = HostName.from(LoadBalancerAlias.createAlias(loadBalancer.cluster(), application, zone));
+ RecordName name = RecordName.from(alias.value());
+ RecordData data = RecordData.fqdn(loadBalancer.hostname().value());
+ Optional<Record> existingRecord = nameService.findRecord(Record.Type.CNAME, name);
+ RecordId id;
+ if(existingRecord.isPresent()) {
+ id = existingRecord.get().id();
+ nameService.updateRecord(existingRecord.get().id(), data);
+ } else {
+ id = nameService.createCname(name, data);
+ }
+ return new LoadBalancerAlias(application, id.asString(), alias, loadBalancer.hostname());
+ }
+
+ /** Find all load balancers assigned to application in given zone */
+ private List<LoadBalancer> findLoadBalancersIn(ZoneId zone, ApplicationId application) {
+ try {
+ return controller().applications().configServer().getLoadBalancers(new DeploymentId(application, zone));
+ } catch (Exception e) {
+ log.log(LogLevel.WARNING,
+ String.format("Got exception fetching load balancers for application: %s, in zone: %s. Retrying in %s",
+ application.toShortString(), zone.value(), maintenanceInterval()), e);
+ }
+ return Collections.emptyList();
+ }
+
+ /** Remove all DNS records that point to non-existing load balancers */
+ private void removeObsoleteDnsRecords() {
+ try (Lock lock = db.lockLoadBalancerAliases()) {
+ List<LoadBalancerAlias> removalCandidates = new ArrayList<>(db.readLoadBalancerAliases());
+ Set<HostName> activeLoadBalancers = controller().applications().asList().stream()
+ .map(Application::deployments)
+ .map(Map::values)
+ .flatMap(Collection::stream)
+ .map(Deployment::loadBalancers)
+ .map(Map::values)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toUnmodifiableSet());
+
+ // Remove any active load balancers
+ removalCandidates.removeIf(lb -> activeLoadBalancers.contains(lb.canonicalName()));
+ for (LoadBalancerAlias alias : removalCandidates) {
+ try {
+ nameService.removeRecord(new RecordId(alias.id()));
+ } catch (Exception e) {
+ log.log(LogLevel.WARNING, "Failed to remove DNS record with ID '" + alias.id() +
+ "'. Retrying in " + maintenanceInterval());
+ }
+ }
+ }
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerMaintainer.java
deleted file mode 100644
index 71b2482a650..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerMaintainer.java
+++ /dev/null
@@ -1,208 +0,0 @@
-// 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.controller.maintenance;
-
-import com.google.common.base.Strings;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ClusterSpec;
-import com.yahoo.config.provision.HostName;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.curator.Lock;
-import com.yahoo.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.LockedApplication;
-import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
-import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordId;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
-import com.yahoo.vespa.hosted.controller.application.Deployment;
-import com.yahoo.vespa.hosted.controller.loadbalancer.LoadBalancerName;
-import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * Maintains loadbalancer endpoints.
- * Reads load balancer information for each application in all zones and updates name service.
- *
- * @author mortent
- */
-public class LoadBalancerMaintainer extends Maintainer {
-
- private static final Logger log = Logger.getLogger(LoadBalancerMaintainer.class.getName());
- private static final String IGNORE_ENDPOINT_VALUE = "default";
-
- private final NameService nameService;
- private final CuratorDb curatorDb;
-
- public LoadBalancerMaintainer(Controller controller,
- Duration interval,
- JobControl jobControl,
- NameService nameService,
- CuratorDb curatorDb) {
- super(controller, interval, jobControl);
- this.nameService = nameService;
- this.curatorDb = curatorDb;
- }
-
- @Override
- protected void maintain() {
- // update application object with load balancer information
- controller().applications().asList().forEach(this::updateApplicationLoadBalancers);
-
- // Create or update cnames
- List<Application> applications = controller().applications().asList();
- Map<ApplicationId, List<LoadBalancerName>> applicationEndpointMap = applications.stream()
- .collect(Collectors.toMap(Application::id, this::registerLoadBalancerEndpoint));
-
- try (Lock lock = curatorDb.lockLoadBalancerNames()) {
- updatePersistedLoadBalancerNames(applicationEndpointMap);
- removeObsoleteLoadBalancerNames();
- }
- }
-
-
- private void removeObsoleteLoadBalancerNames() {
- Map<ApplicationId, List<LoadBalancerName>> persistedLoadBalancerNames = new HashMap<>(curatorDb.readLoadBalancerNames());
- Map<ApplicationId, List<LoadBalancerName>> result = new HashMap<>();
-
- Map<ApplicationId, List<String>> wantedLoadBalancers = new HashMap<>();
-
- for (Application application : controller().applications().asList()) {
- for (Map.Entry<ZoneId, Deployment> entry : application.deployments().entrySet()) {
- Map<ClusterSpec.Id, HostName> loadBalancers = entry.getValue().loadBalancers();
- List<String> loadBalancerNames = loadBalancers.keySet().stream()
- .map(cluster -> getEndpointName(cluster, application.id(), entry.getKey()))
- .collect(Collectors.toList());
- wantedLoadBalancers.merge(application.id(), loadBalancerNames, (v1, v2) ->
- Stream.concat(v1.stream(), v2.stream()).collect(Collectors.toList()));
- }
- }
-
- for (Map.Entry<ApplicationId, List<LoadBalancerName>> loadbalancerEntry : persistedLoadBalancerNames.entrySet()) {
- List<String> wanted = wantedLoadBalancers.getOrDefault(loadbalancerEntry.getKey(), Collections.emptyList());
- List<LoadBalancerName> current = loadbalancerEntry.getValue();
- List<LoadBalancerName> toBeRemoved = current.stream()
- .filter(lbname -> !wanted.contains(lbname.recordName().asString()))
- .collect(Collectors.toList());
- removeLoadBalancerNames(toBeRemoved);
- List<LoadBalancerName> resultingLoadBalancers = current.stream().filter(lb -> !toBeRemoved.contains(lb)).collect(Collectors.toList());
- result.put(loadbalancerEntry.getKey(), resultingLoadBalancers);
- }
-
- curatorDb.writeLoadBalancerNames(result);
- }
-
- private void updatePersistedLoadBalancerNames(Map<ApplicationId, List<LoadBalancerName>> applicationEndpointMap) {
- // Read current list of maintained load balancer endpoints
- Map<ApplicationId, List<LoadBalancerName>> existingApplicationEndpointList = curatorDb.readLoadBalancerNames();
-
- // Update ZK with new load balancer endpoints
- Map<ApplicationId, List<LoadBalancerName>> allCreated = new HashMap<>(applicationEndpointMap);
- existingApplicationEndpointList.forEach((k, v) -> allCreated.merge(k, v, (v1, v2) ->
- // Merge the two lists, removing duplicates
- List.copyOf(new LinkedHashSet<LoadBalancerName>(
- Stream.concat(v1.stream(), v2.stream()).collect(Collectors.toList()))
- )));
-
- curatorDb.writeLoadBalancerNames(allCreated);
-
- }
-
- private void removeLoadBalancerNames(List<LoadBalancerName> loadBalancerNames) {
- if(loadBalancerNames.isEmpty()) return;
- log.log(LogLevel.INFO, String.format("Removing %d Load Balancer names", loadBalancerNames.size()));
- for (LoadBalancerName loadBalancerName : loadBalancerNames) {
- nameService.removeRecord(loadBalancerName.recordId());
- }
- }
-
- private void updateApplicationLoadBalancers(Application application) {
- // Get a list of all load balancers for this applications (for all zones and clusters)
- Map<ZoneId, List<LoadBalancer>> zoneLoadBalancers = new HashMap<>();
- for (ZoneId zoneId : application.deployments().keySet()) {
- try {
- zoneLoadBalancers.put(zoneId, controller().applications().configServer().getLoadBalancers(new DeploymentId(application.id(), zoneId)));
- } catch (Exception e) {
- log.log(LogLevel.WARNING,
- String.format("Got exception fetching load balancers for application: %s, in zone: %s. Retrying in %s",
- application.id().toShortString(), zoneId.value(), maintenanceInterval()),
- e);
- }
- }
-
- // store the load balancers on the deployments
- controller().applications().lockIfPresent(application.id(), lockedApplication -> storeApplicationWithLoadBalancers(lockedApplication, zoneLoadBalancers));
- }
-
- private void storeApplicationWithLoadBalancers(LockedApplication lockedApplication, Map<ZoneId, List<LoadBalancer>> loadBalancers) {
- for (Map.Entry<ZoneId, List<LoadBalancer>> entry : loadBalancers.entrySet()) {
- Map<ClusterSpec.Id, HostName> loadbalancerClusterMap = entry.getValue().stream()
- .collect(Collectors.toMap(LoadBalancer::cluster, LoadBalancer::hostname));
- lockedApplication = lockedApplication.withDeploymentLoadBalancers(entry.getKey(), loadbalancerClusterMap);
-
- }
- controller().applications().store(lockedApplication);
- }
-
- private List<LoadBalancerName> registerLoadBalancerEndpoint(Application application) {
- List<LoadBalancerName> hostNamesRegistered = new ArrayList<>();
- for (Map.Entry<ZoneId, Deployment> deploymentEntry : application.deployments().entrySet()) {
- ZoneId zone = deploymentEntry.getKey();
- Deployment deployment = deploymentEntry.getValue();
- for (Map.Entry<ClusterSpec.Id, HostName> loadBalancers : deployment.loadBalancers().entrySet()) {
- try {
- RecordName recordName = RecordName.from(getEndpointName(loadBalancers.getKey(), application.id(), zone));
- RecordData recordData = RecordData.fqdn(loadBalancers.getValue().value());
- Optional<Record> existingRecord = nameService.findRecord(Record.Type.CNAME, recordName);
- final RecordId recordId;
- if(existingRecord.isPresent()) {
- recordId = existingRecord.get().id();
- nameService.updateRecord(existingRecord.get().id(), recordData);
- } else {
- recordId = nameService.createCname(recordName, recordData);
- }
- hostNamesRegistered.add(new LoadBalancerName(recordId, recordName));
- } catch (Exception e) {
- // Catching any exception, will be retried on next run
- log.log(LogLevel.WARNING,
- String.format("Got exception updating name service for application: %s, cluster: %s in zone: %s. Retrying in %s",
- application.id().toShortString(), loadBalancers.getKey().value(), zone.value(), maintenanceInterval()),
- e);
- }
- }
- }
- return hostNamesRegistered;
- }
-
- static String getEndpointName(ClusterSpec.Id clusterId, ApplicationId applicationId, ZoneId zoneId) {
- List<String> endpointTerms = Arrays.asList(ignorePartIfDefault(clusterId.value()),
- ignorePartIfDefault(applicationId.instance().value()),
- applicationId.application().value(),
- applicationId.tenant().value(),
- zoneId.value(),
- "vespa.oath.cloud"
- );
- return endpointTerms.stream()
- .filter(s -> !Strings.isNullOrEmpty((s)))
- .collect(Collectors.joining("."));
- }
-
- private static String ignorePartIfDefault(String s) {
- return IGNORE_ENDPOINT_VALUE.equalsIgnoreCase(s) ? "" : s;
- }
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
index 5fac0896246..9c498233809 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
@@ -18,7 +18,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import com.yahoo.vespa.hosted.controller.deployment.Run;
import com.yahoo.vespa.hosted.controller.deployment.Step;
-import com.yahoo.vespa.hosted.controller.loadbalancer.LoadBalancerName;
+import com.yahoo.vespa.hosted.controller.application.LoadBalancerAlias;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import com.yahoo.vespa.hosted.controller.tenant.UserTenant;
@@ -72,6 +72,7 @@ public class CuratorDb {
private static final Path applicationRoot = root.append("applications");
private static final Path jobRoot = root.append("jobs");
private static final Path controllerRoot = root.append("controllers");
+ private static final Path loadBalancerAliasesRoot = root.append("loadBalancerAliases");
private final StringSetSerializer stringSetSerializer = new StringSetSerializer();
private final VersionStatusSerializer versionStatusSerializer = new VersionStatusSerializer();
@@ -82,7 +83,7 @@ public class CuratorDb {
private final RunSerializer runSerializer = new RunSerializer();
private final OsVersionSerializer osVersionSerializer = new OsVersionSerializer();
private final OsVersionStatusSerializer osVersionStatusSerializer = new OsVersionStatusSerializer(osVersionSerializer);
- private final LoadBalancerNameSerializer loadBalancerNameSerializer = new LoadBalancerNameSerializer();
+ private final LoadBalancerAliasSerializer loadBalancerAliasSerializer = new LoadBalancerAliasSerializer();
private final Curator curator;
private final Duration tryLockTimeout;
@@ -176,8 +177,8 @@ public class CuratorDb {
return lock(lockRoot.append("osVersionStatus"), defaultLockTimeout);
}
- public Lock lockLoadBalancerNames() {
- return lock(lockRoot.append("loadBalancerNames"), defaultLockTimeout);
+ public Lock lockLoadBalancerAliases() {
+ return lock(lockRoot.append("loadBalancerAliases"), defaultLockTimeout);
}
// -------------- Helpers ------------------------------------------
@@ -460,14 +461,22 @@ public class CuratorDb {
curator.set(openStackServerPoolPath(), data);
}
- // -------------- Load balancer names -------------------------------------
+ // -------------- Load balancer aliases------------------------------------
- public void writeLoadBalancerNames(Map<ApplicationId, List<LoadBalancerName>> loadBalancerNames) {
- curator.set(loadBalancerNamePath(), asJson(loadBalancerNameSerializer.toSlime(loadBalancerNames)));
+ public void writeLoadBalancerAliases(ApplicationId application, Set<LoadBalancerAlias> aliases) {
+ curator.set(loadBalancerAliasPath(application), asJson(loadBalancerAliasSerializer.toSlime(aliases)));
}
- public Map<ApplicationId, List<LoadBalancerName>> readLoadBalancerNames() {
- return readSlime(loadBalancerNamePath()).map(loadBalancerNameSerializer::fromSlime).orElseGet(Collections::emptyMap);
+ public Set<LoadBalancerAlias> readLoadBalancerAliases() {
+ return curator.getChildren(loadBalancerAliasesRoot).stream()
+ .map(ApplicationId::fromSerializedForm)
+ .flatMap(application -> readLoadBalancerAliases(application).stream())
+ .collect(Collectors.toUnmodifiableSet());
+ }
+
+ public Set<LoadBalancerAlias> readLoadBalancerAliases(ApplicationId application) {
+ return readSlime(loadBalancerAliasPath(application)).map(slime -> loadBalancerAliasSerializer.fromSlime(application, slime))
+ .orElseGet(Collections::emptySet);
}
// -------------- Paths ---------------------------------------------------
@@ -545,8 +554,8 @@ public class CuratorDb {
return root.append("versionStatus");
}
- private static Path loadBalancerNamePath() {
- return root.append("loadBalancerNames");
+ private static Path loadBalancerAliasPath(ApplicationId application) {
+ return loadBalancerAliasesRoot.append(application.serializedForm());
}
private static Path provisionStatePath() {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializer.java
new file mode 100644
index 00000000000..0e7682eaf96
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializer.java
@@ -0,0 +1,52 @@
+// 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.controller.persistence;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
+import com.yahoo.slime.ArrayTraverser;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.controller.application.LoadBalancerAlias;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Serializer and deserializer for a {@link LoadBalancerAlias}.
+ *
+ * @author mortent
+ */
+public class LoadBalancerAliasSerializer {
+
+ private static final String aliasesField = "aliases";
+ private static final String idField = "id";
+ private static final String aliasField = "alias";
+ private static final String canonicalNameField = "canonicalName";
+
+ public Slime toSlime(Set<LoadBalancerAlias> aliases) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ Cursor aliasArray = root.setArray(aliasesField);
+ aliases.forEach(alias -> {
+ Cursor nameObject = aliasArray.addObject();
+ nameObject.setString(idField, alias.id());
+ nameObject.setString(aliasField, alias.alias().value());
+ nameObject.setString(canonicalNameField, alias.canonicalName().value());
+ });
+ return slime;
+ }
+
+ public Set<LoadBalancerAlias> fromSlime(ApplicationId owner, Slime slime) {
+ Set<LoadBalancerAlias> names = new LinkedHashSet<>();
+ slime.get().field(aliasesField).traverse((ArrayTraverser) (i, inspect) -> {
+ names.add(new LoadBalancerAlias(owner,
+ inspect.field(idField).asString(),
+ HostName.from(inspect.field(aliasField).asString()),
+ HostName.from(inspect.field(canonicalNameField).asString())));
+ });
+
+ return Collections.unmodifiableSet(names);
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerNameSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerNameSerializer.java
deleted file mode 100644
index a76e1189294..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerNameSerializer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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.controller.persistence;
-
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.slime.ArrayTraverser;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Inspector;
-import com.yahoo.slime.ObjectTraverser;
-import com.yahoo.slime.Slime;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordId;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
-import com.yahoo.vespa.hosted.controller.loadbalancer.LoadBalancerName;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Serializer and deserializer for LoadBalancerName
- *
- * @author mortent
- */
-public class LoadBalancerNameSerializer {
-
- private static final String loadBalancerNamesField = "loadBalancerNames";
-
- public Slime toSlime(Map<ApplicationId, List<LoadBalancerName>> loadBalancerNames) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- Cursor applicationEndpoints = root.setObject(loadBalancerNamesField);
-
- for (Map.Entry<ApplicationId, List<LoadBalancerName>> entry : loadBalancerNames.entrySet()) {
- ApplicationId applicationId = entry.getKey();
- Cursor cursor = applicationEndpoints.setArray(applicationId.serializedForm());
- entry.getValue().forEach( lb -> cursor.addObject().setString(lb.recordId().asString(), lb.recordName().asString()));
- }
- return slime;
- }
-
- public Map<ApplicationId, List<LoadBalancerName>> fromSlime(Slime slime) {
-
- Inspector object = slime.get().field(loadBalancerNamesField);
- Map<ApplicationId, List<LoadBalancerName>> loadBalancerNames = new HashMap<>();
- object.traverse((ObjectTraverser) (appId, inspector) ->
- loadBalancerNames.put(ApplicationId.fromSerializedForm(appId), loadBalancerNamesFromSlime(inspector)));
-
- return loadBalancerNames;
- }
-
- private List<LoadBalancerName> loadBalancerNamesFromSlime(Inspector root) {
- List<LoadBalancerName> names = new ArrayList<>();
- root.traverse((ArrayTraverser) (i, inspector) -> inspector.traverse((ObjectTraverser)(x,y)-> names.add(new LoadBalancerName(new RecordId(x), RecordName.from(y.asString())))));
- return names;
- }
-}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAliasTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAliasTest.java
new file mode 100644
index 00000000000..965a639a68c
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAliasTest.java
@@ -0,0 +1,33 @@
+// 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.controller.application;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
+import org.junit.Test;
+
+import static com.yahoo.vespa.hosted.controller.application.LoadBalancerAlias.createAlias;
+import static org.junit.Assert.*;
+
+/**
+ * @author mpolden
+ */
+public class LoadBalancerAliasTest {
+
+ @Test
+ public void test_endpoint_names() {
+ ZoneId zoneId = ZoneId.from("prod", "us-north-1");
+ ApplicationId withInstanceName = ApplicationId.from("tenant", "application", "instance");
+ testAlias("instance--application--tenant--prod.us-north-1--vespa.oath.cloud", "default", withInstanceName, zoneId);
+ testAlias("cluster--instance--application--tenant--prod.us-north-1--vespa.oath.cloud", "cluster", withInstanceName, zoneId);
+
+ ApplicationId withDefaultInstance = ApplicationId.from("tenant", "application", "default");
+ testAlias("application--tenant--prod.us-north-1--vespa.oath.cloud", "default", withDefaultInstance, zoneId);
+ testAlias("cluster--application--tenant--prod.us-north-1--vespa.oath.cloud", "cluster", withDefaultInstance, zoneId);
+ }
+
+ private void testAlias(String expected, String clusterName, ApplicationId applicationId, ZoneId zoneId) {
+ assertEquals(expected, createAlias(ClusterSpec.Id.from(clusterName), applicationId, zoneId));
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainerTest.java
index 05c3de79023..fa3fe505f3c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainerTest.java
@@ -16,7 +16,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
-import com.yahoo.vespa.hosted.controller.loadbalancer.LoadBalancerName;
+import com.yahoo.vespa.hosted.controller.application.LoadBalancerAlias;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
import org.junit.Test;
@@ -25,23 +25,23 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import static org.junit.Assert.assertEquals;
/**
* @author mortent
*/
-public class LoadBalancerMaintainerTest {
+public class LoadBalancerAliasMaintainerTest {
@Test
- public void maintains_loadbalancer_records_correctly () {
+ public void maintains_load_balancer_records_correctly() {
DeploymentTester tester = new DeploymentTester();
Application application = tester.createApplication("app1", "tenant1", 1, 1L);
-
- LoadBalancerMaintainer loadbalancerMaintainer = new LoadBalancerMaintainer(tester.controller(), Duration.ofHours(12),
- new JobControl(new MockCuratorDb()),
- tester.controllerTester().nameService(),
- tester.controllerTester().curator());
+ LoadBalancerAliasMaintainer maintainer = new LoadBalancerAliasMaintainer(tester.controller(), Duration.ofHours(12),
+ new JobControl(new MockCuratorDb()),
+ tester.controllerTester().nameService(),
+ tester.controllerTester().curator());
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.environment(Environment.prod)
@@ -55,18 +55,17 @@ public class LoadBalancerMaintainerTest {
tester.deployCompletely(application, applicationPackage);
setupClustersWithLoadBalancers(tester, application, numberOfClustersPerZone);
- loadbalancerMaintainer.maintain();
+ maintainer.maintain();
Map<RecordId, Record> records = tester.controllerTester().nameService().records();
long recordCount = records.entrySet().stream().filter(entry -> entry.getValue().data().asString().contains("loadbalancer")).count();
- assertEquals(4,recordCount);
+ assertEquals(4, recordCount);
- Map<ApplicationId, List<LoadBalancerName>> loadBalancerNames = tester.controller().curator().readLoadBalancerNames();
- List<LoadBalancerName> names = loadBalancerNames.get(application.id());
- assertEquals(4, names.size());
+ Set<LoadBalancerAlias> loadBalancerAliases = tester.controller().curator().readLoadBalancerAliases(application.id());
+ assertEquals(4, loadBalancerAliases.size());
// no update
- loadbalancerMaintainer.maintain();
+ maintainer.maintain();
Map<RecordId, Record> records2 = tester.controllerTester().nameService().records();
long recordCount2 = records2.entrySet().stream().filter(entry -> entry.getValue().data().asString().contains("loadbalancer")).count();
assertEquals(recordCount, recordCount2);
@@ -76,14 +75,13 @@ public class LoadBalancerMaintainerTest {
// add 1 cluster per zone
setupClustersWithLoadBalancers(tester, application, numberOfClustersPerZone + 1);
- loadbalancerMaintainer.maintain();
+ maintainer.maintain();
Map<RecordId, Record> records3 = tester.controllerTester().nameService().records();
long recordCount3 = records3.entrySet().stream().filter(entry -> entry.getValue().data().asString().contains("loadbalancer")).count();
assertEquals(6,recordCount3);
- Map<ApplicationId, List<LoadBalancerName>> loadBalancerNames3 = tester.controller().curator().readLoadBalancerNames();
- List<LoadBalancerName> names3 = loadBalancerNames3.get(application.id());
- assertEquals(6, names3.size());
+ Set<LoadBalancerAlias> aliases3 = tester.controller().curator().readLoadBalancerAliases(application.id());
+ assertEquals(6, aliases3.size());
// Add application
@@ -91,35 +89,33 @@ public class LoadBalancerMaintainerTest {
tester.deployCompletely(application2, applicationPackage);
setupClustersWithLoadBalancers(tester, application2, numberOfClustersPerZone);
- loadbalancerMaintainer.maintain();
+ maintainer.maintain();
Map<RecordId, Record> records4 = tester.controllerTester().nameService().records();
long recordCount4 = records4.entrySet().stream().filter(entry -> entry.getValue().data().asString().contains("loadbalancer")).count();
assertEquals(10,recordCount4);
- Map<ApplicationId, List<LoadBalancerName>> loadBalancerNames4 = tester.controller().curator().readLoadBalancerNames();
- List<LoadBalancerName> names4 = loadBalancerNames4.get(application2.id());
- assertEquals(4, names4.size());
+ Set<LoadBalancerAlias> aliases4 = tester.controller().curator().readLoadBalancerAliases(application2.id());
+ assertEquals(4, aliases4.size());
// Remove cluster in app1
setupClustersWithLoadBalancers(tester, application, numberOfClustersPerZone);
- loadbalancerMaintainer.maintain();
+ maintainer.maintain();
Map<RecordId, Record> records5 = tester.controllerTester().nameService().records();
long recordCount5 = records5.entrySet().stream().filter(entry -> entry.getValue().data().asString().contains("loadbalancer")).count();
- assertEquals(8,recordCount5);
+ assertEquals(8, recordCount5);
// Remove application app2
tester.controller().applications().get(application2.id())
- .map(app -> app.deployments().keySet())
- .orElse(Collections.emptySet())
- .forEach(zone -> tester.controller().applications().deactivate(application2.id(), zone));
+ .map(app -> app.deployments().keySet())
+ .orElse(Collections.emptySet())
+ .forEach(zone -> tester.controller().applications().deactivate(application2.id(), zone));
- loadbalancerMaintainer.maintain();
+ maintainer.maintain();
Map<RecordId, Record> records6 = tester.controllerTester().nameService().records();
long recordCount6 = records6.entrySet().stream().filter(entry -> entry.getValue().data().asString().contains("loadbalancer")).count();
- assertEquals(4,recordCount6);
-
+ assertEquals(4, recordCount6);
}
private void setupClustersWithLoadBalancers(DeploymentTester tester, Application application, int numberOfClustersPerZone) {
@@ -128,40 +124,23 @@ public class LoadBalancerMaintainerTest {
.removeLoadBalancers(new DeploymentId(application.id(), zone)));
tester.controller().applications().get(application.id()).orElseThrow(()->new RuntimeException("No deployments")).deployments().keySet()
.forEach(zone -> tester.configServer()
- .addLoadBalancers(zone, application.id(), getLoadBalancers(zone, application.id(), numberOfClustersPerZone)));
+ .addLoadBalancers(zone, application.id(), makeLoadBalancers(zone, application.id(), numberOfClustersPerZone)));
}
-
- @Test
- public void test_endpoint_names() {
- ZoneId zoneId = ZoneId.from("prod", "us-north-1");
- ApplicationId withInstanceName = ApplicationId.from("tenant", "application", "instance");
- testLoadBalancerName("instance.application.tenant.prod.us-north-1.vespa.oath.cloud", "default", withInstanceName, zoneId);
- testLoadBalancerName("cluster.instance.application.tenant.prod.us-north-1.vespa.oath.cloud", "cluster", withInstanceName, zoneId);
-
- ApplicationId withDefaultInstance = ApplicationId.from("tenant", "application", "default");
- testLoadBalancerName("application.tenant.prod.us-north-1.vespa.oath.cloud", "default", withDefaultInstance, zoneId);
- testLoadBalancerName("cluster.application.tenant.prod.us-north-1.vespa.oath.cloud", "cluster", withDefaultInstance, zoneId);
- }
-
- private void testLoadBalancerName(String expected, String clusterName, ApplicationId applicationId, ZoneId zoneId) {
- assertEquals(expected,
- LoadBalancerMaintainer.getEndpointName(ClusterSpec.Id.from(clusterName), applicationId, zoneId));
- }
-
- private List<LoadBalancer> getLoadBalancers(ZoneId zone, ApplicationId applicationId, int loadBalancerCount) {
+ private List<LoadBalancer> makeLoadBalancers(ZoneId zone, ApplicationId applicationId, int count) {
List<LoadBalancer> loadBalancers = new ArrayList<>();
- for (int i = 0; i < loadBalancerCount; i++) {
+ for (int i = 0; i < count; i++) {
loadBalancers.add(
- new LoadBalancer("LB-"+ i + "-Z-"+zone.value(),
+ new LoadBalancer("LB-" + i + "-Z-" + zone.value(),
new TenantId(applicationId.tenant().value()),
new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(applicationId.application().value()),
new InstanceId(applicationId.instance().value()),
- ClusterSpec.Id.from("cluster-"+i),
- HostName.from("loadbalancer-" + i + "-zone-" + zone.value())
+ ClusterSpec.Id.from("cluster-" + i),
+ HostName.from("loadbalancer-" + i + "-" + applicationId.serializedForm() + "-zone-" + zone.value())
));
}
return loadBalancers;
}
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializerTest.java
new file mode 100644
index 00000000000..6ef15be775e
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializerTest.java
@@ -0,0 +1,35 @@
+// 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.controller.persistence;
+
+import com.google.common.collect.ImmutableSet;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
+import com.yahoo.vespa.hosted.controller.application.LoadBalancerAlias;
+import org.junit.Test;
+
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mortent
+ */
+public class LoadBalancerAliasSerializerTest {
+
+ @Test
+ public void test_serialization() {
+ LoadBalancerAliasSerializer serializer = new LoadBalancerAliasSerializer();
+ ApplicationId owner = ApplicationId.defaultId();
+ Set<LoadBalancerAlias> names = ImmutableSet.of(new LoadBalancerAlias(owner,
+ "record-id-1",
+ HostName.from("my-pretty-alias"),
+ HostName.from("long-and-ugly-name")),
+ new LoadBalancerAlias(owner,
+ "record-id-2",
+ HostName.from("my-pretty-alias-2"),
+ HostName.from("long-and-ugly-name-2")));
+ Set<LoadBalancerAlias> serialized = serializer.fromSlime(owner, serializer.toSlime(names));
+ assertEquals(names, serialized);
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerNameSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerNameSerializerTest.java
deleted file mode 100644
index a66fe127cd5..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerNameSerializerTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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.controller.persistence;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordId;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName;
-import com.yahoo.vespa.hosted.controller.loadbalancer.LoadBalancerName;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author mortent
- */
-public class LoadBalancerNameSerializerTest {
-
- @Test
- public void test_serialization() throws IOException {
- LoadBalancerNameSerializer serializer = new LoadBalancerNameSerializer();
- ImmutableMap<ApplicationId, List<LoadBalancerName>> lbnames = ImmutableMap.of(
- ApplicationId.from("foo", "bar", "default"), Lists.newArrayList(new LoadBalancerName(new RecordId("123.4123.:124123"), RecordName.from("foo.bar"))));
- Map<ApplicationId, List<LoadBalancerName>> deserialized = serializer.fromSlime(serializer.toSlime(lbnames));
-
- assertThat(deserialized, equalTo(lbnames));
- }
-} \ No newline at end of file
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 75e98d2d5e9..169c86fabbe 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,7 +34,7 @@
"name": "JobRunner"
},
{
- "name": "LoadBalancerMaintainer"
+ "name": "LoadBalancerAliasMaintainer"
},
{
"name": "MetricsReporter"