aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2023-07-10 20:49:14 +0200
committerGitHub <noreply@github.com>2023-07-10 20:49:14 +0200
commit3008439ac5f2f2dfd0c5acccc1a6b8cf202f9933 (patch)
tree3bd216336060a5e6c13e6b8609e8051076ecde13
parentc4911b3f2924544e4700a1832df34ebbc03b53fc (diff)
parent53744f54af44b6024d4002ba69cf34feaac1b134 (diff)
Merge pull request #27697 from vespa-engine/mpolden/endpoint-lookupv8.192.20
Add API for searching deployments by endpoint
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java71
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java16
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/search-deployments-multi.json31
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/search-deployments-single.json13
8 files changed, 141 insertions, 6 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
index 1a8f4103659..6c603a1da7b 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
@@ -38,7 +38,8 @@ enum PathGroup {
"/provision/v2/{*}",
"/zone/v2/{*}",
"/state/v1/{*}",
- "/changemanagement/v1/{*}"),
+ "/changemanagement/v1/{*}",
+ "/application/v4/search/{*}"),
/** Paths used for creating and reading user resources. */
user("/application/v4/user",
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 65f4c851e3c..a635d512054 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
@@ -200,8 +200,8 @@ public class RoutingController {
return EndpointList.copyOf(endpoints);
}
- /** Read test runner endpoints for given deployments, grouped by their zone */
- public Map<ZoneId, List<Endpoint>> readTestRunnerEndpointsOf(Collection<DeploymentId> deployments) {
+ /** Read endpoints for use in deployment steps, for given deployments, grouped by their zone */
+ public Map<ZoneId, List<Endpoint>> readStepRunnerEndpointsOf(Collection<DeploymentId> deployments) {
TreeMap<ZoneId, List<Endpoint>> endpoints = new TreeMap<>(Comparator.comparing(ZoneId::value));
for (var deployment : deployments) {
EndpointList zoneEndpoints = readEndpointsOf(deployment).scope(Endpoint.Scope.zone)
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
index e554bb2361a..dcc3e229f92 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java
@@ -38,6 +38,11 @@ public class EndpointList extends AbstractFilteringList<Endpoint, EndpointList>
endpoint.name().equals(id.id()));
}
+ /** Returns the endpoint which has given DNS name, if any */
+ public Optional<Endpoint> dnsName(String dnsName) {
+ return matching(endpoint -> endpoint.dnsName().equals(dnsName)).first();
+ }
+
/** Returns the subset of endpoints pointing to given cluster */
public EndpointList cluster(ClusterSpec.Id cluster) {
return matching(endpoint -> endpoint.cluster().equals(cluster));
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
index e19915fbd24..07695c17042 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
@@ -502,7 +502,7 @@ public class InternalStepRunner implements StepRunner {
private Availability endpointsAvailable(ApplicationId id, ZoneId zone, Deployment deployment, DualLogger logger) {
DeploymentId deploymentId = new DeploymentId(id, zone);
- Map<ZoneId, List<Endpoint>> endpoints = controller.routing().readTestRunnerEndpointsOf(Set.of(deploymentId));
+ Map<ZoneId, List<Endpoint>> endpoints = controller.routing().readStepRunnerEndpointsOf(Set.of(deploymentId));
logEndpoints(endpoints, logger);
DeploymentRoutingContext context = controller.routing().of(deploymentId);
boolean resolveEndpoints = context.routingMethod() == RoutingMethod.exclusive;
@@ -594,7 +594,7 @@ public class InternalStepRunner implements StepRunner {
deployments.add(new DeploymentId(id.application(), zoneId));
logger.log("Attempting to find endpoints ...");
- var endpoints = controller.routing().readTestRunnerEndpointsOf(deployments);
+ var endpoints = controller.routing().readStepRunnerEndpointsOf(deployments);
if ( ! endpoints.containsKey(zoneId)) {
logger.log(WARNING, "Endpoints for the deployment to test vanished again, while it was still active!");
return Optional.of(error);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 7c7f0fe9876..daa64ddb07f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -150,6 +150,7 @@ import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
@@ -250,6 +251,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
private HttpResponse handleGET(Path path, HttpRequest request) {
if (path.matches("/application/v4/")) return root(request);
+ if (path.matches("/application/v4/search/{*}")) return search(path, request);
if (path.matches("/application/v4/notifications")) return notifications(request, Optional.ofNullable(request.getProperty("tenant")), true);
if (path.matches("/application/v4/tenant")) return tenants(request);
if (path.matches("/application/v4/tenant/{tenant}")) return tenant(path.get("tenant"), request);
@@ -310,6 +312,57 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
return ErrorResponse.notFoundError("Nothing at " + path);
}
+ private HttpResponse search(Path path, HttpRequest request) {
+ if (path.matches("/application/v4/search/deployment")) return searchDeploymentsByEndpoint(request);
+ return ErrorResponse.notFoundError("Nothing at " + path);
+ }
+
+ private HttpResponse searchDeploymentsByEndpoint(HttpRequest request) {
+ String endpoint = request.getProperty("endpoint");
+ if (endpoint == null) {
+ throw new IllegalArgumentException("Missing 'endpoint' query parameter");
+ }
+ endpoint = endpoint.trim();
+ if (endpoint.startsWith("https://") || endpoint.startsWith("http://")) {
+ // Trim scheme and port
+ endpoint = URI.create(endpoint).getHost();
+ }
+ List<Application> applications = controller.applications().asList();
+ record EndpointTarget(DeploymentId deployment, ClusterSpec.Id cluster) {}
+ List<EndpointTarget> targets = new ArrayList<>();
+ out:
+ for (var app : applications) {
+ Optional<Endpoint> declaredEndpoint = controller.routing().declaredEndpointsOf(app).dnsName(endpoint);
+ if (declaredEndpoint.isPresent()) {
+ for (var target : declaredEndpoint.get().targets()) {
+ targets.add(new EndpointTarget(target.deployment(), declaredEndpoint.get().cluster()));
+ }
+ break;
+ } else {
+ for (var instance : app.instances().values()) {
+ for (var deployment : instance.deployments().values()) {
+ DeploymentId id = new DeploymentId(instance.id(), deployment.zone());
+ Optional<Endpoint> matchingEndpoint = controller.routing().readEndpointsOf(id).dnsName(endpoint);
+ if (matchingEndpoint.isPresent()) {
+ for (var target : matchingEndpoint.get().targets()) {
+ targets.add(new EndpointTarget(target.deployment(), matchingEndpoint.get().cluster()));
+ }
+ break out;
+ }
+ }
+ }
+ }
+ }
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ Cursor deploymentArray = root.setArray("deployments");
+ for (var target : targets) {
+ toSlime(target.deployment, target.cluster, deploymentArray.addObject(), request);
+ }
+ return new SlimeJsonResponse(slime);
+ }
+
+
private HttpResponse handlePUT(Path path, HttpRequest request) {
if (path.matches("/application/v4/tenant/{tenant}")) return updateTenant(path.get("tenant"), request);
if (path.matches("/application/v4/tenant/{tenant}/access/request/operator")) return requestSshAccess(path.get("tenant"), request);
@@ -2640,7 +2693,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
deployment.version(),
deployment.revision(),
deployment.at(),
- controller.routing().readTestRunnerEndpointsOf(deployments),
+ controller.routing().readStepRunnerEndpointsOf(deployments),
controller.applications().reachableContentClustersByZone(deployments)));
}
@@ -3029,6 +3082,22 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
request.getUri()).toString());
}
+ private void toSlime(DeploymentId id, ClusterSpec.Id cluster, Cursor object, HttpRequest request) {
+ object.setString("tenant", id.applicationId().tenant().value());
+ object.setString("application", id.applicationId().application().value());
+ object.setString("instance", id.applicationId().instance().value());
+ object.setString("environment", id.zoneId().environment().value());
+ object.setString("region", id.zoneId().region().value());
+ object.setString("cluster", cluster.value());
+ object.setString("url", withPath("/application/v4" +
+ "/tenant/" + id.applicationId().tenant().value() +
+ "/application/" + id.applicationId().application().value() +
+ "/instance/" + id.applicationId().instance().value() +
+ "/environment/" + id.zoneId().environment().value() +
+ "/region/" + id.zoneId().region().value(),
+ request.getUri()).toString());
+ }
+
private void stringsToSlime(List<String> strings, Cursor array) {
for (String string : strings)
array.addString(string);
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 ac16aa727d5..14c279c9ef8 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
@@ -787,6 +787,22 @@ public class ApplicationApiTest extends ControllerContainerTest {
},
200);
+ // GET searches deployments by endpoints
+ tester.assertResponse(request("/application/v4/search/deployment", GET).userIdentity(HOSTED_VESPA_OPERATOR),
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Missing 'endpoint' query parameter\"}", 400);
+ tester.assertResponse(request("/application/v4/search/deployment", GET).properties(Map.of("endpoint", "https://instance1.application1.tenant1.global.vespa.oath.cloud:4443"))
+ .userIdentity(HOSTED_VESPA_OPERATOR),
+ new File("search-deployments-multi.json"), 200);
+ tester.assertResponse(request("/application/v4/search/deployment", GET).properties(Map.of("endpoint", "instance1.application1.tenant1.global.vespa.oath.cloud"))
+ .userIdentity(HOSTED_VESPA_OPERATOR),
+ new File("search-deployments-multi.json"), 200);
+ tester.assertResponse(request("/application/v4/search/deployment", GET).properties(Map.of("endpoint", "instance1.application1.tenant1.us-central-1.vespa.oath.cloud"))
+ .userIdentity(HOSTED_VESPA_OPERATOR),
+ new File("search-deployments-single.json"), 200);
+ tester.assertResponse(request("/application/v4/search/deployment", GET).properties(Map.of("endpoint", "non-existent"))
+ .userIdentity(HOSTED_VESPA_OPERATOR),
+ "{\"deployments\":[]}", 200);
+
// DELETE application with active deployments fails
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", DELETE)
.userIdentity(USER_ID)
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/search-deployments-multi.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/search-deployments-multi.json
new file mode 100644
index 00000000000..8eded55ad64
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/search-deployments-multi.json
@@ -0,0 +1,31 @@
+{
+ "deployments": [
+ {
+ "tenant": "tenant1",
+ "application": "application1",
+ "instance": "instance1",
+ "environment": "prod",
+ "region": "us-central-1",
+ "cluster": "foo",
+ "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1"
+ },
+ {
+ "tenant": "tenant1",
+ "application": "application1",
+ "instance": "instance1",
+ "environment": "prod",
+ "region": "us-east-3",
+ "cluster": "foo",
+ "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-east-3"
+ },
+ {
+ "tenant": "tenant1",
+ "application": "application1",
+ "instance": "instance1",
+ "environment": "prod",
+ "region": "us-west-1",
+ "cluster": "foo",
+ "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1"
+ }
+ ]
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/search-deployments-single.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/search-deployments-single.json
new file mode 100644
index 00000000000..ef524c300dc
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/search-deployments-single.json
@@ -0,0 +1,13 @@
+{
+ "deployments": [
+ {
+ "tenant": "tenant1",
+ "application": "application1",
+ "instance": "instance1",
+ "environment": "prod",
+ "region": "us-central-1",
+ "cluster": "default",
+ "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1"
+ }
+ ]
+}