summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/SlimeUtils.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java35
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAlias.java)57
-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/RoutingPolicyMaintainer.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainer.java)43
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java52
-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/RoutingPolicySerializer.java81
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicyTest.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/LoadBalancerAliasTest.java)4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainerTest.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainerTest.java)65
-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/RoutingPolicySerializerTest.java78
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json8
13 files changed, 317 insertions, 204 deletions
diff --git a/config/src/main/java/com/yahoo/vespa/config/SlimeUtils.java b/config/src/main/java/com/yahoo/vespa/config/SlimeUtils.java
index bc1b2c90364..80e93977980 100644
--- a/config/src/main/java/com/yahoo/vespa/config/SlimeUtils.java
+++ b/config/src/main/java/com/yahoo/vespa/config/SlimeUtils.java
@@ -5,6 +5,7 @@ import com.yahoo.slime.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Optional;
/**
@@ -113,6 +114,10 @@ public class SlimeUtils {
return slime;
}
+ public static Slime jsonToSlime(String json) {
+ return jsonToSlime(json.getBytes(StandardCharsets.UTF_8));
+ }
+
public static Optional<String> optionalString(Inspector inspector) {
return Optional.of(inspector.asString()).filter(s -> !s.isEmpty());
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java
index b77b63cab8a..9f686570da1 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/LoadBalancer.java
@@ -1,13 +1,15 @@
// 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.api.integration.configserver;
+import com.google.common.collect.ImmutableSortedSet;
+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.identifiers.ApplicationId;
-import com.yahoo.vespa.hosted.controller.api.identifiers.InstanceId;
-import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId;
+import com.yahoo.config.provision.RotationName;
import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
/**
* Represents an exclusive load balancer, assigned to an application's cluster.
@@ -17,37 +19,30 @@ import java.util.Objects;
public class LoadBalancer {
private final String id;
- private final TenantId tenant;
private final ApplicationId application;
- private final InstanceId instance;
private final ClusterSpec.Id cluster;
private final HostName hostname;
+ private final Optional<String> dnsZone;
+ private final Set<RotationName> rotations;
- public LoadBalancer(String id, TenantId tenant, ApplicationId application, InstanceId instance, ClusterSpec.Id cluster, HostName hostname) {
+ public LoadBalancer(String id, ApplicationId application, ClusterSpec.Id cluster, HostName hostname,
+ Optional<String> dnsZone, Set<RotationName> rotations) {
this.id = Objects.requireNonNull(id, "id must be non-null");
- this.tenant = Objects.requireNonNull(tenant, "tenant must be non-null");
this.application = Objects.requireNonNull(application, "application must be non-null");
- this.instance = Objects.requireNonNull(instance, "instance must be non-null");
this.cluster = Objects.requireNonNull(cluster, "cluster must be non-null");
this.hostname = Objects.requireNonNull(hostname, "hostname must be non-null");
+ this.dnsZone = Objects.requireNonNull(dnsZone, "dnsZone must be non-null");
+ this.rotations = ImmutableSortedSet.copyOf(Objects.requireNonNull(rotations, "rotations must be non-null"));
}
public String id() {
return id;
}
- public TenantId tenant() {
- return tenant;
- }
-
public ApplicationId application() {
return application;
}
- public InstanceId instance() {
- return instance;
- }
-
public ClusterSpec.Id cluster() {
return cluster;
}
@@ -56,4 +51,12 @@ public class LoadBalancer {
return hostname;
}
+ public Optional<String> dnsZone() {
+ return dnsZone;
+ }
+
+ public Set<RotationName> rotations() {
+ return rotations;
+ }
+
}
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/RoutingPolicy.java
index d8b90d5aa65..41e3a6086d2 100644
--- 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/RoutingPolicy.java
@@ -2,34 +2,44 @@
package com.yahoo.vespa.hosted.controller.application;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSortedSet;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
+import com.yahoo.config.provision.RotationName;
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.Optional;
+import java.util.Set;
import java.util.stream.Collectors;
/**
- * Represents a DNS alias for a load balancer.
+ * Represents the DNS routing policy for a load balancer.
*
* @author mortent
*/
-public class LoadBalancerAlias {
+public class RoutingPolicy {
private static final String ignoredEndpointPart = "default";
+
private final ApplicationId owner;
- private final String id;
+ private final String recordId;
private final HostName alias;
private final HostName canonicalName;
+ private final Optional<String> dnsZone;
+ private final Set<RotationName> rotations;
- public LoadBalancerAlias(ApplicationId owner, String id, HostName alias, HostName canonicalName) {
+ public RoutingPolicy(ApplicationId owner, String recordId, HostName alias, HostName canonicalName,
+ Optional<String> dnsZone, Set<RotationName> rotations) {
this.owner = Objects.requireNonNull(owner, "owner must be non-null");
- this.id = Objects.requireNonNull(id, "id must be non-null");
+ this.recordId = Objects.requireNonNull(recordId, "recordId must be non-null");
this.alias = Objects.requireNonNull(alias, "alias must be non-null");
this.canonicalName = Objects.requireNonNull(canonicalName, "canonicalName must be non-null");
+ this.dnsZone = Objects.requireNonNull(dnsZone, "dnsZone must be non-null");
+ this.rotations = ImmutableSortedSet.copyOf(Objects.requireNonNull(rotations, "rotations must be non-null"));
}
/** The application owning this */
@@ -37,40 +47,53 @@ public class LoadBalancerAlias {
return owner;
}
- /** The ID of the DNS record represented by this */
- public String id() {
- return id;
+ /** The ID of the DNS record identifying this */
+ public String recordId() {
+ return recordId;
}
- /** This alias (lhs of the CNAME record) */
+ /** This alias (lhs of a CNAME record) */
public HostName alias() {
return alias;
}
- /** The canonical name of this (rhs of the CNAME record) */
+ /** The canonical name for this (rhs of a CNAME record) */
public HostName canonicalName() {
return canonicalName;
}
+ /** DNS zone for this, if any */
+ public Optional<String> dnsZone() {
+ return dnsZone;
+ }
+
+ /** The rotations in this policy */
+ public Set<RotationName> rotations() {
+ return rotations;
+ }
+
@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);
+ RoutingPolicy that = (RoutingPolicy) o;
+ return owner.equals(that.owner) &&
+ recordId.equals(that.recordId) &&
+ alias.equals(that.alias) &&
+ canonicalName.equals(that.canonicalName) &&
+ dnsZone.equals(that.dnsZone) &&
+ rotations.equals(that.rotations);
}
@Override
public int hashCode() {
- return Objects.hash(owner, id, alias, canonicalName);
+ return Objects.hash(owner, recordId, alias, canonicalName, dnsZone, rotations);
}
@Override
public String toString() {
- return String.format("%s: %s -> %s, owned by %s", id, alias, canonicalName, owner.toShortString());
+ return String.format("%s: %s -> %s [rotations: %s%s], owned by %s", recordId, alias, canonicalName, rotations,
+ dnsZone.map(z -> ", DNS zone: " + z).orElse(""), owner.toShortString());
}
public static String createAlias(ClusterSpec.Id clusterId, ApplicationId applicationId, ZoneId zoneId) {
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 5045ab0877c..cc9e4020dab 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 LoadBalancerAliasMaintainer loadBalancerAliasMaintainer;
+ private final RoutingPolicyMaintainer routingPolicyMaintainer;
@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);
- loadBalancerAliasMaintainer = new LoadBalancerAliasMaintainer(controller, Duration.ofMinutes(5), jobControl, nameService, curator);
+ routingPolicyMaintainer = new RoutingPolicyMaintainer(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();
- loadBalancerAliasMaintainer.deconstruct();
+ routingPolicyMaintainer.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/RoutingPolicyMaintainer.java
index 407fdfbe466..42e8a572815 100644
--- 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/RoutingPolicyMaintainer.java
@@ -17,7 +17,7 @@ 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.application.RoutingPolicy;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import java.time.Duration;
@@ -33,23 +33,23 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
- * Maintains DNS aliases for all load balancers in this system.
+ * Maintains routing policies for all exclusive load balancers in this system.
*
* @author mortent
*/
-public class LoadBalancerAliasMaintainer extends Maintainer {
+public class RoutingPolicyMaintainer extends Maintainer {
- private static final Logger log = Logger.getLogger(LoadBalancerAliasMaintainer.class.getName());
+ private static final Logger log = Logger.getLogger(RoutingPolicyMaintainer.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) {
+ public RoutingPolicyMaintainer(Controller controller,
+ Duration interval,
+ JobControl jobControl,
+ NameService nameService,
+ CuratorDb db) {
super(controller, interval, jobControl);
this.nameService = nameService;
this.db = db;
@@ -73,26 +73,26 @@ public class LoadBalancerAliasMaintainer extends Maintainer {
applications.store(locked.withLoadBalancersIn(zone, loadBalancers));
});
- try (Lock lock = db.lockLoadBalancerAliases()) {
- Set<LoadBalancerAlias> aliases = new LinkedHashSet<>(db.readLoadBalancerAliases(application.id()));
+ try (Lock lock = db.lockRoutingPolicies()) {
+ Set<RoutingPolicy> policies = new LinkedHashSet<>(db.readRoutingPolicies(application.id()));
for (LoadBalancer loadBalancer : loadBalancers) {
try {
- aliases.add(registerDnsAlias(application.id(), zone, loadBalancer));
+ policies.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);
+ db.writeRoutingPolicies(application.id(), policies);
}
}
}
}
/** 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));
+ private RoutingPolicy registerDnsAlias(ApplicationId application, ZoneId zone, LoadBalancer loadBalancer) {
+ HostName alias = HostName.from(RoutingPolicy.createAlias(loadBalancer.cluster(), application, zone));
RecordName name = RecordName.from(alias.value());
RecordData data = RecordData.fqdn(loadBalancer.hostname().value());
List<Record> existingRecords = nameService.findRecords(Record.Type.CNAME, name);
@@ -107,7 +107,8 @@ public class LoadBalancerAliasMaintainer extends Maintainer {
} else {
id = nameService.createCname(name, data);
}
- return new LoadBalancerAlias(application, id.asString(), alias, loadBalancer.hostname());
+ return new RoutingPolicy(application, id.asString(), alias, loadBalancer.hostname(), loadBalancer.dnsZone(),
+ loadBalancer.rotations());
}
/** Find all load balancers assigned to application in given zone */
@@ -124,8 +125,8 @@ public class LoadBalancerAliasMaintainer extends Maintainer {
/** 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());
+ try (Lock lock = db.lockRoutingPolicies()) {
+ List<RoutingPolicy> removalCandidates = new ArrayList<>(db.readRoutingPolicies());
Set<HostName> activeLoadBalancers = controller().applications().asList().stream()
.map(Application::deployments)
.map(Map::values)
@@ -137,11 +138,11 @@ public class LoadBalancerAliasMaintainer extends Maintainer {
// Remove any active load balancers
removalCandidates.removeIf(lb -> activeLoadBalancers.contains(lb.canonicalName()));
- for (LoadBalancerAlias alias : removalCandidates) {
+ for (RoutingPolicy policy : removalCandidates) {
try {
- nameService.removeRecord(new RecordId(alias.id()));
+ nameService.removeRecord(new RecordId(policy.recordId()));
} catch (Exception e) {
- log.log(LogLevel.WARNING, "Failed to remove DNS record with ID '" + alias.id() +
+ log.log(LogLevel.WARNING, "Failed to remove DNS record with ID '" + policy.recordId() +
"'. Retrying in " + maintenanceInterval());
}
}
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 466e00cef2a..e9920c9f1c6 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
@@ -16,9 +16,9 @@ import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.Application;
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.application.RoutingPolicy;
import com.yahoo.vespa.hosted.controller.deployment.Run;
import com.yahoo.vespa.hosted.controller.deployment.Step;
-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;
@@ -73,7 +73,8 @@ 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 static final Path legacyRoutingPoliciesRoot = root.append("loadBalancerAliases");
+ private static final Path routingPoliciesRoot = root.append("routingPolicies");
private final StringSetSerializer stringSetSerializer = new StringSetSerializer();
private final VersionStatusSerializer versionStatusSerializer = new VersionStatusSerializer();
@@ -84,7 +85,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 LoadBalancerAliasSerializer loadBalancerAliasSerializer = new LoadBalancerAliasSerializer();
+ private final RoutingPolicySerializer routingPolicySerializer = new RoutingPolicySerializer();
private final Curator curator;
private final Duration tryLockTimeout;
@@ -185,8 +186,16 @@ public class CuratorDb {
return lock(lockRoot.append("osVersionStatus"), defaultLockTimeout);
}
- public Lock lockLoadBalancerAliases() {
- return lock(lockRoot.append("loadBalancerAliases"), defaultLockTimeout);
+ public Lock lockRoutingPolicies() {
+ Set<Version> clusterVersions = cluster().stream()
+ .map(this::readControllerVersion)
+ .collect(Collectors.toSet());
+ // TODO: Remove this once cluster has completely upgraded once
+ Path newPath = lockRoot.append("routingPolicies");
+ if (clusterVersions.size() > 1 && !curator.exists(newPath)) {
+ return lock(lockRoot.append("loadBalancerAliases"), defaultLockTimeout);
+ }
+ return lock(newPath, defaultLockTimeout);
}
// -------------- Helpers ------------------------------------------
@@ -467,22 +476,29 @@ public class CuratorDb {
curator.set(openStackServerPoolPath(), data);
}
- // -------------- Load balancer aliases------------------------------------
+ // -------------- Routing policies ----------------------------------------
- public void writeLoadBalancerAliases(ApplicationId application, Set<LoadBalancerAlias> aliases) {
- curator.set(loadBalancerAliasPath(application), asJson(loadBalancerAliasSerializer.toSlime(aliases)));
+ public void writeRoutingPolicies(ApplicationId application, Set<RoutingPolicy> policies) {
+ curator.set(routingPolicyPath(application), asJson(routingPolicySerializer.toSlime(policies)));
}
- public Set<LoadBalancerAlias> readLoadBalancerAliases() {
- return curator.getChildren(loadBalancerAliasesRoot).stream()
- .map(ApplicationId::fromSerializedForm)
- .flatMap(application -> readLoadBalancerAliases(application).stream())
- .collect(Collectors.toUnmodifiableSet());
+ public Set<RoutingPolicy> readRoutingPolicies() {
+ List<String> children;
+ if (curator.exists(routingPoliciesRoot)) {
+ children = curator.getChildren(routingPoliciesRoot);
+ curator.delete(legacyRoutingPoliciesRoot);
+ } else {
+ children = curator.getChildren(legacyRoutingPoliciesRoot); // TODO: Remove after 7.9 has been released
+ }
+ return children.stream()
+ .map(ApplicationId::fromSerializedForm)
+ .flatMap(application -> readRoutingPolicies(application).stream())
+ .collect(Collectors.toUnmodifiableSet());
}
- public Set<LoadBalancerAlias> readLoadBalancerAliases(ApplicationId application) {
- return readSlime(loadBalancerAliasPath(application)).map(slime -> loadBalancerAliasSerializer.fromSlime(application, slime))
- .orElseGet(Collections::emptySet);
+ public Set<RoutingPolicy> readRoutingPolicies(ApplicationId application) {
+ return readSlime(routingPolicyPath(application)).map(slime -> routingPolicySerializer.fromSlime(application, slime))
+ .orElseGet(Collections::emptySet);
}
// -------------- Paths ---------------------------------------------------
@@ -560,8 +576,8 @@ public class CuratorDb {
return root.append("versionStatus");
}
- private static Path loadBalancerAliasPath(ApplicationId application) {
- return loadBalancerAliasesRoot.append(application.serializedForm());
+ private static Path routingPolicyPath(ApplicationId application) {
+ return routingPoliciesRoot.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
deleted file mode 100644
index 0e7682eaf96..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializer.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.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/RoutingPolicySerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java
new file mode 100644
index 00000000000..479d87cbe71
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java
@@ -0,0 +1,81 @@
+// 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.config.provision.RotationName;
+import com.yahoo.slime.ArrayTraverser;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * Serializer and deserializer for a {@link RoutingPolicy}.
+ *
+ * @author mortent
+ */
+public class RoutingPolicySerializer {
+
+ private static final String routingPoliciesField = "routingPolicies";
+ private static final String aliasesField = "aliases";
+ private static final String idField = "id";
+ private static final String recordIdField = "recordId";
+ private static final String aliasField = "alias";
+ private static final String canonicalNameField = "canonicalName";
+ private static final String dnsZoneField = "dnsZone";
+ private static final String rotationsField = "rotations";
+
+ public Slime toSlime(Set<RoutingPolicy> routingPolicies) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ Cursor policyArray = root.setArray(routingPoliciesField);
+ routingPolicies.forEach(policy -> {
+ Cursor policyObject = policyArray.addObject();
+ policyObject.setString(recordIdField, policy.recordId());
+ policyObject.setString(aliasField, policy.alias().value());
+ policyObject.setString(canonicalNameField, policy.canonicalName().value());
+ policy.dnsZone().ifPresent(dnsZone -> policyObject.setString(dnsZoneField, dnsZone));
+ Cursor rotationArray = policyObject.setArray(rotationsField);
+ policy.rotations().forEach(rotation -> {
+ rotationArray.addString(rotation.value());
+ });
+ });
+ return slime;
+ }
+
+ public Set<RoutingPolicy> fromSlime(ApplicationId owner, Slime slime) {
+ Set<RoutingPolicy> policies = new LinkedHashSet<>();
+ Cursor root = slime.get();
+ Cursor field = root.field(routingPoliciesField);
+ if (!field.valid()) {
+ field = root.field(aliasesField); // TODO: Remove after 7.9 has been released
+ }
+ field.traverse((ArrayTraverser) (i, inspect) -> {
+ Set<RotationName> rotations = new LinkedHashSet<>();
+ inspect.field(rotationsField).traverse((ArrayTraverser) (j, rotation) -> rotations.add(RotationName.from(rotation.asString())));
+ Inspector recordId = inspect.field(recordIdField);
+ if (!recordId.valid()) {
+ recordId = inspect.field(idField); // TODO: Remove after 7.9 has been released
+ }
+ policies.add(new RoutingPolicy(owner,
+ recordId.asString(),
+ HostName.from(inspect.field(aliasField).asString()),
+ HostName.from(inspect.field(canonicalNameField).asString()),
+ optionalField(inspect.field(dnsZoneField), Function.identity()),
+ rotations));
+ });
+ return Collections.unmodifiableSet(policies);
+ }
+
+ private static <T> Optional<T> optionalField(Inspector field, Function<String, T> fieldMapper) {
+ return Optional.of(field).filter(Inspector::valid).map(Inspector::asString).map(fieldMapper);
+ }
+
+}
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/RoutingPolicyTest.java
index a331ee07239..2e2ab3d9f67 100644
--- 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/RoutingPolicyTest.java
@@ -6,13 +6,13 @@ 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 com.yahoo.vespa.hosted.controller.application.RoutingPolicy.createAlias;
import static org.junit.Assert.*;
/**
* @author mpolden
*/
-public class LoadBalancerAliasTest {
+public class RoutingPolicyTest {
@Test
public void test_endpoint_names() {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainerTest.java
index fa3fe505f3c..f1c571da451 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/LoadBalancerAliasMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainerTest.java
@@ -5,18 +5,17 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
+import com.yahoo.config.provision.RotationName;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
-import com.yahoo.vespa.hosted.controller.api.identifiers.InstanceId;
-import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordId;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
+import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
-import com.yahoo.vespa.hosted.controller.application.LoadBalancerAlias;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
import org.junit.Test;
@@ -25,23 +24,25 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
+import java.util.function.Supplier;
import static org.junit.Assert.assertEquals;
/**
* @author mortent
*/
-public class LoadBalancerAliasMaintainerTest {
+public class RoutingPolicyMaintainerTest {
@Test
- public void maintains_load_balancer_records_correctly() {
+ public void maintains_routing_policies() {
DeploymentTester tester = new DeploymentTester();
Application application = tester.createApplication("app1", "tenant1", 1, 1L);
- LoadBalancerAliasMaintainer maintainer = new LoadBalancerAliasMaintainer(tester.controller(), Duration.ofHours(12),
- new JobControl(new MockCuratorDb()),
- tester.controllerTester().nameService(),
- tester.controllerTester().curator());
+ RoutingPolicyMaintainer maintainer = new RoutingPolicyMaintainer(tester.controller(), Duration.ofHours(12),
+ new JobControl(new MockCuratorDb()),
+ tester.controllerTester().nameService(),
+ tester.controllerTester().curator());
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.environment(Environment.prod)
@@ -57,18 +58,17 @@ public class LoadBalancerAliasMaintainerTest {
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);
+ Supplier<Long> recordCount = () -> records.entrySet().stream().filter(entry -> entry.getValue().data().asString().contains("loadbalancer")).count();
+ assertEquals(4, (long) recordCount.get());
- Set<LoadBalancerAlias> loadBalancerAliases = tester.controller().curator().readLoadBalancerAliases(application.id());
- assertEquals(4, loadBalancerAliases.size());
+ Set<RoutingPolicy> policies = tester.controller().curator().readRoutingPolicies(application.id());
+ assertEquals(4, policies.size());
// no update
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);
+ assertEquals(4, (long) recordCount.get());
assertEquals(records, records2);
@@ -76,12 +76,10 @@ public class LoadBalancerAliasMaintainerTest {
setupClustersWithLoadBalancers(tester, application, numberOfClustersPerZone + 1);
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);
+ assertEquals(6, (long) recordCount.get());
- Set<LoadBalancerAlias> aliases3 = tester.controller().curator().readLoadBalancerAliases(application.id());
- assertEquals(6, aliases3.size());
+ Set<RoutingPolicy> policies2 = tester.controller().curator().readRoutingPolicies(application.id());
+ assertEquals(6, policies2.size());
// Add application
@@ -90,11 +88,9 @@ public class LoadBalancerAliasMaintainerTest {
setupClustersWithLoadBalancers(tester, application2, numberOfClustersPerZone);
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);
+ assertEquals(10, (long) recordCount.get());
- Set<LoadBalancerAlias> aliases4 = tester.controller().curator().readLoadBalancerAliases(application2.id());
+ Set<RoutingPolicy> aliases4 = tester.controller().curator().readRoutingPolicies(application2.id());
assertEquals(4, aliases4.size());
@@ -102,9 +98,7 @@ public class LoadBalancerAliasMaintainerTest {
setupClustersWithLoadBalancers(tester, application, numberOfClustersPerZone);
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, (long) recordCount.get());
// Remove application app2
tester.controller().applications().get(application2.id())
@@ -113,9 +107,7 @@ public class LoadBalancerAliasMaintainerTest {
.forEach(zone -> tester.controller().applications().deactivate(application2.id(), zone));
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, (long) recordCount.get());
}
private void setupClustersWithLoadBalancers(DeploymentTester tester, Application application, int numberOfClustersPerZone) {
@@ -128,17 +120,18 @@ public class LoadBalancerAliasMaintainerTest {
}
- private List<LoadBalancer> makeLoadBalancers(ZoneId zone, ApplicationId applicationId, int count) {
+ private List<LoadBalancer> makeLoadBalancers(ZoneId zone, ApplicationId application, int count) {
List<LoadBalancer> loadBalancers = new ArrayList<>();
+ Set<RotationName> rotations = Collections.singleton(RotationName.from("r1"));
for (int i = 0; i < count; i++) {
loadBalancers.add(
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()),
+ application,
ClusterSpec.Id.from("cluster-" + i),
- HostName.from("loadbalancer-" + i + "-" + applicationId.serializedForm() + "-zone-" + zone.value())
- ));
+ HostName.from("loadbalancer-" + i + "-" + application.serializedForm() +
+ "-zone-" + zone.value()),
+ Optional.of("dns-zone-1"),
+ rotations));
}
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
deleted file mode 100644
index 6ef15be775e..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LoadBalancerAliasSerializerTest.java
+++ /dev/null
@@ -1,35 +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.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/RoutingPolicySerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
new file mode 100644
index 00000000000..7dc31581d3b
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
@@ -0,0 +1,78 @@
+// 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.config.provision.RotationName;
+import com.yahoo.vespa.config.SlimeUtils;
+import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mortent
+ */
+public class RoutingPolicySerializerTest {
+
+ @Test
+ public void test_serialization() {
+ RoutingPolicySerializer serializer = new RoutingPolicySerializer();
+ ApplicationId owner = ApplicationId.defaultId();
+ Set<RotationName> rotations = Set.of(RotationName.from("r1"), RotationName.from("r2"));
+ Set<RoutingPolicy> loadBalancers = ImmutableSet.of(new RoutingPolicy(owner,
+ "record-id-1",
+ HostName.from("my-pretty-alias"),
+ HostName.from("long-and-ugly-name"),
+ Optional.of("zone1"),
+ rotations),
+ new RoutingPolicy(owner,
+ "record-id-2",
+ HostName.from("my-pretty-alias-2"),
+ HostName.from("long-and-ugly-name-2"),
+ Optional.empty(),
+ rotations));
+ Set<RoutingPolicy> serialized = serializer.fromSlime(owner, serializer.toSlime(loadBalancers));
+ assertEquals(loadBalancers, serialized);
+ }
+
+ // TODO: Remove after 7.9 has been released
+ @Test
+ public void test_serialization_old_format() {
+ String json = "{\n" +
+ " \"aliases\": [\n" +
+ " {\n" +
+ " \"id\": \"record-id-1\",\n" +
+ " \"alias\": \"my-pretty-alias\",\n" +
+ " \"canonicalName\": \"long-and-ugly-name\"" +
+ " },\n" +
+ " {\n" +
+ " \"id\": \"record-id-2\",\n" +
+ " \"alias\": \"my-pretty-alias-2\",\n" +
+ " \"canonicalName\": \"long-and-ugly-name-2\"" +
+ " }\n" +
+ " ]\n" +
+ "}\n";
+ ApplicationId owner = ApplicationId.defaultId();
+ Set<RoutingPolicy> loadBalancers = ImmutableSet.of(new RoutingPolicy(owner,
+ "record-id-1",
+ HostName.from("my-pretty-alias"),
+ HostName.from("long-and-ugly-name"),
+ Optional.empty(),
+ Collections.emptySet()),
+ new RoutingPolicy(owner,
+ "record-id-2",
+ HostName.from("my-pretty-alias-2"),
+ HostName.from("long-and-ugly-name-2"),
+ Optional.empty(),
+ Collections.emptySet()));
+ RoutingPolicySerializer serializer = new RoutingPolicySerializer();
+ assertEquals(loadBalancers, serializer.fromSlime(owner, SlimeUtils.jsonToSlime(json)));
+ }
+
+}
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 169c86fabbe..dd64d480453 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
@@ -16,7 +16,7 @@
"name": "CostReportMaintainer"
},
{
- "name":"DefaultOsUpgrader"
+ "name": "DefaultOsUpgrader"
},
{
"name": "DeploymentExpirer"
@@ -34,9 +34,6 @@
"name": "JobRunner"
},
{
- "name": "LoadBalancerAliasMaintainer"
- },
- {
"name": "MetricsReporter"
},
{
@@ -49,6 +46,9 @@
"name": "ReadyJobsTrigger"
},
{
+ "name": "RoutingPolicyMaintainer"
+ },
+ {
"name": "SystemUpgrader"
},
{