diff options
Diffstat (limited to 'controller-server/src/main/java')
6 files changed, 137 insertions, 60 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 38c06e4dac2..84aa26b93f3 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 @@ -20,6 +20,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; +import com.yahoo.vespa.hosted.controller.application.Endpoint.Scope; import com.yahoo.vespa.hosted.controller.application.EndpointId; import com.yahoo.vespa.hosted.controller.application.EndpointList; import com.yahoo.vespa.hosted.controller.application.SystemApplication; @@ -115,7 +116,7 @@ public class RoutingController { if (!policy.status().isActive()) continue; RoutingMethod routingMethod = controller.zoneRegistry().routingMethod(policy.id().zone()); endpoints.addAll(policy.zoneEndpointsIn(controller.system(), routingMethod, controller.zoneRegistry())); - endpoints.add(policy.regionEndpointIn(controller.system(), routingMethod)); + endpoints.add(policy.regionEndpointIn(controller.system(), routingMethod, controller.zoneRegistry())); } return EndpointList.copyOf(endpoints); } @@ -158,23 +159,21 @@ public class RoutingController { } // Add application endpoints for (var declaredEndpoint : deploymentSpec.endpoints()) { - Map<ZoneId, Map<DeploymentId, Integer>> deployments = declaredEndpoint.targets().stream() - .collect(groupingBy(t -> ZoneId.from(Environment.prod, t.region()), - toMap(t -> new DeploymentId(application.id().instance(t.instance()), - ZoneId.from(Environment.prod, t.region())), - t -> t.weight()))); - - deployments.forEach((zone, weightedInstances) -> { - // Application endpoints are only supported when using direct routing methods - RoutingMethod routingMethod = usesSharedRouting(zone) ? RoutingMethod.sharedLayer4 : RoutingMethod.exclusive; - endpoints.add(Endpoint.of(application.id()) - .targetApplication(EndpointId.of(declaredEndpoint.endpointId()), - ClusterSpec.Id.from(declaredEndpoint.containerId()), - weightedInstances) - .routingMethod(routingMethod) - .on(Port.fromRoutingMethod(routingMethod)) - .in(controller.system())); - }); + Map<DeploymentId, Integer> deployments = declaredEndpoint.targets().stream() + .collect(toMap(t -> new DeploymentId(application.id().instance(t.instance()), + ZoneId.from(Environment.prod, t.region())), + t -> t.weight())); + + ZoneId zone = deployments.keySet().iterator().next().zoneId(); // Where multiple zones are possible, they all have the same routing method. + // Application endpoints are only supported when using direct routing methods + RoutingMethod routingMethod = usesSharedRouting(zone) ? RoutingMethod.sharedLayer4 : RoutingMethod.exclusive; + endpoints.add(Endpoint.of(application.id()) + .targetApplication(EndpointId.of(declaredEndpoint.endpointId()), + ClusterSpec.Id.from(declaredEndpoint.containerId()), + deployments) + .routingMethod(routingMethod) + .on(Port.fromRoutingMethod(routingMethod)) + .in(controller.system())); } return EndpointList.copyOf(endpoints); } @@ -236,6 +235,7 @@ public class RoutingController { .on(Port.tls()) .in(controller.system()); endpointDnsNames.add(endpoint.dnsName()); + if (endpoint.scope() == Scope.application) endpointDnsNames.add(endpoint.legacyRegionalDnsName()); } return Collections.unmodifiableList(endpointDnsNames); } @@ -313,6 +313,9 @@ public class RoutingController { controller.nameServiceForwarder().createRecord( new Record(Record.Type.CNAME, RecordName.from(endpoint.dnsName()), RecordData.fqdn(vipHostname)), Priority.normal); + controller.nameServiceForwarder().createRecord( + new Record(Record.Type.CNAME, RecordName.from(endpoint.legacyRegionalDnsName()), RecordData.fqdn(vipHostname)), + Priority.normal); } Map<ClusterSpec.Id, EndpointList> applicationEndpointsByCluster = applicationEndpoints.groupingBy(Endpoint::cluster); for (var kv : applicationEndpointsByCluster.entrySet()) { @@ -325,7 +328,7 @@ public class RoutingController { 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()), + List.of(endpoint.dnsName(), endpoint.legacyRegionalDnsName()), OptionalInt.of(matchingTarget.get().weight()), endpoint.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 88366466289..cbac700a9a0 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 @@ -20,9 +20,11 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.util.Comparator.comparing; + /** * Represents an application or instance endpoint in hosted Vespa. - * + * <p> * This encapsulates the logic for building URLs and DNS names for applications in all hosted Vespa systems. * * @author mpolden @@ -38,13 +40,14 @@ public class Endpoint { private final ClusterSpec.Id cluster; private final Optional<InstanceName> instance; private final URI url; + private final URI legacyRegionalUrl; private final List<Target> targets; private final Scope scope; private final boolean legacy; private final RoutingMethod routingMethod; private Endpoint(TenantAndApplicationId application, Optional<InstanceName> instanceName, EndpointId id, - ClusterSpec.Id cluster, URI url, List<Target> targets, Scope scope, Port port, boolean legacy, + ClusterSpec.Id cluster, URI url, URI legacyRegionalUrl, List<Target> targets, Scope scope, Port port, boolean legacy, RoutingMethod routingMethod, boolean certificateName) { Objects.requireNonNull(application, "application must be non-null"); Objects.requireNonNull(instanceName, "instanceName must be non-null"); @@ -58,6 +61,7 @@ public class Endpoint { this.cluster = requireCluster(cluster, certificateName); this.instance = requireInstance(instanceName, scope); this.url = url; + this.legacyRegionalUrl = legacyRegionalUrl; this.targets = List.copyOf(requireTargets(targets, application, instanceName, scope, certificateName)); this.scope = requireScope(scope, routingMethod); this.legacy = legacy; @@ -96,6 +100,12 @@ public class Endpoint { return url.getAuthority().replaceAll(":.*", ""); } + /** Returns the legacy DNS name with region, for application endpoints */ + public String legacyRegionalDnsName() { + if (scope != Scope.application) throw new IllegalStateException("legacy regional URL is only for application scope endpoints, not " + this); + return legacyRegionalUrl.getAuthority().replaceAll(":.*", ""); + } + /** Returns the target(s) to which this routes traffic */ public List<Target> targets() { return targets; @@ -160,7 +170,8 @@ public class Endpoint { } private static URI createUrl(String name, TenantAndApplicationId application, Optional<InstanceName> instance, - List<Target> targets, Scope scope, SystemName system, Port port, boolean legacy) { + List<Target> targets, Scope scope, SystemName system, Port port, boolean legacyRegionalUrl) { + String separator = "."; String portPart = port.isDefault() ? "" : ":" + port.port; return URI.create("https://" + @@ -171,8 +182,8 @@ public class Endpoint { separator + sanitize(application.tenant().value()) + "." + - scopePart(scope, targets, system, legacy) + - dnsSuffix(system, legacy) + + scopePart(scope, targets, system, legacyRegionalUrl) + + dnsSuffix(system) + portPart + "/"); } @@ -186,13 +197,14 @@ public class Endpoint { return name + separator; } - private static String scopePart(Scope scope, List<Target> targets, SystemName system, boolean legacy) { - String scopeSymbol = scopeSymbol(scope, system); + private static String scopePart(Scope scope, List<Target> targets, SystemName system, boolean legacyRegion) { + String scopeSymbol = scopeSymbol(scope, system, legacyRegion); if (scope == Scope.global) return scopeSymbol; + if (scope == Scope.application && ! legacyRegion) return scopeSymbol; - ZoneId zone = targets.get(0).deployment().zoneId(); + ZoneId zone = targets.stream().map(target -> target.deployment.zoneId()).min(comparing(ZoneId::value)).get(); String region = zone.region().value(); - boolean skipEnvironment = zone.environment().isProduction() && (system.isPublic() || !legacy); + boolean skipEnvironment = zone.environment().isProduction(); String environment = skipEnvironment ? "" : "." + zone.environment().value(); if (system.isPublic()) { return region + environment + "." + scopeSymbol; @@ -200,20 +212,21 @@ public class Endpoint { return region + (scopeSymbol.isEmpty() ? "" : "-" + scopeSymbol) + environment; } - private static String scopeSymbol(Scope scope, SystemName system) { + private static String scopeSymbol(Scope scope, SystemName system, boolean legacyRegion) { + if (legacyRegion) return "r"; if (system.isPublic()) { return switch (scope) { case zone -> "z"; case weighted -> "w"; case global -> "g"; - case application -> "r"; + case application -> "a"; }; } return switch (scope) { case zone -> ""; case weighted -> "w"; case global -> "global"; - case application -> "r"; + case application -> "a"; }; } @@ -230,18 +243,15 @@ public class Endpoint { } /** Returns the DNS suffix used for endpoints in given system */ - private static String dnsSuffix(SystemName system, boolean legacy) { + private static String dnsSuffix(SystemName system) { switch (system) { case cd, main -> { - if (legacy) return YAHOO_DNS_SUFFIX; return OATH_DNS_SUFFIX; } case Public -> { - if (legacy) throw new IllegalArgumentException("No legacy DNS suffix declared for system " + system); return PUBLIC_DNS_SUFFIX; } case PublicCd -> { - if (legacy) throw new IllegalArgumentException("No legacy DNS suffix declared for system " + system); return PUBLIC_CD_DNS_SUFFIX; } default -> throw new IllegalArgumentException("No DNS suffix declared for system " + system); @@ -250,7 +260,7 @@ public class Endpoint { /** Returns the DNS suffix used for internal names (i.e. names not exposed to tenants) in given system */ public static String internalDnsSuffix(SystemName system) { - String suffix = dnsSuffix(system, false); + String suffix = dnsSuffix(system); if (system.isPublic()) { // Certificate provider requires special approval for three-level DNS names, e.g. foo.vespa-app.cloud. // To avoid this in public we always add an extra level. @@ -578,12 +588,22 @@ public class Endpoint { Objects.requireNonNull(scope, "scope must be non-null"), Objects.requireNonNull(system, "system must be non-null"), Objects.requireNonNull(port, "port must be non-null"), - legacy); + false); + URI legacyRegionalUrl = scope != Scope.application ? null + : createUrl(endpointOrClusterAsString(endpointId, cluster), + Objects.requireNonNull(application, "application must be non-null"), + Objects.requireNonNull(instance, "instance must be non-null"), + Objects.requireNonNull(targets, "targets must be non-null"), + Objects.requireNonNull(scope, "scope must be non-null"), + Objects.requireNonNull(system, "system must be non-null"), + Objects.requireNonNull(port, "port must be non-null"), + true); return new Endpoint(application, instance, endpointId, cluster, url, + legacyRegionalUrl, targets, scope, port, 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 9c6ab32a338..89ca31105bb 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 @@ -78,6 +78,7 @@ public class ControllerMaintenance extends AbstractComponent { maintainers.add(new UserManagementMaintainer(controller, intervals.userManagementMaintainer, controller.serviceRegistry().roleMaintainer())); maintainers.add(new BillingDatabaseMaintainer(controller, intervals.billingDatabaseMaintainer)); maintainers.add(new MeteringMonitorMaintainer(controller, intervals.meteringMonitorMaintainer, controller.serviceRegistry().resourceDatabase(), metric)); + maintainers.add(new EnclaveAccessMaintainer(controller, intervals.defaultInterval)); } public Upgrader upgrader() { return upgrader; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainer.java new file mode 100644 index 00000000000..d9576f4e176 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainer.java @@ -0,0 +1,43 @@ +package com.yahoo.vespa.hosted.controller.maintenance; + +import com.yahoo.config.provision.CloudAccount; +import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.tenant.Tenant; + +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; + +public class EnclaveAccessMaintainer extends ControllerMaintainer { + + private static final Logger logger = Logger.getLogger(EnclaveAccessMaintainer.class.getName()); + + EnclaveAccessMaintainer(Controller controller, Duration interval) { + super(controller, interval); + } + + @Override + protected double maintain() { + try { + controller().serviceRegistry().enclaveAccessService().allowAccessFor(externalAccounts()); + return 1; + } + catch (RuntimeException e) { + logger.log(WARNING, "Failed sharing AMIs", e); + return 0; + } + } + + private Set<CloudAccount> externalAccounts() { + Set<CloudAccount> accounts = new HashSet<>(); + for (Tenant tenant : controller().tenants().asList()) + accounts.addAll(controller().applications().accountsOf(tenant.name())); + + return accounts; + } + + +} 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 27247c065ed..b0d16126600 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 @@ -215,7 +215,7 @@ public class RoutingPolicies { for (var policy : policies) { if (policy.dnsZone().isEmpty() && policy.canonicalName().isPresent()) continue; if (controller.zoneRegistry().routingMethod(policy.id().zone()) != RoutingMethod.exclusive) continue; - Endpoint endpoint = policy.regionEndpointIn(controller.system(), RoutingMethod.exclusive); + Endpoint endpoint = policy.regionEndpointIn(controller.system(), RoutingMethod.exclusive, controller.zoneRegistry()); var zonePolicy = db.readZoneRoutingPolicy(policy.id().zone()); long weight = 1; if (isConfiguredOut(zonePolicy, policy, inactiveZones)) { @@ -289,12 +289,10 @@ public class RoutingPolicies { activeTargets.addAll(inactiveTargets); inactiveTargets.clear(); } + targetsByEndpoint.forEach((applicationEndpoint, targets) -> { - ZoneId targetZone = applicationEndpoint.targets().stream() - .map(Endpoint.Target::deployment) - .map(DeploymentId::zoneId) - .findFirst() - .get(); + // Where multiple zones are permitted, they all have the same routing policy, and nameServiceForwarder (below). + ZoneId targetZone = applicationEndpoint.targets().iterator().next().deployment().zoneId(); Set<AliasTarget> aliasTargets = new LinkedHashSet<>(); Set<DirectTarget> directTargets = new LinkedHashSet<>(); for (Target target : targets) { @@ -305,23 +303,28 @@ public class RoutingPolicies { if ( ! aliasTargets.isEmpty()) { nameServiceForwarderIn(targetZone).createAlias( RecordName.from(applicationEndpoint.dnsName()), aliasTargets, Priority.normal); + nameServiceForwarderIn(targetZone).createAlias( + RecordName.from(applicationEndpoint.legacyRegionalDnsName()), aliasTargets, Priority.normal); } if ( ! directTargets.isEmpty()) { nameServiceForwarderIn(targetZone).createDirect( RecordName.from(applicationEndpoint.dnsName()), directTargets, Priority.normal); + nameServiceForwarderIn(targetZone).createDirect( + RecordName.from(applicationEndpoint.legacyRegionalDnsName()), directTargets, Priority.normal); } }); inactiveTargetsByEndpoint.forEach((applicationEndpoint, targets) -> { - ZoneId targetZone = applicationEndpoint.targets().stream() - .map(Endpoint.Target::deployment) - .map(DeploymentId::zoneId) - .findFirst() - .get(); + // Where multiple zones are permitted, they all have the same routing policy, and nameServiceForwarder. + ZoneId targetZone = applicationEndpoint.targets().iterator().next().deployment().zoneId(); targets.forEach(target -> { nameServiceForwarderIn(targetZone).removeRecords(target.type(), RecordName.from(applicationEndpoint.dnsName()), target.data(), Priority.normal); + nameServiceForwarderIn(targetZone).removeRecords(target.type(), + RecordName.from(applicationEndpoint.legacyRegionalDnsName()), + target.data(), + Priority.normal); }); }); } @@ -377,9 +380,8 @@ public class RoutingPolicies { .not().matching(policy -> activeIds.contains(policy.id())); for (var policy : removable) { for (var endpoint : policy.zoneEndpointsIn(controller.system(), RoutingMethod.exclusive, controller.zoneRegistry())) { - var dnsName = endpoint.dnsName(); nameServiceForwarderIn(allocation.deployment.zoneId()).removeRecords(Record.Type.CNAME, - RecordName.from(dnsName), + RecordName.from(endpoint.dnsName()), Priority.normal); } newPolicies.remove(policy.id()); @@ -424,14 +426,22 @@ public class RoutingPolicies { for (Endpoint endpoint : endpoints) { if (policy.canonicalName().isPresent()) { forwarder.removeRecords(Record.Type.ALIAS, - RecordName.from(endpoint.dnsName()), - RecordData.fqdn(policy.canonicalName().get().value()), - Priority.normal); + RecordName.from(endpoint.dnsName()), + RecordData.fqdn(policy.canonicalName().get().value()), + Priority.normal); + forwarder.removeRecords(Record.Type.ALIAS, + RecordName.from(endpoint.legacyRegionalDnsName()), + RecordData.fqdn(policy.canonicalName().get().value()), + Priority.normal); } else { forwarder.removeRecords(Record.Type.DIRECT, - RecordName.from(endpoint.dnsName()), - RecordData.from(policy.ipAddress().get()), - Priority.normal); + RecordName.from(endpoint.dnsName()), + RecordData.from(policy.ipAddress().get()), + Priority.normal); + forwarder.removeRecords(Record.Type.DIRECT, + RecordName.from(endpoint.legacyRegionalDnsName()), + RecordData.from(policy.ipAddress().get()), + Priority.normal); } } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java index 04c32590a4c..6ae729a3c02 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java @@ -96,12 +96,12 @@ public record RoutingPolicy(RoutingPolicyId id, /** Returns the zone endpoints of this */ public List<Endpoint> zoneEndpointsIn(SystemName system, RoutingMethod routingMethod, ZoneRegistry zoneRegistry) { DeploymentId deployment = new DeploymentId(id.owner(), id.zone()); - return List.of(endpoint(routingMethod).target(id.cluster(), deployment).in(system)); + return List.of(endpoint(routingMethod, zoneRegistry).target(id.cluster(), deployment).in(system)); } /** Returns the region endpoint of this */ - public Endpoint regionEndpointIn(SystemName system, RoutingMethod routingMethod) { - return endpoint(routingMethod).targetRegion(id.cluster(), id.zone()).in(system); + public Endpoint regionEndpointIn(SystemName system, RoutingMethod routingMethod, ZoneRegistry zoneRegistry) { + return endpoint(routingMethod, zoneRegistry).targetRegion(id.cluster(), id.zone()).in(system); } @Override @@ -125,7 +125,7 @@ public record RoutingPolicy(RoutingPolicyId id, id.zone().value()); } - private Endpoint.EndpointBuilder endpoint(RoutingMethod routingMethod) { + private Endpoint.EndpointBuilder endpoint(RoutingMethod routingMethod, ZoneRegistry zones) { return Endpoint.of(id.owner()) .on(Port.fromRoutingMethod(routingMethod)) .routingMethod(routingMethod); |