aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/go/script-utils/main.go9
-rw-r--r--client/go/vespa/prestart.go19
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/filter/util/FilterTestUtils.java103
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/EnclaveAccessService.java15
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/MockEnclaveAccessService.java22
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java41
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java56
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainer.java43
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java48
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java59
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java43
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java26
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java30
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java15
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainerTest.java39
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json3
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java7
-rw-r--r--jdisc-security-filters/pom.xml10
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java20
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java25
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java4
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilterTest.java7
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/LocalhostFilterTest.java15
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java10
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/rule/RuleBasedRequestFilterTest.java8
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java1
-rw-r--r--parent/pom.xml5
-rw-r--r--searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp8
-rw-r--r--security-utils/src/main/java/com/yahoo/security/AeadCipher.java44
-rw-r--r--security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java37
-rw-r--r--security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java34
-rw-r--r--vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/CipherUtils.java10
43 files changed, 642 insertions, 219 deletions
diff --git a/client/go/script-utils/main.go b/client/go/script-utils/main.go
index 713a208a319..9d35cb46e1c 100644
--- a/client/go/script-utils/main.go
+++ b/client/go/script-utils/main.go
@@ -50,6 +50,13 @@ func main() {
} else {
os.Exit(1)
}
+ case "detect-hostname":
+ myName, err := vespa.FindOurHostname()
+ fmt.Println(myName)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
case "vespa-deploy":
cobra := deploy.NewDeployCmd()
cobra.Execute()
@@ -70,7 +77,7 @@ func main() {
os.Exit(startcbinary.Run(os.Args))
}
fmt.Fprintf(os.Stderr, "unknown action '%s'\n", action)
- fmt.Fprintln(os.Stderr, "actions: export-env, ipv6-only, security-env")
+ fmt.Fprintln(os.Stderr, "actions: export-env, ipv6-only, security-env, detect-hostname")
fmt.Fprintln(os.Stderr, "(also: vespa-deploy, vespa-logfmt)")
}
}
diff --git a/client/go/vespa/prestart.go b/client/go/vespa/prestart.go
index eb4a598ef37..c102e1ed41b 100644
--- a/client/go/vespa/prestart.go
+++ b/client/go/vespa/prestart.go
@@ -31,7 +31,6 @@ func RunPreStart() error {
}
fixSpec.FixDir("logs")
fixSpec.FixDir("logs/vespa")
- fixSpec.FixDir("logs/vespa/access")
fixSpec.FixDir("logs/vespa/configserver")
fixSpec.FixDir("logs/vespa/search")
fixSpec.FixDir("var/tmp")
@@ -54,9 +53,10 @@ func RunPreStart() error {
fixSpec.FixDir("var/vespa/bundlecache")
fixSpec.FixDir("var/vespa/bundlecache/configserver")
fixSpec.FixDir("var/vespa/cache/config")
+ // fix wrong ownerships within directories:
var fixer fs.WalkDirFunc = func(path string, d fs.DirEntry, err error) error {
if err != nil {
- panic(err)
+ util.JustExitWith(err)
}
if d.IsDir() {
fixSpec.FixDir(path)
@@ -68,5 +68,20 @@ func RunPreStart() error {
fileSystem := os.DirFS(vespaHome)
fs.WalkDir(fileSystem, "logs/vespa", fixer)
fs.WalkDir(fileSystem, "var/db/vespa", fixer)
+ // we used to have (Vespa 7) a directory "qrs", with "access" a symlink to that.
+ // now we want it the other way around, try to make it so:
+ const lva string = "logs/vespa/access"
+ const lvq string = "logs/vespa/qrs"
+ info, err := os.Lstat(lva)
+ if err == nil && (info.Mode()&os.ModeSymlink) != 0 {
+ err = os.Remove(lva)
+ if err != nil {
+ return err
+ }
+ }
+ fixSpec.FixDir(lva)
+ // best-effort fixup, not trying too hard to get backwards compatibility:
+ _ = os.Remove(lvq)
+ _ = os.Symlink("access", lvq)
return nil
}
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/filter/util/FilterTestUtils.java b/container-core/src/main/java/com/yahoo/jdisc/http/filter/util/FilterTestUtils.java
new file mode 100644
index 00000000000..147c13c8e3a
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/filter/util/FilterTestUtils.java
@@ -0,0 +1,103 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.filter.util;
+
+import com.yahoo.jdisc.Container;
+import com.yahoo.jdisc.References;
+import com.yahoo.jdisc.Request;
+import com.yahoo.jdisc.ResourceReference;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.RequestHandler;
+import com.yahoo.jdisc.handler.ResponseHandler;
+import com.yahoo.jdisc.http.Cookie;
+import com.yahoo.jdisc.http.HttpRequest;
+import com.yahoo.jdisc.http.HttpRequest.Method;
+import com.yahoo.jdisc.http.HttpRequest.Version;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
+import com.yahoo.jdisc.http.filter.SecurityResponseFilter;
+import com.yahoo.jdisc.http.server.jetty.RequestUtils;
+import com.yahoo.jdisc.service.CurrentContainer;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.time.Clock;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import static com.yahoo.jdisc.http.HttpRequest.Version.HTTP_1_1;
+
+/**
+ * Test helper for {@link SecurityRequestFilter}/{@link SecurityResponseFilter}.
+ *
+ * @author bjorncs
+ */
+public class FilterTestUtils {
+
+ private FilterTestUtils() {}
+
+ public static RequestBuilder newRequestBuilder() { return new RequestBuilder(); }
+
+ public static class RequestBuilder {
+
+ private URI uri = URI.create("https://localhost:443/");
+ private Method method = Method.GET;
+ private Clock clock = Clock.systemUTC();
+ private Principal principal;
+ private final Map<String, Object> attributes = new TreeMap<>();
+ private List<X509Certificate> certificates = List.of();
+ private final Map<String, String> headers = new TreeMap<>();
+ private Version version = HTTP_1_1;
+ private SocketAddress remoteAddress;
+ private final List<Cookie> cookies = new ArrayList<>();
+
+ private RequestBuilder() {}
+
+ public RequestBuilder withUri(String uri) { return withUri(URI.create(uri)); }
+ public RequestBuilder withUri(URI uri) { this.uri = uri; return this; }
+ public RequestBuilder withMethod(String method) { return withMethod(Method.valueOf(method)); }
+ public RequestBuilder withMethod(Method method) { this.method = method; return this; }
+ public RequestBuilder withClock(Clock clock) { this.clock = clock; return this; }
+ public RequestBuilder withPrincipal(Principal principal) { this.principal = principal; return this; }
+ public RequestBuilder withAttribute(String name, Object value) { attributes.put(name, value); return this; }
+ public RequestBuilder withClientCertificate(X509Certificate cert) { return withClientCertificate(List.of(cert)); }
+ public RequestBuilder withClientCertificate(List<X509Certificate> certs) { certificates = List.copyOf(certs); return this; }
+ public RequestBuilder withHeader(String name, String value) { headers.put(name, value); return this; }
+ public RequestBuilder withHttpVersion(Version version) { this.version = version; return this; }
+ public RequestBuilder withRemoteAddress(String host, int port) { return withRemoteAddress(new InetSocketAddress(host, port)); }
+ public RequestBuilder withRemoteAddress(SocketAddress address) { this.remoteAddress = address; return this; }
+ public RequestBuilder withCookie(String cookie) { cookies.addAll(Cookie.fromCookieHeader(cookie)); return this; }
+ public RequestBuilder withCookie(Cookie cookie) { cookies.add(cookie); return this; }
+
+ public DiscFilterRequest build() {
+ var httpReq = HttpRequest.newServerRequest(
+ new DummyContainer(clock), uri, method, version, remoteAddress, clock.millis());
+ var filterReq = new DiscFilterRequest(httpReq);
+ filterReq.setUserPrincipal(principal);
+ attributes.forEach(filterReq::setAttribute);
+ filterReq.setAttribute(RequestUtils.JDISC_REQUEST_X509CERT, certificates.toArray(X509Certificate[]::new));
+ headers.forEach(filterReq::addHeader);
+ filterReq.setCookies(cookies);
+ return filterReq;
+ }
+ }
+
+ private record DummyContainer(Clock clock) implements CurrentContainer, Container, RequestHandler {
+ @Override public RequestHandler resolveHandler(Request request) { return this; }
+ @Override public <T> T getInstance(Class<T> type) { throw new UnsupportedOperationException(); }
+ @Override public void release() {}
+ @Override public long currentTimeMillis() { return clock.millis(); }
+ @Override public ContentChannel handleRequest(Request request, ResponseHandler handler) { throw new UnsupportedOperationException(); }
+ @Override public void handleTimeout(Request request, ResponseHandler handler) { throw new UnsupportedOperationException(); }
+ @Override public Container newReference(URI uri, Object context) { return this; }
+ @Override public Container newReference(URI uri) { return this; }
+ @Override public ResourceReference refer(Object context) { return References.NOOP_REFERENCE; }
+ @Override public ResourceReference refer() { return References.NOOP_REFERENCE; }
+ @Override public Instant currentTime() { return clock.instant(); }
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
index bf16913d05a..e49e9c7998e 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
@@ -7,6 +7,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.ControllerVersion;
import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveService;
import com.yahoo.vespa.hosted.controller.api.integration.artifact.ArtifactRegistry;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AccessControlService;
+import com.yahoo.vespa.hosted.controller.api.integration.aws.EnclaveAccessService;
import com.yahoo.vespa.hosted.controller.api.integration.aws.ResourceTagger;
import com.yahoo.vespa.hosted.controller.api.integration.aws.RoleService;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingController;
@@ -86,6 +87,8 @@ public interface ServiceRegistry {
ResourceTagger resourceTagger();
+ EnclaveAccessService enclaveAccessService();
+
RoleService roleService();
SystemMonitor systemMonitor();
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/EnclaveAccessService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/EnclaveAccessService.java
new file mode 100644
index 00000000000..52e8ba5adf8
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/EnclaveAccessService.java
@@ -0,0 +1,15 @@
+package com.yahoo.vespa.hosted.controller.api.integration.aws;
+
+import com.yahoo.config.provision.CloudAccount;
+
+import java.util.Set;
+
+/**
+ * @author jonmv
+ */
+public interface EnclaveAccessService {
+
+ /** Ensures the given enclave accounts have access to resources they require to function. */
+ void allowAccessFor(Set<CloudAccount> accounts);
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/MockEnclaveAccessService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/MockEnclaveAccessService.java
new file mode 100644
index 00000000000..81163404007
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/MockEnclaveAccessService.java
@@ -0,0 +1,22 @@
+package com.yahoo.vespa.hosted.controller.api.integration.aws;
+
+import com.yahoo.config.provision.CloudAccount;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * @author jonmv
+ */
+public class MockEnclaveAccessService implements EnclaveAccessService {
+
+ private volatile Set<CloudAccount> currentAccounts = new TreeSet<>();
+
+ public Set<CloudAccount> currentAccounts() { return currentAccounts; }
+
+ @Override
+ public void allowAccessFor(Set<CloudAccount> accounts) {
+ currentAccounts = new TreeSet<>(accounts);
+ }
+
+}
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);
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 c87a4e490b9..978587d9c5c 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
@@ -664,22 +664,22 @@ public class ControllerTest {
ZoneId east1a = ZoneId.from("prod", "aws-us-east-1a");
ZoneId east1b = ZoneId.from("prod", "aws-us-east-1b");
// Expected container endpoints are passed to each deployment
- Map<DeploymentId, Map<String, Integer>> deploymentEndpoints = Map.of(
+ Map<DeploymentId, Map<List<String>, Integer>> deploymentEndpoints = Map.of(
new DeploymentId(beta, east3), Map.of(),
- new DeploymentId(main, east3), Map.of("e.app1.tenant1.us-east-3-r.vespa.oath.cloud", 3),
- new DeploymentId(beta, west1), Map.of("d.app1.tenant1.us-west-1-r.vespa.oath.cloud", 3),
- new DeploymentId(main, west1), Map.of("d.app1.tenant1.us-west-1-r.vespa.oath.cloud", 7),
- new DeploymentId(beta, east1a), Map.of("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", 2,
- "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", 1),
- new DeploymentId(main, east1a), Map.of("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", 8,
- "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", 1),
- new DeploymentId(beta, east1b), Map.of("c.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud", 4),
- new DeploymentId(main, east1b), Map.of("a.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud", 1)
+ new DeploymentId(main, east3), Map.of(List.of("e.app1.tenant1.a.vespa.oath.cloud", "e.app1.tenant1.us-east-3-r.vespa.oath.cloud"), 3),
+ new DeploymentId(beta, west1), Map.of(List.of("d.app1.tenant1.a.vespa.oath.cloud", "d.app1.tenant1.us-west-1-r.vespa.oath.cloud"), 3),
+ new DeploymentId(main, west1), Map.of(List.of("d.app1.tenant1.a.vespa.oath.cloud", "d.app1.tenant1.us-west-1-r.vespa.oath.cloud"), 7),
+ new DeploymentId(beta, east1a), Map.of(List.of("a.app1.tenant1.a.vespa.oath.cloud", "a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 2,
+ List.of("b.app1.tenant1.a.vespa.oath.cloud", "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 1),
+ new DeploymentId(main, east1a), Map.of(List.of("a.app1.tenant1.a.vespa.oath.cloud", "a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 8,
+ List.of("b.app1.tenant1.a.vespa.oath.cloud", "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 1),
+ new DeploymentId(beta, east1b), Map.of(List.of("c.app1.tenant1.a.vespa.oath.cloud", "c.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud"), 4),
+ new DeploymentId(main, east1b), Map.of(List.of("a.app1.tenant1.a.vespa.oath.cloud", "a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 1)
);
deploymentEndpoints.forEach((deployment, endpoints) -> {
Set<ContainerEndpoint> expected = endpoints.entrySet().stream()
.map(kv -> new ContainerEndpoint("default", "application",
- List.of(kv.getKey()),
+ kv.getKey(),
OptionalInt.of(kv.getValue()),
tester.controller().zoneRegistry().routingMethod(deployment.zoneId())))
.collect(Collectors.toSet());
@@ -704,13 +704,37 @@ public class ControllerTest {
RecordName.from("main.app1.tenant1.aws-us-east-1b.vespa.oath.cloud"),
RecordData.from("lb-0--tenant1.app1.main--prod.aws-us-east-1b.")),
new Record(Record.Type.ALIAS,
+ RecordName.from("a.app1.tenant1.a.vespa.oath.cloud"),
+ RecordData.from("weighted/lb-0--tenant1.app1.beta--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/2")),
+ new Record(Record.Type.ALIAS,
+ RecordName.from("a.app1.tenant1.a.vespa.oath.cloud"),
+ RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/8")),
+ new Record(Record.Type.ALIAS,
+ RecordName.from("a.app1.tenant1.a.vespa.oath.cloud"),
+ RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1b/dns-zone-1/prod.aws-us-east-1b/1")),
+ new Record(Record.Type.ALIAS,
+ RecordName.from("b.app1.tenant1.a.vespa.oath.cloud"),
+ RecordData.from("weighted/lb-0--tenant1.app1.beta--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/1")),
+ new Record(Record.Type.ALIAS,
+ RecordName.from("b.app1.tenant1.a.vespa.oath.cloud"),
+ RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/1")),
+ new Record(Record.Type.ALIAS,
+ RecordName.from("c.app1.tenant1.a.vespa.oath.cloud"),
+ RecordData.from("weighted/lb-0--tenant1.app1.beta--prod.aws-us-east-1b/dns-zone-1/prod.aws-us-east-1b/4")),
+ new Record(Record.Type.CNAME,
+ RecordName.from("d.app1.tenant1.a.vespa.oath.cloud"),
+ RecordData.from("vip.prod.us-west-1.")),
+ new Record(Record.Type.CNAME,
+ RecordName.from("e.app1.tenant1.a.vespa.oath.cloud"),
+ RecordData.from("vip.prod.us-east-3.")),
+ new Record(Record.Type.ALIAS,
RecordName.from("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"),
RecordData.from("weighted/lb-0--tenant1.app1.beta--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/2")),
new Record(Record.Type.ALIAS,
RecordName.from("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"),
RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/8")),
new Record(Record.Type.ALIAS,
- RecordName.from("a.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud"),
+ RecordName.from("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"),
RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1b/dns-zone-1/prod.aws-us-east-1b/1")),
new Record(Record.Type.ALIAS,
RecordName.from("b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"),
@@ -732,12 +756,11 @@ public class ControllerTest {
.scope(Endpoint.Scope.application)
.sortedBy(comparing(Endpoint::dnsName))
.mapToList(Endpoint::dnsName);
- assertEquals(List.of("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud",
- "a.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud",
- "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud",
- "c.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud",
- "d.app1.tenant1.us-west-1-r.vespa.oath.cloud",
- "e.app1.tenant1.us-east-3-r.vespa.oath.cloud"),
+ assertEquals(List.of("a.app1.tenant1.a.vespa.oath.cloud",
+ "b.app1.tenant1.a.vespa.oath.cloud",
+ "c.app1.tenant1.a.vespa.oath.cloud",
+ "d.app1.tenant1.a.vespa.oath.cloud",
+ "e.app1.tenant1.a.vespa.oath.cloud"),
endpointDnsNames);
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index f3324e0c1f3..a76d2eca521 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -277,30 +277,65 @@ public class EndpointTest {
}
@Test
+ void application_endpoints_legacy_dns_names() {
+ Map<String, Endpoint> tests = Map.of(
+ "weighted.a1.t1.us-west-1.r.vespa-app.cloud",
+ Endpoint.of(app1)
+ .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
+ Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1))
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.Public),
+ "weighted.a1.t1.us-west-1.r.cd.vespa-app.cloud",
+ Endpoint.of(app1)
+ .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
+ Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1))
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.PublicCd),
+ "a2.t2.us-east-3-r.vespa.oath.cloud",
+ Endpoint.of(app2)
+ .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
+ Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1))
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.main),
+ "cd.a2.t2.us-east-3-r.vespa.oath.cloud",
+ Endpoint.of(app2)
+ .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
+ Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1))
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.cd)
+ );
+ tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.legacyRegionalDnsName()));
+ }
+
+ @Test
void application_endpoints() {
Map<String, Endpoint> tests = Map.of(
- "https://weighted.a1.t1.us-west-1.r.vespa-app.cloud/",
+ "https://weighted.a1.t1.a.vespa-app.cloud/",
Endpoint.of(app1)
.targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1))
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
- "https://weighted.a1.t1.us-west-1.r.cd.vespa-app.cloud/",
+ "https://weighted.a1.t1.a.cd.vespa-app.cloud/",
Endpoint.of(app1)
.targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1))
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.PublicCd),
- "https://a2.t2.us-east-3-r.vespa.oath.cloud/",
+ "https://a2.t2.a.vespa.oath.cloud/",
Endpoint.of(app2)
.targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1))
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.main),
- "https://cd.a2.t2.us-east-3-r.vespa.oath.cloud/",
+ "https://cd.a2.t2.a.vespa.oath.cloud/",
Endpoint.of(app2)
.targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1))
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
index b06b4eb0cfa..d79a81c7746 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.certificate;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.xml.DeploymentSpecXmlReader;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.CloudName;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.RegionName;
@@ -25,6 +26,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCe
import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock;
+import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -39,6 +41,8 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -136,9 +140,15 @@ public class EndpointCertificatesTest {
assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans());
}
+ private ControllerTester publicTester() {
+ ControllerTester publicTester = new ControllerTester(SystemName.Public);
+ publicTester.zoneRegistry().setZones(tester.zoneRegistry().zones().all().zones());
+ return publicTester;
+ }
+
@Test
void provisions_new_certificate_in_public_prod() {
- ControllerTester tester = new ControllerTester(SystemName.Public);
+ ControllerTester tester = publicTester();
EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock);
EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator);
List<String> expectedSans = List.of(
@@ -238,6 +248,9 @@ public class EndpointCertificatesTest {
Instance instance = new Instance(ApplicationId.from("t1", "a1", "default"), Tags.empty());
ZoneId zone1 = ZoneId.from(Environment.prod, RegionName.from("aws-us-east-1c"));
ZoneId zone2 = ZoneId.from(Environment.prod, RegionName.from("aws-us-west-2a"));
+ ControllerTester tester = publicTester();
+ tester.zoneRegistry().addZones(ZoneApiMock.newBuilder().with(CloudName.DEFAULT).with(zone1).build(),
+ ZoneApiMock.newBuilder().with(CloudName.AWS).with(zone2).build());
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.instances("beta,main")
.region(zone1.region())
@@ -251,16 +264,17 @@ public class EndpointCertificatesTest {
InstanceName.from("main"), 6),
zone2.region().value(), Map.of(InstanceName.from("main"), 2)))
.build();
- ControllerTester tester = new ControllerTester(SystemName.Public);
EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock);
EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator);
- List<String> expectedSans = List.of(
+ List<String> expectedSans = Stream.of(
"vlfms2wpoa4nyrka2s5lktucypjtxkqhv.internal.vespa-app.cloud",
"a1.t1.g.vespa-app.cloud",
"*.a1.t1.g.vespa-app.cloud",
+ "a1.t1.a.vespa-app.cloud",
"a1.t1.aws-us-west-2a.r.vespa-app.cloud",
- "*.a1.t1.aws-us-west-2a.r.vespa-app.cloud",
"a1.t1.aws-us-east-1c.r.vespa-app.cloud",
+ "*.a1.t1.a.vespa-app.cloud",
+ "*.a1.t1.aws-us-west-2a.r.vespa-app.cloud",
"*.a1.t1.aws-us-east-1c.r.vespa-app.cloud",
"a1.t1.aws-us-east-1c.z.vespa-app.cloud",
"*.a1.t1.aws-us-east-1c.z.vespa-app.cloud",
@@ -268,13 +282,13 @@ public class EndpointCertificatesTest {
"*.a1.t1.us-east-1.test.z.vespa-app.cloud",
"a1.t1.us-east-3.staging.z.vespa-app.cloud",
"*.a1.t1.us-east-3.staging.z.vespa-app.cloud"
- );
+ ).sorted().toList();
Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone1, applicationPackage.deploymentSpec());
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.t1.a1.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.t1.a1.*-cert"));
assertEquals(0, endpointCertificateMetadata.get().version());
- assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans());
+ assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans().stream().sorted().toList());
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
index 6493eafcde5..831a79f24b8 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.CloudName;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
@@ -31,22 +32,23 @@ public class TestConfigSerializerTest {
@Test
void testConfig() throws IOException {
ZoneId zone = DeploymentContext.systemTest.zone();
- byte[] json = new TestConfigSerializer(SystemName.PublicCd).configJson(instanceId,
- DeploymentContext.systemTest,
- true,
- Version.fromString("1.2.3"),
- RevisionId.forProduction(321),
- Instant.ofEpochMilli(222),
- Map.of(zone, List.of(Endpoint.of(ApplicationId.defaultId())
- .target(EndpointId.of("ai"), ClusterSpec.Id.from("qrs"),
- List.of(new DeploymentId(ApplicationId.defaultId(),
- ZoneId.defaultId())))
- .on(Endpoint.Port.tls())
- .in(SystemName.main))),
- Map.of(zone, List.of("facts")));
+ byte[] json = new TestConfigSerializer(SystemName.PublicCd)
+ .configJson(instanceId,
+ DeploymentContext.systemTest,
+ true,
+ Version.fromString("1.2.3"),
+ RevisionId.forProduction(321),
+ Instant.ofEpochMilli(222),
+ Map.of(zone, List.of(Endpoint.of(ApplicationId.defaultId())
+ .target(EndpointId.of("ai"), ClusterSpec.Id.from("qrs"),
+ List.of(new DeploymentId(ApplicationId.defaultId(),
+ ZoneId.defaultId())))
+ .on(Endpoint.Port.tls())
+ .in(SystemName.main))),
+ Map.of(zone, List.of("facts")));
byte[] expected = Files.readAllBytes(Paths.get("src/test/resources/testConfig.json"));
assertEquals(new String(SlimeUtils.toJsonBytes(SlimeUtils.jsonToSlime(expected))),
- new String(json));
+ new String(json));
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index af542521b31..46c731e6e49 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -15,10 +15,9 @@ import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveService;
import com.yahoo.vespa.hosted.controller.api.integration.archive.MockArchiveService;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AccessControlService;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.MockAccessControlService;
+import com.yahoo.vespa.hosted.controller.api.integration.aws.MockEnclaveAccessService;
import com.yahoo.vespa.hosted.controller.api.integration.aws.MockResourceTagger;
import com.yahoo.vespa.hosted.controller.api.integration.aws.MockRoleService;
-import com.yahoo.vespa.hosted.controller.api.integration.aws.ResourceTagger;
-import com.yahoo.vespa.hosted.controller.api.integration.aws.RoleService;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingController;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingDatabaseClient;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingDatabaseClientMock;
@@ -79,8 +78,9 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final MockTesterCloud mockTesterCloud;
private final ApplicationStoreMock applicationStoreMock = new ApplicationStoreMock();
private final MockRunDataStore mockRunDataStore = new MockRunDataStore();
+ private final MockEnclaveAccessService mockAMIService = new MockEnclaveAccessService();
private final MockResourceTagger mockResourceTagger = new MockResourceTagger();
- private final RoleService roleService = new MockRoleService();
+ private final MockRoleService roleService = new MockRoleService();
private final BillingController billingController = new MockBillingController(clock);
private final ArtifactRegistryMock containerRegistry = new ArtifactRegistryMock();
private final NoopTenantSecretService tenantSecretService = new NoopTenantSecretService();
@@ -206,12 +206,17 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
}
@Override
- public ResourceTagger resourceTagger() {
+ public MockResourceTagger resourceTagger() {
return mockResourceTagger;
}
@Override
- public RoleService roleService() {
+ public MockEnclaveAccessService enclaveAccessService() {
+ return mockAMIService;
+ }
+
+ @Override
+ public MockRoleService roleService() {
return roleService;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainerTest.java
new file mode 100644
index 00000000000..f5188d52db6
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainerTest.java
@@ -0,0 +1,39 @@
+package com.yahoo.vespa.hosted.controller.maintenance;
+
+import com.yahoo.config.provision.CloudAccount;
+import com.yahoo.vespa.flags.PermanentFlags;
+import com.yahoo.vespa.hosted.controller.ControllerTester;
+import com.yahoo.vespa.hosted.controller.api.integration.aws.MockEnclaveAccessService;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author jonmv
+ */
+class EnclaveAccessMaintainerTest {
+
+ @Test
+ void test() {
+ ControllerTester tester = new ControllerTester();
+ MockEnclaveAccessService amis = tester.serviceRegistry().enclaveAccessService();
+ EnclaveAccessMaintainer sharer = new EnclaveAccessMaintainer(tester.controller(), Duration.ofMinutes(1));
+ assertEquals(Set.of(), amis.currentAccounts());
+
+ assertEquals(1, sharer.maintain());
+ assertEquals(Set.of(), amis.currentAccounts());
+
+ tester.createTenant("tanten");
+ assertEquals(1, sharer.maintain());
+ assertEquals(Set.of(), amis.currentAccounts());
+
+ tester.flagSource().withListFlag(PermanentFlags.CLOUD_ACCOUNTS.id(), List.of("123123123123", "321321321321"), String.class);
+ assertEquals(1, sharer.maintain());
+ assertEquals(Set.of(CloudAccount.from("123123123123"), CloudAccount.from("321321321321")), amis.currentAccounts());
+ }
+
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index 0f03333146f..d6da677cb27 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -114,10 +114,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class ApplicationApiTest extends ControllerContainerTest {
private static final String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/";
- private static final String pemPublicKey = "-----BEGIN PUBLIC KEY-----\n" +
- "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuKVFA8dXk43kVfYKzkUqhEY2rDT9\n" +
- "z/4jKSTHwbYR8wdsOSrJGVEUPbS2nguIJ64OJH7gFnxM6sxUVj+Nm2HlXw==\n" +
- "-----END PUBLIC KEY-----\n";
+ private static final String pemPublicKey = """
+ -----BEGIN PUBLIC KEY-----
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuKVFA8dXk43kVfYKzkUqhEY2rDT9
+ z/4jKSTHwbYR8wdsOSrJGVEUPbS2nguIJ64OJH7gFnxM6sxUVj+Nm2HlXw==
+ -----END PUBLIC KEY-----
+ """;
private static final String quotedPemPublicKey = pemPublicKey.replaceAll("\\n", "\\\\n");
private static final String accessDenied = "{\n \"code\" : 403,\n \"message\" : \"Access denied\"\n}";
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
index 37bd69d5863..cc42b3e006c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
@@ -24,7 +24,7 @@
{
"cluster": "foo",
"tls": true,
- "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/",
+ "url": "https://a0.application1.tenant1.a.vespa.oath.cloud/",
"scope": "application",
"routingMethod": "sharedLayer4",
"legacy": false
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
index d78ab67dcc5..f37112ea887 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
@@ -111,7 +111,7 @@
{
"cluster": "foo",
"tls": true,
- "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/",
+ "url": "https://a0.application1.tenant1.a.vespa.oath.cloud/",
"scope": "application",
"routingMethod": "sharedLayer4",
"legacy": false
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json
index 8d76d54458d..4458040858b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json
@@ -118,7 +118,7 @@
{
"cluster": "foo",
"tls": true,
- "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/",
+ "url": "https://a0.application1.tenant1.a.vespa.oath.cloud/",
"scope": "application",
"routingMethod": "sharedLayer4",
"legacy": false
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json
index b9bf714d362..ea025b60d1b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json
@@ -117,7 +117,7 @@
{
"cluster": "foo",
"tls": true,
- "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/",
+ "url": "https://a0.application1.tenant1.a.vespa.oath.cloud/",
"scope": "application",
"routingMethod": "sharedLayer4",
"legacy": false
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 48771ac064b..18c7322b885 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
@@ -49,6 +49,9 @@
"name": "DeploymentUpgrader"
},
{
+ "name": "EnclaveAccessMaintainer"
+ },
+ {
"name": "EndpointCertificateMaintainer"
},
{
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index 274b0d114ed..0ccdfddf1b8 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -432,6 +432,13 @@ public class Flags {
"Takes effect immediately?",
ZONE_ID, APPLICATION_ID);
+ public static final UnboundBooleanFlag USE_LOCKS_IN_FILEDISTRIBUTION = defineFeatureFlag(
+ "use-locks-in-filedistribution", false,
+ List.of("hmusum"), "2022-11-16", "2023-01-31",
+ "If true, use locks when writing and deleting file references.",
+ "Takes effect immediately",
+ ZONE_ID, APPLICATION_ID);
+
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, List<String> owners,
String createdAt, String expiresAt, String description,
diff --git a/jdisc-security-filters/pom.xml b/jdisc-security-filters/pom.xml
index a452327f6ff..652b864747d 100644
--- a/jdisc-security-filters/pom.xml
+++ b/jdisc-security-filters/pom.xml
@@ -53,6 +53,16 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java
index 752f1026f3d..f7a2e41dae4 100644
--- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzAuthorizationFilterTest.java
@@ -9,6 +9,7 @@ import com.yahoo.jdisc.Metric;
import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
import com.yahoo.jdisc.http.filter.security.athenz.AthenzAuthorizationFilterConfig.EnabledCredentials;
+import com.yahoo.jdisc.http.filter.util.FilterTestUtils;
import com.yahoo.security.KeyAlgorithm;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.SubjectAlternativeName;
@@ -267,14 +268,11 @@ public class AthenzAuthorizationFilterTest {
}
private static DiscFilterRequest createRequest(ZToken roleToken, AthenzAccessToken accessToken, X509Certificate clientCert) {
- DiscFilterRequest request = mock(DiscFilterRequest.class);
- when(request.getHeader(HEADER_NAME)).thenReturn(roleToken != null ? roleToken.getRawToken() : null);
- when(request.getHeader(AthenzAccessToken.HTTP_HEADER_NAME)).thenReturn(accessToken != null ? "Bearer " + accessToken.value() : null);
- when(request.getMethod()).thenReturn("GET");
- when(request.getRequestURI()).thenReturn("/my/path");
- when(request.getQueryString()).thenReturn(null);
- when(request.getClientCertificateChain()).thenReturn(clientCert != null ? List.of(clientCert) : List.of());
- return request;
+ var builder = FilterTestUtils.newRequestBuilder().withUri("https://localhost/my/path");
+ if (roleToken != null) builder.withHeader(HEADER_NAME, roleToken.getRawToken());
+ if (accessToken != null) builder.withHeader(AthenzAccessToken.HTTP_HEADER_NAME, accessToken.value());
+ if (clientCert != null) builder.withClientCertificate(clientCert);
+ return builder.build();
}
private static AthenzAuthorizationFilter createFilter(Zpe zpe, List<EnabledCredentials.Enum> enabledCredentials) {
@@ -298,7 +296,7 @@ public class AthenzAuthorizationFilterTest {
}
private static void assertAuthorizationResult(DiscFilterRequest request, Type expectedResult) {
- verify(request).setAttribute(RESULT_ATTRIBUTE, expectedResult.name());
+ assertEquals(expectedResult.name(), request.getAttribute(RESULT_ATTRIBUTE));
}
private static void assertStatusCode(MockResponseHandler responseHandler, int statusCode) {
@@ -308,7 +306,7 @@ public class AthenzAuthorizationFilterTest {
}
private static void assertMatchedCredentialType(DiscFilterRequest request, EnabledCredentials.Enum expectedType) {
- verify(request).setAttribute(MATCHED_CREDENTIAL_TYPE_ATTRIBUTE, expectedType.name());
+ assertEquals(expectedType.name(), request.getAttribute(MATCHED_CREDENTIAL_TYPE_ATTRIBUTE));
}
private static void assertRequestNotFiltered(MockResponseHandler responseHandler) {
@@ -316,7 +314,7 @@ public class AthenzAuthorizationFilterTest {
}
private static void assertMatchedRole(DiscFilterRequest request, AthenzRole role) {
- verify(request).setAttribute(MATCHED_ROLE_ATTRIBUTE, role.roleName());
+ assertEquals(role.roleName(), request.getAttribute(MATCHED_ROLE_ATTRIBUTE));
}
private static void assertErrorMessage(MockResponseHandler responseHandler, String errorMessage) {
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java
index 0b04993a723..6ee589c1908 100644
--- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java
@@ -6,6 +6,7 @@ import com.yahoo.jdisc.handler.ContentChannel;
import com.yahoo.jdisc.handler.ReadableContentChannel;
import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.util.FilterTestUtils;
import com.yahoo.security.KeyAlgorithm;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.X509CertificateBuilder;
@@ -28,16 +29,11 @@ import java.util.Objects;
import static com.yahoo.jdisc.Response.Status.UNAUTHORIZED;
import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.joining;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
/**
* @author bjorncs
@@ -49,8 +45,7 @@ public class AthenzPrincipalFilterTest {
@Test
void missing_certificate_is_unauthorized() {
- DiscFilterRequest request = createRequestMock();
- when(request.getClientCertificateChain()).thenReturn(emptyList());
+ DiscFilterRequest request = FilterTestUtils.newRequestBuilder().build();
ResponseHandlerMock responseHandler = new ResponseHandlerMock();
@@ -62,8 +57,7 @@ public class AthenzPrincipalFilterTest {
@Test
void certificate_is_accepted() {
- DiscFilterRequest request = createRequestMock();
- when(request.getClientCertificateChain()).thenReturn(singletonList(CERTIFICATE));
+ DiscFilterRequest request = FilterTestUtils.newRequestBuilder().withClientCertificate(CERTIFICATE).build();
ResponseHandlerMock responseHandler = new ResponseHandlerMock();
@@ -75,15 +69,14 @@ public class AthenzPrincipalFilterTest {
}
private void assertAuthenticated(DiscFilterRequest request, AthenzPrincipal expectedPrincipal) {
- verify(request).setUserPrincipal(expectedPrincipal);
- verify(request).setAttribute(AthenzPrincipalFilter.RESULT_PRINCIPAL, expectedPrincipal);
+ assertEquals(expectedPrincipal, request.getUserPrincipal());
+ assertEquals(expectedPrincipal, request.getAttribute(AthenzPrincipalFilter.RESULT_PRINCIPAL));
}
@Test
void no_response_produced_when_passthrough_mode_is_enabled() {
- DiscFilterRequest request = createRequestMock();
- when(request.getClientCertificateChain()).thenReturn(emptyList());
+ DiscFilterRequest request = FilterTestUtils.newRequestBuilder().build();
ResponseHandlerMock responseHandler = new ResponseHandlerMock();
@@ -93,10 +86,6 @@ public class AthenzPrincipalFilterTest {
assertNull(responseHandler.response);
}
- private DiscFilterRequest createRequestMock() {
- return mock(DiscFilterRequest.class);
- }
-
private AthenzPrincipalFilter createFilter(boolean passthroughModeEnabled) {
return new AthenzPrincipalFilter(passthroughModeEnabled);
}
@@ -105,7 +94,7 @@ public class AthenzPrincipalFilterTest {
assertNotNull(responseHandler.response);
assertEquals(UNAUTHORIZED, responseHandler.response.getStatus());
assertTrue(responseHandler.getResponseContent().contains(expectedMessageSubstring));
- verify(request).setAttribute(AthenzPrincipalFilter.RESULT_ERROR_CODE_ATTRIBUTE, UNAUTHORIZED);
+ assertEquals(UNAUTHORIZED, request.getAttribute(AthenzPrincipalFilter.RESULT_ERROR_CODE_ATTRIBUTE));
}
private static class ResponseHandlerMock implements ResponseHandler {
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java
index 34db051aa28..fe530ed90cb 100644
--- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java
@@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.util.FilterTestUtils;
import org.junit.jupiter.api.Test;
import java.io.IOException;
@@ -13,7 +14,6 @@ import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.mockito.Mockito.mock;
/**
* @author bjorncs
@@ -26,7 +26,7 @@ public class JsonSecurityRequestFilterBaseTest {
void filter_renders_errors_as_json() throws IOException {
int statusCode = 403;
String message = "Forbidden";
- DiscFilterRequest request = mock(DiscFilterRequest.class);
+ DiscFilterRequest request = FilterTestUtils.newRequestBuilder().build();
SimpleSecurityRequestFilter filter =
new SimpleSecurityRequestFilter(new JsonSecurityRequestFilterBase.ErrorResponse(statusCode, message));
RequestHandlerTestDriver.MockResponseHandler responseHandler = new RequestHandlerTestDriver.MockResponseHandler();
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilterTest.java
index 7ba050b7cc0..576b04e23b6 100644
--- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilterTest.java
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilterTest.java
@@ -8,6 +8,7 @@ import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
import com.yahoo.jdisc.http.filter.security.cors.CorsFilterConfig.Builder;
+import com.yahoo.jdisc.http.filter.util.FilterTestUtils;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
@@ -19,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
/**
@@ -70,10 +70,7 @@ public class CorsPreflightRequestFilterTest {
}
private static DiscFilterRequest newOptionsRequest(String origin) {
- DiscFilterRequest request = mock(DiscFilterRequest.class);
- when(request.getHeader("Origin")).thenReturn(origin);
- when(request.getMethod()).thenReturn(OPTIONS.name());
- return request;
+ return FilterTestUtils.newRequestBuilder().withHeader("Origin", origin).withMethod(OPTIONS).build();
}
private static CorsPreflightRequestFilter newRequestFilter(String... allowedOriginUrls) {
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/LocalhostFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/LocalhostFilterTest.java
index aaf6ebf1aee..5b9f143a72b 100644
--- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/LocalhostFilterTest.java
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/LocalhostFilterTest.java
@@ -4,14 +4,11 @@ package com.yahoo.jdisc.http.filter.security.misc;
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.util.FilterTestUtils;
import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
-
-import java.net.URI;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.mockito.Mockito.when;
/**
* @author mpolden
@@ -34,12 +31,10 @@ public class LocalhostFilterTest {
}
private static DiscFilterRequest createRequest(String remoteAddr, String localAddr) {
- DiscFilterRequest request = Mockito.mock(DiscFilterRequest.class);
- when(request.getRemoteAddr()).thenReturn(remoteAddr);
- when(request.getLocalAddr()).thenReturn(localAddr);
- when(request.getMethod()).thenReturn("GET");
- when(request.getUri()).thenReturn(URI.create("http://localhost:8080/"));
- return request;
+ return FilterTestUtils.newRequestBuilder()
+ .withUri("http://%s:8080/".formatted(localAddr))
+ .withRemoteAddress(remoteAddr, 12345)
+ .build();
}
private static void assertUnauthorized(DiscFilterRequest request) {
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java
index 95bc1f92572..2dd243618c9 100644
--- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/misc/VespaTlsFilterTest.java
@@ -5,16 +5,15 @@ package com.yahoo.jdisc.http.filter.security.misc;
import com.yahoo.container.jdisc.RequestHandlerTestDriver;
import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.util.FilterTestUtils;
import com.yahoo.security.KeyAlgorithm;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.SignatureAlgorithm;
import com.yahoo.security.X509CertificateBuilder;
import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
import javax.security.auth.x500.X500Principal;
import java.math.BigInteger;
-import java.net.URI;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -23,7 +22,6 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.mockito.Mockito.when;
public class VespaTlsFilterTest {
@@ -43,11 +41,7 @@ public class VespaTlsFilterTest {
}
private static DiscFilterRequest createRequest(List<X509Certificate> certChain) {
- DiscFilterRequest request = Mockito.mock(DiscFilterRequest.class);
- when(request.getClientCertificateChain()).thenReturn(certChain);
- when(request.getMethod()).thenReturn("GET");
- when(request.getUri()).thenReturn(URI.create("http://localhost:8080/"));
- return request;
+ return FilterTestUtils.newRequestBuilder().withClientCertificate(certChain).withUri("http://localhost:8080/").build();
}
private static void assertForbidden(DiscFilterRequest request) {
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/rule/RuleBasedRequestFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/rule/RuleBasedRequestFilterTest.java
index 4ad593efe82..c4a78a2d962 100644
--- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/rule/RuleBasedRequestFilterTest.java
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/rule/RuleBasedRequestFilterTest.java
@@ -8,13 +8,13 @@ import com.yahoo.container.jdisc.RequestHandlerTestDriver.MockResponseHandler;
import com.yahoo.jdisc.Metric;
import com.yahoo.jdisc.Response;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.util.FilterTestUtils;
import com.yahoo.vespa.config.jdisc.http.filter.RuleBasedFilterConfig;
import com.yahoo.vespa.config.jdisc.http.filter.RuleBasedFilterConfig.DefaultRule;
import com.yahoo.vespa.config.jdisc.http.filter.RuleBasedFilterConfig.Rule;
import org.junit.jupiter.api.Test;
import java.io.IOException;
-import java.net.URI;
import java.util.List;
import java.util.Set;
@@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
/**
* @author bjorncs
@@ -218,10 +217,7 @@ class RuleBasedRequestFilterTest {
}
private static DiscFilterRequest request(String method, String uri) {
- DiscFilterRequest request = mock(DiscFilterRequest.class);
- when(request.getMethod()).thenReturn(method);
- when(request.getUri()).thenReturn(URI.create(uri));
- return request;
+ return FilterTestUtils.newRequestBuilder().withMethod(method).withUri(uri).build();
}
private static void assertAllowed(MockResponseHandler handler, Metric metric) {
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java
index f4cd35bc5bd..4a8353d92ec 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java
@@ -29,7 +29,6 @@ import com.yahoo.vespa.hosted.node.admin.task.util.file.MakeDirectory;
import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;
import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
-import javax.crypto.CipherOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
@@ -49,7 +48,6 @@ import java.util.function.Supplier;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
-import java.util.stream.Stream;
import static com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder.nameEndsWith;
import static com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder.nameMatches;
@@ -275,7 +273,7 @@ public class CoredumpHandler {
static OutputStream maybeWrapWithEncryption(OutputStream wrappedStream, Optional<SecretSharedKey> sharedCoreKey) {
return sharedCoreKey
- .map(key -> (OutputStream)new CipherOutputStream(wrappedStream, SharedKeyGenerator.makeAesGcmEncryptionCipher(key)))
+ .map(key -> SharedKeyGenerator.makeAesGcmEncryptionCipher(key).wrapOutputStream(wrappedStream))
.orElse(wrappedStream);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java
index 6b61c864a0c..bb9d28d48f9 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RebuildingOsUpgrader.java
@@ -68,8 +68,10 @@ public class RebuildingOsUpgrader implements OsUpgrader {
private List<Node> rebuildableHosts(OsVersionTarget target, NodeList allNodes, Instant now) {
NodeList hostsOfTargetType = allNodes.nodeType(target.nodeType());
if (softRebuild) {
- // Soft rebuild is enabled so this should only act on hosts with remote storage
- hostsOfTargetType = hostsOfTargetType.storageType(NodeResources.StorageType.remote);
+ // Soft rebuild is enabled so this should only act on hosts with remote storage and on x86-64
+ // TODO(mpolden): Rebuild arm64 hosts as well if image permissions can be fixed
+ hostsOfTargetType = hostsOfTargetType.matching(node -> node.resources().storageType() == NodeResources.StorageType.remote &&
+ node.resources().architecture() == NodeResources.Architecture.x86_64);
}
int rebuildLimit = rebuildLimit(target.nodeType(), hostsOfTargetType);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java
index 72ab26f71a7..acdb689b809 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/os/RetiringOsUpgrader.java
@@ -62,8 +62,9 @@ public class RetiringOsUpgrader implements OsUpgrader {
private NodeList candidates(Instant instant, OsVersionTarget target, NodeList allNodes) {
NodeList activeNodes = allNodes.state(Node.State.active).nodeType(target.nodeType());
if (softRebuild) {
- // Soft rebuild is enabled, so this should only act on hosts with local storage
- activeNodes = activeNodes.storageType(NodeResources.StorageType.local);
+ // Soft rebuild is enabled, so this should only act on hosts with local storage, or non-x86-64
+ activeNodes = activeNodes.matching(node -> node.resources().storageType() == NodeResources.StorageType.local ||
+ node.resources().architecture() != NodeResources.Architecture.x86_64);
}
if (activeNodes.isEmpty()) return NodeList.of();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java
index df316567422..64b32276ca2 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java
@@ -75,7 +75,6 @@ public class HostResumeProvisionerTest {
Supplier<NodeList> provisioning = () -> tester.nodeRepository().nodes().list(Node.State.provisioned).nodeType(NodeType.host);
assertEquals(1, provisioning.get().size());
- provisioning.get().forEach(h -> System.out.println(h.hostname() + " " + h.ipConfig()));
hostResumeProvisioner.maintain();
assertTrue("No IP addresses written as DNS updates are failing",
diff --git a/parent/pom.xml b/parent/pom.xml
index b17a56c9dc8..1c9f4d387bb 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -911,6 +911,11 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
diff --git a/searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp b/searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp
index 72260072842..e8d9259ad20 100644
--- a/searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp
+++ b/searchlib/src/vespa/searchlib/memoryindex/field_inverter.cpp
@@ -16,6 +16,7 @@
#include <vespa/searchcommon/common/schema.h>
#include <vespa/searchlib/common/sort.h>
#include <vespa/searchlib/util/url.h>
+#include <vespa/vespalib/datastore/aligner.h>
#include <vespa/vespalib/text/utf8.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/stllike/hash_map.hpp>
@@ -50,6 +51,7 @@ using index::Schema;
using search::index::schema::CollectionType;
using search::util::URL;
using vespalib::make_string;
+using vespalib::datastore::Aligner;
namespace documentinverterkludge::linguistics {
@@ -252,15 +254,15 @@ FieldInverter::saveWord(const vespalib::stringref word)
return 0u;
}
- const size_t fullyPaddedSize = (wordsSize + 4 + len + 1 + 3) & ~3;
+ const size_t unpadded_size = wordsSize + 4 + len + 1;
+ const size_t fullyPaddedSize = Aligner<4>::align(unpadded_size);
_words.reserve(vespalib::roundUp2inN(fullyPaddedSize));
_words.resize(fullyPaddedSize);
char * buf = &_words[0] + wordsSize;
memset(buf, 0, 4);
memcpy(buf + 4, word.data(), len);
- uint32_t *lastWord = reinterpret_cast<uint32_t *>(buf + 4 + (len & ~0x3));
- *lastWord &= (0xffffff >> ((3 - (len & 3)) << 3)); //only on little endian machiness !!
+ memset(buf + 4 + len, 0, fullyPaddedSize - unpadded_size + 1);
uint32_t wordRef = (wordsSize + 4) >> 2;
// assert(wordRef != 0);
diff --git a/security-utils/src/main/java/com/yahoo/security/AeadCipher.java b/security-utils/src/main/java/com/yahoo/security/AeadCipher.java
new file mode 100644
index 00000000000..598f5d01db7
--- /dev/null
+++ b/security-utils/src/main/java/com/yahoo/security/AeadCipher.java
@@ -0,0 +1,44 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.security;
+
+import org.bouncycastle.crypto.io.CipherInputStream;
+import org.bouncycastle.crypto.io.CipherOutputStream;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * AEAD cipher wrapper to hide the underlying crypto provider used.
+ *
+ * @author vekterli
+ */
+public class AeadCipher {
+
+ private final AEADBlockCipher cipher;
+
+ private AeadCipher(AEADBlockCipher cipher) {
+ this.cipher = cipher;
+ }
+
+ static AeadCipher of(AEADBlockCipher cipher) {
+ return new AeadCipher(cipher);
+ }
+
+ /**
+ * Returns a wrapping <code>OutputStream</code> that, depending on the cipher mode, either
+ * encrypts or decrypts all data that is written to it before passing it on to <code>out</code>.
+ */
+ public OutputStream wrapOutputStream(OutputStream out) {
+ return new CipherOutputStream(out, cipher);
+ }
+
+ /**
+ * Returns a wrapping <code>InputStream</code> that, depending on the cipher mode, either
+ * encrypts or decrypts all data that is read from the underlying input stream.
+ */
+ public InputStream wrapInputStream(InputStream in) {
+ return new CipherInputStream(in, cipher);
+ }
+
+}
diff --git a/security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java b/security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java
index 66a87a94707..16bd82e2af3 100644
--- a/security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java
+++ b/security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java
@@ -6,15 +6,14 @@ import com.yahoo.security.hpke.Ciphersuite;
import com.yahoo.security.hpke.Hpke;
import com.yahoo.security.hpke.Kdf;
import com.yahoo.security.hpke.Kem;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
-import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
-import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -93,33 +92,29 @@ public class SharedKeyGenerator {
'h','e','r','e','B','d','r','a','g','o','n','s' // Nothing up my sleeve!
};
- private static Cipher makeAesGcmCipher(SecretSharedKey secretSharedKey, int cipherMode) {
- try {
- var cipher = Cipher.getInstance(AES_GCM_ALGO_SPEC);
- var gcmSpec = new GCMParameterSpec(AES_GCM_AUTH_TAG_BITS, FIXED_96BIT_IV_FOR_SINGLE_USE_KEY);
- cipher.init(cipherMode, secretSharedKey.secretKey(), gcmSpec);
- return cipher;
- } catch (NoSuchAlgorithmException | NoSuchPaddingException
- | InvalidKeyException | InvalidAlgorithmParameterException e) {
- throw new RuntimeException(e);
- }
+ private static AeadCipher makeAesGcmCipher(SecretSharedKey secretSharedKey, boolean forEncryption) {
+ var aeadParams = new AEADParameters(new KeyParameter(secretSharedKey.secretKey().getEncoded()),
+ AES_GCM_AUTH_TAG_BITS, FIXED_96BIT_IV_FOR_SINGLE_USE_KEY);
+ var cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(forEncryption, aeadParams);
+ return AeadCipher.of(cipher);
}
/**
- * Creates an AES-GCM Cipher that can be used to encrypt arbitrary plaintext.
+ * Creates an AES-GCM cipher that can be used to encrypt arbitrary plaintext.
*
* The given secret key MUST NOT be used to encrypt more than one plaintext.
*/
- public static Cipher makeAesGcmEncryptionCipher(SecretSharedKey secretSharedKey) {
- return makeAesGcmCipher(secretSharedKey, Cipher.ENCRYPT_MODE);
+ public static AeadCipher makeAesGcmEncryptionCipher(SecretSharedKey secretSharedKey) {
+ return makeAesGcmCipher(secretSharedKey, true);
}
/**
- * Creates an AES-GCM Cipher that can be used to decrypt ciphertext that was previously
+ * Creates an AES-GCM cipher that can be used to decrypt ciphertext that was previously
* encrypted with the given secret key.
*/
- public static Cipher makeAesGcmDecryptionCipher(SecretSharedKey secretSharedKey) {
- return makeAesGcmCipher(secretSharedKey, Cipher.DECRYPT_MODE);
+ public static AeadCipher makeAesGcmDecryptionCipher(SecretSharedKey secretSharedKey) {
+ return makeAesGcmCipher(secretSharedKey, false);
}
}
diff --git a/security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java b/security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java
index 23e22345cc6..aede100574d 100644
--- a/security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java
+++ b/security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java
@@ -3,8 +3,6 @@ package com.yahoo.security;
import org.junit.jupiter.api.Test;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -142,7 +140,7 @@ public class SharedKeyTest {
static byte[] streamEncryptString(String data, SecretSharedKey secretSharedKey) throws IOException {
var cipher = SharedKeyGenerator.makeAesGcmEncryptionCipher(secretSharedKey);
var outStream = new ByteArrayOutputStream();
- try (var cipherStream = new CipherOutputStream(outStream, cipher)) {
+ try (var cipherStream = cipher.wrapOutputStream(outStream)) {
cipherStream.write(data.getBytes(StandardCharsets.UTF_8));
cipherStream.flush();
}
@@ -154,7 +152,7 @@ public class SharedKeyTest {
var inStream = new ByteArrayInputStream(encrypted);
var total = ByteBuffer.allocate(encrypted.length); // Assume decrypted form can't be _longer_
byte[] tmp = new byte[8]; // short buf to test chunking
- try (var cipherStream = new CipherInputStream(inStream, cipher)) {
+ try (var cipherStream = cipher.wrapInputStream(inStream)) {
while (true) {
int read = cipherStream.read(tmp);
if (read == -1) {
@@ -180,4 +178,32 @@ public class SharedKeyTest {
assertEquals(terrifyingSecret, decrypted);
}
+ // javax.crypto.CipherOutputStream swallows exceptions caused by MAC failures in cipher
+ // decryption mode (!) and must therefore _not_ be used for this purpose. This is documented,
+ // but still very surprising behavior.
+ @Test
+ void cipher_output_stream_tag_mismatch_is_not_swallowed() throws Exception {
+ var receiverKeyPair = KeyUtils.generateX25519KeyPair();
+ var myShared = SharedKeyGenerator.generateForReceiverPublicKey(receiverKeyPair.getPublic(), KEY_ID_1);
+ String plaintext = "...hello world?";
+ byte[] encrypted = streamEncryptString(plaintext, myShared);
+ // Corrupt MAC tag in ciphertext
+ encrypted[encrypted.length - 1] ^= 0x80;
+ // We don't necessarily know _which_ exception is thrown, but one _should_ be thrown!
+ assertThrows(Exception.class, () -> doOutputStreamCipherDecrypt(myShared, encrypted));
+ // Also try with corrupted ciphertext (pre MAC tag)
+ encrypted[encrypted.length - 1] ^= 0x80; // Flip MAC bit back to correct state
+ encrypted[encrypted.length - 17] ^= 0x80; // Pre 128-bit MAC tag
+ assertThrows(Exception.class, () -> doOutputStreamCipherDecrypt(myShared, encrypted));
+ }
+
+ private static void doOutputStreamCipherDecrypt(SecretSharedKey myShared, byte[] encrypted) throws Exception {
+ var cipher = SharedKeyGenerator.makeAesGcmDecryptionCipher(myShared);
+ var outStream = new ByteArrayOutputStream();
+ try (var cipherStream = cipher.wrapOutputStream(outStream)) {
+ cipherStream.write(encrypted);
+ cipherStream.flush();
+ }
+ }
+
}
diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/CipherUtils.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/CipherUtils.java
index 051189c20b6..87d3cb4d9f0 100644
--- a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/CipherUtils.java
+++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/CipherUtils.java
@@ -1,8 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.security.tool.crypto;
-import javax.crypto.Cipher;
-import javax.crypto.CipherOutputStream;
+import com.yahoo.security.AeadCipher;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -19,11 +19,11 @@ public class CipherUtils {
*
* @param input source stream to read from
* @param output destination stream to write to
- * @param cipher a Cipher in either ENCRYPT or DECRYPT mode
+ * @param cipher an {@link AeadCipher} created with for either encryption or decryption
* @throws IOException if any file operation fails
*/
- public static void streamEncipher(InputStream input, OutputStream output, Cipher cipher) throws IOException {
- try (var cipherStream = new CipherOutputStream(output, cipher)) {
+ public static void streamEncipher(InputStream input, OutputStream output, AeadCipher cipher) throws IOException {
+ try (var cipherStream = cipher.wrapOutputStream(output)) {
input.transferTo(cipherStream);
cipherStream.flush();
}