aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java70
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveZoneRoutingContext.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveRoutingContext.java)4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedZoneRoutingContext.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedRoutingContext.java)4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepository.java9
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java116
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java24
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-supported-tenant.json35
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java7
13 files changed, 215 insertions, 76 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
index 4772dbeaab1..6ef0df9f099 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
@@ -35,9 +35,10 @@ import com.yahoo.vespa.hosted.controller.routing.RoutingPolicies;
import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext;
import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext.ExclusiveDeploymentRoutingContext;
import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext.SharedDeploymentRoutingContext;
-import com.yahoo.vespa.hosted.controller.routing.context.ExclusiveRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.context.ExclusiveZoneRoutingContext;
import com.yahoo.vespa.hosted.controller.routing.context.RoutingContext;
-import com.yahoo.vespa.hosted.controller.routing.context.SharedRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.context.SharedZoneRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.rotation.Rotation;
import com.yahoo.vespa.hosted.controller.routing.rotation.RotationLock;
import com.yahoo.vespa.hosted.controller.routing.rotation.RotationRepository;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
@@ -53,6 +54,8 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
+import java.util.OptionalInt;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
@@ -95,9 +98,9 @@ public class RoutingController {
/** Create a routing context for given zone */
public RoutingContext of(ZoneId zone) {
if (usesSharedRouting(zone)) {
- return new SharedRoutingContext(zone, controller.serviceRegistry().configServer());
+ return new SharedZoneRoutingContext(zone, controller.serviceRegistry().configServer());
}
- return new ExclusiveRoutingContext(zone, routingPolicies);
+ return new ExclusiveZoneRoutingContext(zone, routingPolicies);
}
public RoutingPolicies policies() {
@@ -257,7 +260,6 @@ public class RoutingController {
EndpointList endpoints = declaredEndpointsOf(application.get()).targets(deployment);
EndpointList globalEndpoints = endpoints.scope(Endpoint.Scope.global);
for (var assignedRotation : instance.rotations()) {
- var names = new ArrayList<String>();
EndpointList rotationEndpoints = globalEndpoints.named(assignedRotation.endpointId())
.requiresRotation();
@@ -272,21 +274,21 @@ public class RoutingController {
}
// Register names in DNS
- var rotation = rotationRepository.getRotation(assignedRotation.rotationId());
- if (rotation.isPresent()) {
- rotationEndpoints.forEach(endpoint -> {
- controller.nameServiceForwarder().createCname(RecordName.from(endpoint.dnsName()),
- RecordData.fqdn(rotation.get().name()),
- Priority.normal);
- names.add(endpoint.dnsName());
- });
+ Rotation rotation = rotationRepository.requireRotation(assignedRotation.rotationId());
+ for (var endpoint : rotationEndpoints) {
+ controller.nameServiceForwarder().createCname(RecordName.from(endpoint.dnsName()),
+ RecordData.fqdn(rotation.name()),
+ Priority.normal);
+ List<String> names = List.of(endpoint.dnsName(),
+ // Include rotation ID as a valid name of this container endpoint
+ // (required by global routing health checks)
+ assignedRotation.rotationId().asString());
+ containerEndpoints.add(new ContainerEndpoint(assignedRotation.clusterId().value(),
+ asString(Endpoint.Scope.global),
+ names,
+ OptionalInt.empty(),
+ endpoint.routingMethod()));
}
-
- // Include rotation ID as a valid name of this container endpoint (required by global routing health checks)
- names.add(assignedRotation.rotationId().asString());
- containerEndpoints.add(new ContainerEndpoint(assignedRotation.clusterId().value(),
- asString(Endpoint.Scope.global),
- names));
}
// Add endpoints not backed by a rotation (i.e. other routing methods so that the config server always knows
// about global names, even when not using rotations)
@@ -295,7 +297,9 @@ public class RoutingController {
.forEach((clusterId, clusterEndpoints) -> {
containerEndpoints.add(new ContainerEndpoint(clusterId.value(),
asString(Endpoint.Scope.global),
- clusterEndpoints.mapToList(Endpoint::dnsName)));
+ clusterEndpoints.mapToList(Endpoint::dnsName),
+ OptionalInt.empty(),
+ RoutingMethod.exclusive));
});
// Add application endpoints
EndpointList applicationEndpoints = endpoints.scope(Endpoint.Scope.application);
@@ -313,12 +317,22 @@ public class RoutingController {
RecordData.fqdn(vipHostname),
Priority.normal);
}
- applicationEndpoints.groupingBy(Endpoint::cluster)
- .forEach((clusterId, clusterEndpoints) -> {
- containerEndpoints.add(new ContainerEndpoint(clusterId.value(),
- asString(Endpoint.Scope.application),
- clusterEndpoints.mapToList(Endpoint::dnsName)));
- });
+ Map<ClusterSpec.Id, EndpointList> applicationEndpointsByCluster = applicationEndpoints.groupingBy(Endpoint::cluster);
+ for (var kv : applicationEndpointsByCluster.entrySet()) {
+ ClusterSpec.Id clusterId = kv.getKey();
+ EndpointList clusterEndpoints = kv.getValue();
+ for (var endpoint : clusterEndpoints) {
+ Optional<Endpoint.Target> matchingTarget = endpoint.targets().stream()
+ .filter(t -> t.routesTo(deployment))
+ .findFirst();
+ if (matchingTarget.isEmpty()) throw new IllegalStateException("No target found routing to " + deployment + " in " + endpoint);
+ containerEndpoints.add(new ContainerEndpoint(clusterId.value(),
+ asString(Endpoint.Scope.application),
+ List.of(endpoint.dnsName()),
+ OptionalInt.of(matchingTarget.get().weight()),
+ endpoint.routingMethod()));
+ }
+ }
return Collections.unmodifiableSet(containerEndpoints);
}
@@ -376,8 +390,8 @@ public class RoutingController {
var deploymentsByMethod = new HashMap<RoutingMethod, Set<DeploymentId>>();
for (var deployment : deployments) {
for (var method : controller.zoneRegistry().routingMethods(deployment.zoneId())) {
- deploymentsByMethod.putIfAbsent(method, new LinkedHashSet<>());
- deploymentsByMethod.get(method).add(deployment);
+ deploymentsByMethod.computeIfAbsent(method, k -> new LinkedHashSet<>())
+ .add(deployment);
}
}
var routingMethods = new ArrayList<RoutingMethod>();
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index aee7c1052be..544822e3be3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -482,6 +482,11 @@ public class Endpoint {
return weight;
}
+ /** Returns whether this routes to given deployment */
+ public boolean routesTo(DeploymentId deployment) {
+ return this.deployment.equals(deployment);
+ }
+
}
public static class EndpointBuilder {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
index f9fd02fbf56..7fe8d554998 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
@@ -53,7 +53,7 @@ public class EndpointList extends AbstractFilteringList<Endpoint, EndpointList>
return matching(endpoint -> endpoint.deployments().containsAll(deployments));
}
- /** Returns the subset of endpoints which target the given deployments */
+ /** Returns the subset of endpoints which target the given deployment */
public EndpointList targets(DeploymentId deployment) {
return targets(List.of(deployment));
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
index 0d12b283543..7e9ae036cc7 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
@@ -25,6 +25,7 @@ import com.yahoo.vespa.flags.IntFlag;
import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.LockedTenant;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.Plan;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
import com.yahoo.vespa.hosted.controller.api.integration.user.Roles;
import com.yahoo.vespa.hosted.controller.api.integration.user.User;
@@ -176,6 +177,7 @@ public class UserApiHandler extends LoggingRequestHandler {
.sorted()
.forEach(tenant -> {
Cursor tenantObject = tenants.setObject(tenant.value());
+ tenantObject.setBool("supported", hasSupportedPlan(tenant));
Cursor tenantRolesObject = tenantObject.setArray("roles");
tenantRolesByTenantName.getOrDefault(tenant, List.of())
@@ -405,4 +407,11 @@ public class UserApiHandler extends LoggingRequestHandler {
.map(clazz::cast)
.orElseThrow(() -> new IllegalArgumentException("Attribute '" + attributeName + "' was not set on request"));
}
+
+ private boolean hasSupportedPlan(TenantName tenantName) {
+ var planId = controller.serviceRegistry().billingController().getPlan(tenantName);
+ return controller.serviceRegistry().planRegistry().plan(planId)
+ .map(Plan::isSupported)
+ .orElse(false);
+ }
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveRoutingContext.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveZoneRoutingContext.java
index e949c45f2fd..e29fb5ab404 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveRoutingContext.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveZoneRoutingContext.java
@@ -13,12 +13,12 @@ import java.util.Objects;
*
* @author mpolden
*/
-public class ExclusiveRoutingContext implements RoutingContext {
+public class ExclusiveZoneRoutingContext implements RoutingContext {
private final RoutingPolicies policies;
private final ZoneId zone;
- public ExclusiveRoutingContext(ZoneId zone, RoutingPolicies policies) {
+ public ExclusiveZoneRoutingContext(ZoneId zone, RoutingPolicies policies) {
this.policies = Objects.requireNonNull(policies);
this.zone = Objects.requireNonNull(zone);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedRoutingContext.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedZoneRoutingContext.java
index e38212d7f80..2923c8dff5c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedRoutingContext.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedZoneRoutingContext.java
@@ -15,12 +15,12 @@ import java.util.Objects;
*
* @author mpolden
*/
-public class SharedRoutingContext implements RoutingContext {
+public class SharedZoneRoutingContext implements RoutingContext {
private final ConfigServer configServer;
private final ZoneId zone;
- public SharedRoutingContext(ZoneId zone, ConfigServer configServer) {
+ public SharedZoneRoutingContext(ZoneId zone, ConfigServer configServer) {
this.configServer = Objects.requireNonNull(configServer);
this.zone = Objects.requireNonNull(zone);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepository.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepository.java
index 961fdc6dd9c..39a0b6a8858 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepository.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepository.java
@@ -21,7 +21,6 @@ import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Logger;
@@ -56,9 +55,11 @@ public class RotationRepository {
return new RotationLock(curator.lockRotations());
}
- /** Get rotation by given rotationId */
- public Optional<Rotation> getRotation(RotationId rotationId) {
- return Optional.of(allRotations.get(rotationId));
+ /** Get rotation with given id */
+ public Rotation requireRotation(RotationId id) {
+ Rotation rotation = allRotations.get(id);
+ if (rotation == null) throw new IllegalArgumentException("No such rotation: '" + id.asString() + "'");
+ return rotation;
}
/**
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index 30cdd1b8466..1215ddbc2ad 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -13,7 +13,6 @@ import com.yahoo.config.provision.CloudName;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.RoutingMethod;
@@ -37,10 +36,10 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
-import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId;
-import com.yahoo.vespa.hosted.controller.routing.rotation.RotationLock;
import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationLock;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
import org.junit.Test;
@@ -50,6 +49,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -233,22 +233,41 @@ public class ControllerTest {
public void testDnsUpdatesForGlobalEndpoint() {
var betaContext = tester.newDeploymentContext("tenant1", "app1", "beta");
var defaultContext = tester.newDeploymentContext("tenant1", "app1", "default");
+
+ ZoneId usWest = ZoneId.from("prod.us-west-1");
+ ZoneId usCentral = ZoneId.from("prod.us-central-1");
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
.instances("beta,default")
.endpoint("default", "foo")
- .region("us-west-1")
- .region("us-central-1") // Two deployments should result in each DNS alias being registered once
+ .region(usWest.region())
+ .region(usCentral.region()) // Two deployments should result in each DNS alias being registered once
.build();
+ tester.controllerTester().zoneRegistry().setRoutingMethod(List.of(ZoneApiMock.from(usWest), ZoneApiMock.from(usCentral)),
+ RoutingMethod.shared,
+ RoutingMethod.sharedLayer4);
betaContext.submit(applicationPackage).deploy();
{ // Expected rotation names are passed to beta instance deployments
Collection<Deployment> betaDeployments = betaContext.instance().deployments().values();
assertFalse(betaDeployments.isEmpty());
+ Set<ContainerEndpoint> containerEndpoints = Set.of(new ContainerEndpoint("foo",
+ "global",
+ List.of("beta--app1--tenant1.global.vespa.oath.cloud",
+ "rotation-id-01"),
+ OptionalInt.empty(),
+ RoutingMethod.shared),
+ new ContainerEndpoint("foo",
+ "global",
+ List.of("beta.app1.tenant1.global.vespa.oath.cloud",
+ "rotation-id-01"),
+ OptionalInt.empty(),
+ RoutingMethod.sharedLayer4));
+
for (Deployment deployment : betaDeployments) {
- assertEquals("Rotation names are passed to config server in " + deployment.zone(),
- Set.of("rotation-id-01",
- "beta--app1--tenant1.global.vespa.oath.cloud"),
- tester.configServer().containerEndpointNames(betaContext.deploymentIdIn(deployment.zone())));
+ assertEquals(containerEndpoints,
+ tester.configServer().containerEndpoints()
+ .get(betaContext.deploymentIdIn(deployment.zone())));
}
betaContext.flushDnsUpdates();
}
@@ -256,11 +275,21 @@ public class ControllerTest {
{ // Expected rotation names are passed to default instance deployments
Collection<Deployment> defaultDeployments = defaultContext.instance().deployments().values();
assertFalse(defaultDeployments.isEmpty());
+ Set<ContainerEndpoint> containerEndpoints = Set.of(new ContainerEndpoint("foo",
+ "global",
+ List.of("app1--tenant1.global.vespa.oath.cloud",
+ "rotation-id-02"),
+ OptionalInt.empty(),
+ RoutingMethod.shared),
+ new ContainerEndpoint("foo",
+ "global",
+ List.of("app1.tenant1.global.vespa.oath.cloud",
+ "rotation-id-02"),
+ OptionalInt.empty(),
+ RoutingMethod.sharedLayer4));
for (Deployment deployment : defaultDeployments) {
- assertEquals("Rotation names are passed to config server in " + deployment.zone(),
- Set.of("rotation-id-02",
- "app1--tenant1.global.vespa.oath.cloud"),
- tester.configServer().containerEndpointNames(defaultContext.deploymentIdIn(deployment.zone())));
+ assertEquals(containerEndpoints,
+ tester.configServer().containerEndpoints().get(defaultContext.deploymentIdIn(deployment.zone())));
}
defaultContext.flushDnsUpdates();
}
@@ -274,13 +303,17 @@ public class ControllerTest {
assertEquals(data, record.get().data().asString());
});
- Map<ApplicationId, List<String>> globalDnsNamesByInstance = Map.of(betaContext.instanceId(), List.of("beta--app1--tenant1.global.vespa.oath.cloud"),
- defaultContext.instanceId(), List.of("app1--tenant1.global.vespa.oath.cloud"));
+ Map<ApplicationId, Set<String>> globalDnsNamesByInstance = Map.of(betaContext.instanceId(), Set.of("beta--app1--tenant1.global.vespa.oath.cloud",
+ "beta.app1.tenant1.global.vespa.oath.cloud"),
+ defaultContext.instanceId(), Set.of("app1--tenant1.global.vespa.oath.cloud",
+ "app1.tenant1.global.vespa.oath.cloud"));
globalDnsNamesByInstance.forEach((instance, dnsNames) -> {
- List<String> actualDnsNames = tester.controller().routing().readDeclaredEndpointsOf(instance)
- .scope(Endpoint.Scope.global)
- .mapToList(Endpoint::dnsName);
+ Set<String> actualDnsNames = tester.controller().routing().readDeclaredEndpointsOf(instance)
+ .scope(Endpoint.Scope.global)
+ .asList().stream()
+ .map(Endpoint::dnsName)
+ .collect(Collectors.toSet());
assertEquals("Global DNS names for " + instance, dnsNames, actualDnsNames);
});
}
@@ -617,33 +650,46 @@ public class ControllerTest {
@Test
public void testDnsUpdatesForApplicationEndpoint() {
- var context = tester.newDeploymentContext("tenant1", "app1", "beta");
+ ApplicationId beta = ApplicationId.from("tenant1", "app1", "beta");
+ ApplicationId main = ApplicationId.from("tenant1", "app1", "main");
+ var context = tester.newDeploymentContext(beta);
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.instances("beta,main")
.region("us-west-1")
.region("us-east-3")
.applicationEndpoint("a", "default", "us-west-1",
- Map.of(InstanceName.from("beta"), 2,
- InstanceName.from("main"), 8))
+ Map.of(beta.instance(), 2,
+ main.instance(), 8))
.applicationEndpoint("b", "default", "us-west-1",
- Map.of(InstanceName.from("beta"), 1,
- InstanceName.from("main"), 1))
+ Map.of(beta.instance(), 1,
+ main.instance(), 1))
.applicationEndpoint("c", "default", "us-east-3",
- Map.of(InstanceName.from("beta"), 4,
- InstanceName.from("main"), 6))
+ Map.of(beta.instance(), 4,
+ main.instance(), 6))
.build();
context.submit(applicationPackage).deploy();
- // Endpoint names are passed to each deployment
- DeploymentId usWest = context.deploymentIdIn(ZoneId.from("prod", "us-west-1"));
- DeploymentId usEast = context.deploymentIdIn(ZoneId.from("prod", "us-east-3"));
- Map<DeploymentId, List<String>> deploymentEndpoints = Map.of(usWest, List.of("a.app1.tenant1.us-west-1-r.vespa.oath.cloud", "b.app1.tenant1.us-west-1-r.vespa.oath.cloud"),
- usEast, List.of("c.app1.tenant1.us-east-3-r.vespa.oath.cloud"));
- deploymentEndpoints.forEach((zone, endpointNames) -> {
- assertEquals("Endpoint names are passed to config server in " + zone,
- Set.of(new ContainerEndpoint("default", "application",
- endpointNames)),
- tester.configServer().containerEndpoints().get(zone));
+ ZoneId usWest = ZoneId.from("prod", "us-west-1");
+ ZoneId usEast = ZoneId.from("prod", "us-east-3");
+ // Expected container endpoints are passed to each deployment
+ Map<DeploymentId, Map<String, Integer>> deploymentEndpoints = Map.of(
+ new DeploymentId(beta, usWest), Map.of("a.app1.tenant1.us-west-1-r.vespa.oath.cloud", 2,
+ "b.app1.tenant1.us-west-1-r.vespa.oath.cloud", 1),
+ new DeploymentId(main, usWest), Map.of("a.app1.tenant1.us-west-1-r.vespa.oath.cloud", 8,
+ "b.app1.tenant1.us-west-1-r.vespa.oath.cloud", 1),
+ new DeploymentId(beta, usEast), Map.of("c.app1.tenant1.us-east-3-r.vespa.oath.cloud", 4),
+ new DeploymentId(main, usEast), Map.of("c.app1.tenant1.us-east-3-r.vespa.oath.cloud", 6)
+ );
+ deploymentEndpoints.forEach((deployment, endpoints) -> {
+ Set<ContainerEndpoint> expected = endpoints.entrySet().stream()
+ .map(kv -> new ContainerEndpoint("default", "application",
+ List.of(kv.getKey()),
+ OptionalInt.of(kv.getValue()),
+ RoutingMethod.sharedLayer4))
+ .collect(Collectors.toSet());
+ assertEquals("Endpoint names for " + deployment + " are passed to config server",
+ expected,
+ tester.configServer().containerEndpoints().get(deployment));
});
context.flushDnsUpdates();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
index 1d41beb8a99..537f6c48bdf 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
@@ -8,6 +8,7 @@ import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.controller.ControllerTester;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
import com.yahoo.vespa.hosted.controller.api.integration.user.User;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
@@ -270,4 +271,27 @@ public class UserApiTest extends ControllerContainerCloudTest {
new File("user-without-trial-capacity-cloud.json"));
}
}
+
+ @Test
+ public void supportTenant() {
+ try (Flags.Replacer ignored = Flags.clearFlagsForTesting(PermanentFlags.MAX_TRIAL_TENANTS.id(), PermanentFlags.ENABLE_PUBLIC_SIGNUP_FLOW.id())) {
+ ContainerTester tester = new ContainerTester(container, responseFiles);
+ ((InMemoryFlagSource) tester.controller().flagSource())
+ .withIntFlag(PermanentFlags.MAX_TRIAL_TENANTS.id(), 10)
+ .withBooleanFlag(PermanentFlags.ENABLE_PUBLIC_SIGNUP_FLOW.id(), true);
+ ControllerTester controller = new ControllerTester(tester);
+ User user = new User("dev@domail", "Joe Developer", "dev", null);
+
+ var tenant1 = controller.createTenant("tenant1", Tenant.Type.cloud);
+ var tenant2 = controller.createTenant("tenant2", Tenant.Type.cloud);
+ controller.serviceRegistry().billingController().setPlan(tenant2, PlanId.from("paid"), false);
+
+ tester.assertResponse(
+ request("/user/v1/user")
+ .roles(Role.reader(tenant1), Role.reader(tenant2))
+ .user(user),
+ new File("user-with-supported-tenant.json"));
+ }
+
+ }
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
index 006c3b98a4d..0211f595ce7 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
@@ -10,6 +10,7 @@
},
"tenants": {
"sandbox": {
+ "supported": (ignore),
"roles": [
"administrator",
"developer",
@@ -17,6 +18,7 @@
]
},
"tenant1": {
+ "supported": (ignore),
"roles": [
"administrator",
"developer",
@@ -24,6 +26,7 @@
]
},
"tenant2": {
+ "supported": (ignore),
"roles": [
"administrator",
"developer",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
index 4ae55e97baa..76904bf9bb4 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
@@ -10,17 +10,20 @@
},
"tenants": {
"sandbox": {
+ "supported": false,
"roles": [
"developer",
"reader"
]
},
"tenant1": {
+ "supported": false,
"roles": [
"administrator"
]
},
"tenant2": {
+ "supported": false,
"roles": [
"developer"
]
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-supported-tenant.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-supported-tenant.json
new file mode 100644
index 00000000000..a40354a9e71
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-supported-tenant.json
@@ -0,0 +1,35 @@
+{
+ "isPublic": true,
+ "isCd": false,
+ "hasTrialCapacity": true,
+ "user": {
+ "name": "Joe Developer",
+ "email": "dev@domail",
+ "nickname": "dev",
+ "verified": false
+ },
+ "tenants": {
+ "tenant1": {
+ "supported": false,
+ "roles": [
+ "reader"
+ ]
+ },
+ "tenant2": {
+ "supported": true,
+ "roles": [
+ "reader"
+ ]
+ }
+ },
+ "flags": [
+ {
+ "id": "enable-public-signup-flow",
+ "rules": [
+ {
+ "value": false
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java
index 9a3ac8b547d..9a56123e8e3 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java
@@ -205,10 +205,9 @@ public class RotationRepositoryTest {
private void assertSingleRotation(Rotation expected, List<AssignedRotation> assignedRotations, RotationRepository repository) {
assertEquals(1, assignedRotations.size());
- var rotationId = assignedRotations.get(0).rotationId();
- var rotation = repository.getRotation(rotationId);
- assertTrue(rotationId + " exists", rotation.isPresent());
- assertEquals(expected, rotation.get());
+ RotationId rotationId = assignedRotations.get(0).rotationId();
+ Rotation rotation = repository.requireRotation(rotationId);
+ assertEquals(expected, rotation);
}
private static List<RotationId> rotationIds(List<AssignedRotation> assignedRotations) {