diff options
3 files changed, 30 insertions, 3 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClient.java index c2973313995..49a24296fd3 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClient.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClient.java @@ -14,6 +14,7 @@ import java.time.YearMonth; import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -30,7 +31,7 @@ public interface ResourceDatabaseClient { void writeScalingEvents(ClusterId clusterId, Collection<Cluster.ScalingEvent> scalingEvents); - List<Cluster.ScalingEvent> scalingEvents(Instant from, Instant to, Optional<ApplicationId> application); + Map<ClusterId, List<Cluster.ScalingEvent>> scalingEvents(Instant from, Instant to, DeploymentId deploymentId); Set<YearMonth> getMonthsWithSnapshotsForTenant(TenantName tenantName); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java index cae3743f386..f3abfd37b38 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java @@ -138,8 +138,8 @@ public class ResourceDatabaseClientMock implements ResourceDatabaseClient { } @Override - public List<Cluster.ScalingEvent> scalingEvents(Instant from, Instant to, Optional<ApplicationId> application) { - return List.of(); + public Map<ClusterId, List<Cluster.ScalingEvent>> scalingEvents(Instant from, Instant to, DeploymentId deploymentId) { + return Map.of(); } public void setPlan(TenantName tenant, Plan plan) { 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 cf21b8ef0af..670cb775c69 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 @@ -137,6 +137,8 @@ import java.security.PublicKey; import java.time.DayOfWeek; import java.time.Duration; import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Base64; @@ -283,6 +285,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/logs")) return logs(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request.propertyMap()); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/access/support")) return supportAccess(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request.propertyMap()); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/node/{node}/service-dump")) return getServiceDump(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), path.get("node"), request); + if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/scaling")) return scaling(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/metrics")) return metrics(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region")); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/global-rotation")) return rotationStatus(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), Optional.ofNullable(request.getProperty("endpointId"))); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/global-rotation/override")) return getGlobalRotationOverride(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region")); @@ -1426,6 +1429,29 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { return buildResponseFromProtonMetrics(protonMetrics); } + private HttpResponse scaling(String tenantName, String applicationName, String instanceName, String environment, String region, HttpRequest request) { + var from = Optional.ofNullable(request.getProperty("from")) + .map(Long::valueOf) + .map(Instant::ofEpochSecond) + .orElse(Instant.EPOCH); + var until = Optional.ofNullable(request.getProperty("until")) + .map(Long::valueOf) + .map(Instant::ofEpochSecond) + .orElse(Instant.now(controller.clock())); + + var application = ApplicationId.from(tenantName, applicationName, instanceName); + var zone = requireZone(environment, region); + var deployment = new DeploymentId(application, zone); + var events = controller.serviceRegistry().resourceDatabase().scalingEvents(from, until, deployment); + var slime = new Slime(); + var root = slime.setObject(); + for (var entry : events.entrySet()) { + var serviceRoot = root.setArray(entry.getKey().clusterId().value()); + scalingEventsToSlime(entry.getValue(), serviceRoot); + } + return new SlimeJsonResponse(slime); + } + private JsonResponse buildResponseFromProtonMetrics(List<ProtonMetrics> protonMetrics) { try { var jsonObject = jsonMapper.createObjectNode(); |