diff options
author | Martin Polden <mpolden@mpolden.no> | 2023-05-14 20:25:39 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-14 20:25:39 +0200 |
commit | f302342ae4ba920dbcc49d4787fa3b81ac97545a (patch) | |
tree | 6c96ce5ef0cbf5b265bd7ba2bde4ce78f203aca2 | |
parent | ecc2411379f53e7bd2374512c0596d2d6e817c99 (diff) |
Handle removal of endpoints with duplicated IDs across scopes (#27099)
4 files changed, 38 insertions, 13 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 fa8851c414d..c76616c6d2c 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 @@ -256,7 +256,7 @@ public class RoutingController { EndpointList endpoints = declaredEndpointsOf(application.get()).targets(deployment); EndpointList globalEndpoints = endpoints.scope(Endpoint.Scope.global); for (var assignedRotation : instance.rotations()) { - EndpointList rotationEndpoints = globalEndpoints.named(assignedRotation.endpointId()) + EndpointList rotationEndpoints = globalEndpoints.named(assignedRotation.endpointId(), Scope.global) .requiresRotation(); // Skip rotations which do not apply to this zone. Legacy names always point to all zones 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 7fe8d554998..3da9065b52d 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 @@ -32,9 +32,10 @@ public class EndpointList extends AbstractFilteringList<Endpoint, EndpointList> return not().legacy().asList().stream().findFirst(); } - /** Returns the subset of endpoints named according to given ID */ - public EndpointList named(EndpointId id) { - return matching(endpoint -> endpoint.name().equals(id.id())); + /** Returns the subset of endpoints named according to given ID and scope */ + public EndpointList named(EndpointId id, Endpoint.Scope scope) { + return matching(endpoint -> endpoint.scope() == scope && // ID is only unique within a scope + endpoint.name().equals(id.id())); } /** Returns the subset of endpoints pointing to given cluster */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java index 637393d71cb..59f886bdea6 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java @@ -170,7 +170,7 @@ public class RoutingPolicies { for (Map.Entry<RoutingId, List<RoutingPolicy>> routeEntry : routingTable.entrySet()) { RoutingId routingId = routeEntry.getKey(); controller.routing().readDeclaredEndpointsOf(routingId.instance()) - .named(routingId.endpointId()) + .named(routingId.endpointId(), Endpoint.Scope.global) .not().requiresRotation() .forEach(endpoint -> updateGlobalDnsOf(endpoint, inactiveZones, routeEntry.getValue(), owner)); } @@ -269,8 +269,7 @@ public class RoutingPolicies { for (Map.Entry<RoutingId, List<RoutingPolicy>> routeEntry : routingTable.entrySet()) { RoutingId routingId = routeEntry.getKey(); EndpointList endpoints = controller.routing().declaredEndpointsOf(application) - .scope(Endpoint.Scope.application) - .named(routingId.endpointId()); + .named(routingId.endpointId(), Endpoint.Scope.application); for (Endpoint endpoint : endpoints) { for (var policy : routeEntry.getValue()) { for (var target : endpoint.targets()) { @@ -471,7 +470,7 @@ public class RoutingPolicies { for (var id : removalCandidates) { EndpointList endpoints = controller.routing().readDeclaredEndpointsOf(id.instance()) .not().requiresRotation() - .named(id.endpointId()); + .named(id.endpointId(), Endpoint.Scope.global); NameServiceForwarder forwarder = nameServiceForwarderIn(allocation.deployment.zoneId()); // This removes all ALIAS records having this DNS name. There is no attempt to delete only the entry for the // affected zone. Instead, the correct set of records is (re)created by updateGlobalDnsOf @@ -491,7 +490,7 @@ public class RoutingPolicies { TenantAndApplicationId application = TenantAndApplicationId.from(id.instance()); EndpointList endpoints = controller.routing() .readDeclaredEndpointsOf(application) - .named(id.endpointId()); + .named(id.endpointId(), Endpoint.Scope.application); List<RoutingPolicy> policies = routingTable.get(id); for (var policy : policies) { if (!policy.appliesTo(allocation.deployment)) continue; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java index a90d942bd09..24d5e02240d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java @@ -846,8 +846,8 @@ public class RoutingPoliciesTest { tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, Map.of(betaZone5, 1)); assertTrue(tester.controllerTester().controller().routing() - .readDeclaredEndpointsOf(application) - .named(EndpointId.of("a1")).isEmpty(), + .readDeclaredEndpointsOf(application) + .named(EndpointId.of("a1"), Endpoint.Scope.application).isEmpty(), "Endpoint removed"); } @@ -917,6 +917,31 @@ public class RoutingPoliciesTest { mainZone2, 9)); } + @Test + public void duplicate_endpoint_ids_across_different_scopes() { + RoutingPoliciesTester tester = new RoutingPoliciesTester(); + ApplicationId instance = ApplicationId.from("t1", "a1", "i1"); + DeploymentContext context = tester.newDeploymentContext(instance); + var applicationPackage = applicationPackageBuilder() + .instances(instance.instance().value()) + .region(zone1.region()) + .region(zone2.region()) + .endpoint("default", "c0") + .applicationEndpoint("default", "c0", zone1.region().value(), + Map.of(instance.instance(), 1)) + .build(); + tester.provisionLoadBalancers(1, instance, zone1, zone2); + context.submit(applicationPackage).deploy(); + tester.assertTargets(instance, EndpointId.defaultId(), 0, zone1, zone2); + tester.assertTargets(TenantAndApplicationId.from(instance), EndpointId.defaultId(), + ClusterSpec.Id.from("c0"), 0, Map.of(context.deploymentIdIn(zone1), 1)); + + tester.controllerTester().controller().applications().deactivate(context.instanceId(), zone1); + tester.controllerTester().controller().applications().deactivate(context.instanceId(), zone2); + assertTrue(tester.controllerTester().controller().routing().policies().read(context.instanceId()).isEmpty(), + "Policies removed"); + } + /** Returns an application package builder that satisfies requirements for a directly routed endpoint */ private static ApplicationPackageBuilder applicationPackageBuilder() { return new ApplicationPackageBuilder().athenzIdentity(AthenzDomain.from("domain"), @@ -1053,7 +1078,7 @@ public class RoutingPoliciesTest { Map<String, List<DeploymentId>> deploymentsByDnsName = new HashMap<>(); for (var deployment : deploymentWeights.keySet()) { EndpointList applicationEndpoints = tester.controller().routing().readDeclaredEndpointsOf(application) - .named(endpointId) + .named(endpointId, Endpoint.Scope.application) .targets(deployment) .cluster(cluster); assertEquals(1, @@ -1103,7 +1128,7 @@ public class RoutingPoliciesTest { }); List<DeploymentId> deployments = zoneWeights.keySet().stream().map(z -> new DeploymentId(instance, z)).toList(); String globalEndpoint = tester.controller().routing().readDeclaredEndpointsOf(instance) - .named(endpointId) + .named(endpointId, Endpoint.Scope.global) .targets(deployments) .primary() .map(Endpoint::dnsName) |