summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorValerij Fredriksen <valerijf@yahooinc.com>2022-02-18 15:24:56 +0100
committerValerij Fredriksen <valerijf@yahooinc.com>2022-02-18 15:24:56 +0100
commitb33b6cbd6ad7ec2ec441d59486a7b473e745e8ad (patch)
tree5bcd88a3b9ab02ddce3fc7c00b30ef645abddd50 /controller-server
parentf2db340fe6129aa10ed907a9a54555546b4819f1 (diff)
Allow operators to list all notifications
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDb.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java36
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/notifications-applicationPackage.json24
5 files changed, 60 insertions, 11 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDb.java
index 4fdfdfc461b..c0bd1ac03ff 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDb.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDb.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.notification;
import com.yahoo.collections.Pair;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.TenantName;
import com.yahoo.text.Text;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.Controller;
@@ -41,6 +42,10 @@ public class NotificationsDb {
this.curatorDb = curatorDb;
}
+ public List<TenantName> listTenantsWithNotifications() {
+ return curatorDb.listTenantsWithNotifications();
+ }
+
public List<Notification> listNotifications(NotificationSource source, boolean productionOnly) {
return curatorDb.readNotifications(source.tenant()).stream()
.filter(notification -> source.contains(notification.source()) && (!productionOnly || notification.source().isProduction()))
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
index 9b1a03039fb..6eaad63251c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
@@ -625,7 +625,7 @@ public class CuratorDb {
}
- public List<TenantName> listNotifications() {
+ public List<TenantName> listTenantsWithNotifications() {
return curator.getChildren(notificationsRoot).stream()
.map(TenantName::from)
.collect(Collectors.toUnmodifiableList());
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 3b4154759ca..f6bc37835b8 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
@@ -144,6 +144,9 @@ import java.util.Optional;
import java.util.OptionalLong;
import java.util.Scanner;
import java.util.StringJoiner;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -233,11 +236,12 @@ 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/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);
if (path.matches("/application/v4/tenant/{tenant}/access/request/ssh")) return accessRequests(path.get("tenant"), request);
if (path.matches("/application/v4/tenant/{tenant}/info")) return tenantInfo(path.get("tenant"), request);
- if (path.matches("/application/v4/tenant/{tenant}/notifications")) return notifications(path.get("tenant"), request);
+ if (path.matches("/application/v4/tenant/{tenant}/notifications")) return notifications(request, Optional.of(path.get("tenant")), false);
if (path.matches("/application/v4/tenant/{tenant}/secret-store/{name}/validate")) return validateSecretStore(path.get("tenant"), path.get("name"), request);
if (path.matches("/application/v4/tenant/{tenant}/application")) return applications(path.get("tenant"), Optional.empty(), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return application(path.get("tenant"), path.get("application"), request);
@@ -542,26 +546,38 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
.withAddress(updateTenantInfoAddress(insp.field("address"), oldContact.address()));
}
- private HttpResponse notifications(String tenantName, HttpRequest request) {
- NotificationSource notificationSource = new NotificationSource(TenantName.from(tenantName),
- Optional.ofNullable(request.getProperty("application")).map(ApplicationName::from),
- Optional.ofNullable(request.getProperty("instance")).map(InstanceName::from),
- Optional.empty(), Optional.empty(), Optional.empty(), OptionalLong.empty());
-
+ private HttpResponse notifications(HttpRequest request, Optional<String> tenant, boolean includeTenantFieldInResponse) {
+ boolean productionOnly = showOnlyProductionInstances(request);
Slime slime = new Slime();
Cursor notificationsArray = slime.setObject().setArray("notifications");
- controller.notificationsDb().listNotifications(notificationSource, showOnlyProductionInstances(request))
- .forEach(notification -> toSlime(notificationsArray.addObject(), notification));
+
+ tenant.map(t -> Stream.of(TenantName.from(t)))
+ .orElseGet(() -> controller.notificationsDb().listTenantsWithNotifications().stream())
+ .flatMap(tenantName -> controller.notificationsDb().listNotifications(NotificationSource.from(tenantName), productionOnly).stream())
+ .filter(notification ->
+ propertyEquals(request, "application", ApplicationName::from, notification.source().application()) &&
+ propertyEquals(request, "instance", InstanceName::from, notification.source().instance()) &&
+ propertyEquals(request, "zone", ZoneId::from, notification.source().zoneId()) &&
+ propertyEquals(request, "job", JobType::fromJobName, notification.source().jobType()) &&
+ propertyEquals(request, "type", Notification.Type::valueOf, Optional.of(notification.type())) &&
+ propertyEquals(request, "level", Notification.Level::valueOf, Optional.of(notification.level())))
+ .forEach(notification -> toSlime(notificationsArray.addObject(), notification, includeTenantFieldInResponse));
return new SlimeJsonResponse(slime);
}
+ private static <T> boolean propertyEquals(HttpRequest request, String property, Function<String, T> mapper, Optional<T> value) {
+ return Optional.ofNullable(request.getProperty(property))
+ .map(propertyValue -> value.isPresent() && mapper.apply(propertyValue).equals(value.get()))
+ .orElse(true);
+ }
- private static void toSlime(Cursor cursor, Notification notification) {
+ private static void toSlime(Cursor cursor, Notification notification, boolean includeTenantFieldInResponse) {
cursor.setLong("at", notification.at().toEpochMilli());
cursor.setString("level", notificationLevelAsString(notification.level()));
cursor.setString("type", notificationTypeAsString(notification.type()));
Cursor messagesArray = cursor.setArray("messages");
notification.messages().forEach(messagesArray::addString);
+ if (includeTenantFieldInResponse) cursor.setString("tenant", notification.source().tenant().value());
notification.source().application().ifPresent(application -> cursor.setString("application", application.value()));
notification.source().instance().ifPresent(instance -> cursor.setString("instance", instance.value()));
notification.source().zoneId().ifPresent(zoneId -> {
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 516911b3c7b..38340c1a988 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
@@ -861,6 +861,10 @@ public class ApplicationApiTest extends ControllerContainerTest {
"");
addNotifications(TenantName.from("tenant1"));
+ addNotifications(TenantName.from("tenant2"));
+ tester.assertResponse(request("/application/v4/notifications", GET)
+ .properties(Map.of("type", "applicationPackage")).userIdentity(HOSTED_VESPA_OPERATOR),
+ new File("notifications-applicationPackage.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/notifications", GET).userIdentity(USER_ID),
new File("notifications-tenant1.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/notifications", GET)
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/notifications-applicationPackage.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/notifications-applicationPackage.json
new file mode 100644
index 00000000000..d5a44538d12
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/notifications-applicationPackage.json
@@ -0,0 +1,24 @@
+{
+ "notifications": [
+ {
+ "at": "(ignore)",
+ "level": "warning",
+ "type": "applicationPackage",
+ "messages": [
+ "Something something deprecated..."
+ ],
+ "tenant": "tenant1",
+ "application": "app1"
+ },
+ {
+ "at": "(ignore)",
+ "level": "warning",
+ "type": "applicationPackage",
+ "messages": [
+ "Something something deprecated..."
+ ],
+ "tenant": "tenant2",
+ "application": "app1"
+ }
+ ]
+}