Skip to content

Commit

Permalink
[#9670] Platform: Alerts: Handle Alert Receivers/Alert Routes duplica…
Browse files Browse the repository at this point in the history
…tes gracefully, now response returns 500.

Summary:
  - Extracted the AlertReceiver functionality into a separate service;
  - Changed responses for updates of AlertRoute and AlertReceiver with duplicate names.

Test Plan:
- Recheck alert receiver operations;
- Check responses for attempts to create/update alert receivers/routes with duplicate names.

Reviewers: amalyshev

Reviewed By: amalyshev

Subscribers: yugaware, jenkins-bot

Differential Revision: https://phabricator.dev.yugabyte.com/D12566
  • Loading branch information
SergeyPotachev committed Aug 13, 2021
1 parent c09d3c8 commit 4a20bad
Show file tree
Hide file tree
Showing 18 changed files with 519 additions and 245 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.yugabyte.yw.common.alerts.AlertReceiverEmailParams;
import com.yugabyte.yw.common.alerts.AlertReceiverInterface;
import com.yugabyte.yw.common.alerts.AlertReceiverManager;
import com.yugabyte.yw.common.alerts.AlertReceiverService;
import com.yugabyte.yw.common.alerts.AlertRouteService;
import com.yugabyte.yw.common.alerts.AlertService;
import com.yugabyte.yw.common.alerts.AlertUtils;
Expand Down Expand Up @@ -55,6 +56,7 @@ public class AlertManager {

private final EmailHelper emailHelper;
private final AlertDefinitionGroupService alertDefinitionGroupService;
private final AlertReceiverService alertReceiverService;
private final AlertRouteService alertRouteService;
private final AlertReceiverManager receiversManager;
private final AlertService alertService;
Expand All @@ -65,12 +67,14 @@ public AlertManager(
EmailHelper emailHelper,
AlertService alertService,
AlertDefinitionGroupService alertDefinitionGroupService,
AlertReceiverService alertReceiverService,
AlertRouteService alertRouteService,
AlertReceiverManager receiversManager,
MetricService metricService) {
this.emailHelper = emailHelper;
this.alertService = alertService;
this.alertDefinitionGroupService = alertDefinitionGroupService;
this.alertReceiverService = alertReceiverService;
this.alertRouteService = alertRouteService;
this.receiversManager = receiversManager;
this.metricService = metricService;
Expand Down Expand Up @@ -222,7 +226,7 @@ private boolean sendNotification(

for (AlertReceiver receiver : receivers) {
try {
AlertUtils.validate(receiver);
alertReceiverService.validate(receiver);
} catch (YWValidateException e) {
if (report.failuresByReceiver(receiver.getUuid()) == 0) {
log.warn("Receiver {} skipped: {}", receiver.getUuid(), e.getMessage(), e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright 2021 YugaByte, Inc. and Contributors
*
* Licensed under the Polyform Free Trial License 1.0.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://github.com/YugaByte/yugabyte-db/blob/master/licenses/POLYFORM-FREE-TRIAL-LICENSE-1.0.0.txt
*/
package com.yugabyte.yw.common.alerts;

import static com.yugabyte.yw.models.helpers.CommonUtils.appendInClause;
import static play.mvc.Http.Status.BAD_REQUEST;
import static play.mvc.Http.Status.INTERNAL_SERVER_ERROR;

import com.yugabyte.yw.common.YWServiceException;
import com.yugabyte.yw.models.AlertReceiver;
import com.yugabyte.yw.models.AlertRoute;
import io.ebean.ExpressionList;
import io.ebean.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import play.data.validation.Constraints.Required;

@Singleton
@Slf4j
public class AlertReceiverService {

public AlertReceiver get(UUID customerUUID, UUID receiverUUID) {
return AlertReceiver.get(customerUUID, receiverUUID);
}

private AlertReceiver get(UUID customerUUID, String receiverName) {
return AlertReceiver.createQuery()
.eq("customerUUID", customerUUID)
.eq("name", receiverName)
.findOne();
}

public AlertReceiver getOrBadRequest(UUID customerUUID, @Required UUID receiverUUID) {
AlertReceiver alertReceiver = get(customerUUID, receiverUUID);
if (alertReceiver == null) {
throw new YWServiceException(BAD_REQUEST, "Invalid Alert Receiver UUID: " + receiverUUID);
}
return alertReceiver;
}

public List<AlertReceiver> getOrBadRequest(UUID customerUUID, @Required List<UUID> uuids) {
ExpressionList<AlertReceiver> query =
AlertReceiver.createQuery().eq("customerUUID", customerUUID);
appendInClause(query, "uuid", uuids);
List<AlertReceiver> result = query.findList();
if (result.size() != uuids.size()) {
// We have incorrect receiver id(s).
List<UUID> uuidsToRemove =
result.stream().map(AlertReceiver::getUuid).collect(Collectors.toList());
List<UUID> uuidsToReport = new ArrayList<>(uuids);
uuidsToReport.removeAll(uuidsToRemove);
throw new YWServiceException(
BAD_REQUEST,
"Invalid Alert Receiver UUID: "
+ uuidsToReport
.stream()
.map(uuid -> uuid.toString())
.collect(Collectors.joining(", ")));
}
return result;
}

public List<AlertReceiver> list(UUID customerUUID) {
return AlertReceiver.createQuery().eq("customerUUID", customerUUID).findList();
}

@Transactional
public AlertReceiver save(AlertReceiver receiver) {
if (receiver.getUuid() == null) {
receiver.generateUUID();
}

AlertReceiver valueWithSameName = get(receiver.getCustomerUUID(), receiver.getName());
if ((valueWithSameName != null) && !receiver.getUuid().equals(valueWithSameName.getUuid())) {
throw new YWServiceException(BAD_REQUEST, "Alert receiver with such name already exists.");
}

try {
validate(receiver);
} catch (YWValidateException e) {
throw new YWServiceException(
BAD_REQUEST, "Unable to create/update alert receiver: " + e.getMessage());
}

receiver.save();
return receiver;
}

public void delete(UUID customerUUID, UUID receiverUUID) {
AlertReceiver receiver = getOrBadRequest(customerUUID, receiverUUID);

List<String> blockingRoutes =
receiver
.getRoutesList()
.stream()
.filter(route -> route.getReceiversList().size() == 1)
.map(AlertRoute::getName)
.sorted()
.collect(Collectors.toList());
if (!blockingRoutes.isEmpty()) {
throw new YWServiceException(
BAD_REQUEST,
String.format(
"Unable to delete alert receiver: %s. %d alert routes have it as a last receiver."
+ " Examples: %s",
receiverUUID,
blockingRoutes.size(),
blockingRoutes.stream().limit(5).collect(Collectors.toList())));
}

if (!receiver.delete()) {
throw new YWServiceException(
INTERNAL_SERVER_ERROR, "Unable to delete alert receiver: " + receiverUUID);
}
log.info("Deleted alert receiver {} for customer {}", receiverUUID, customerUUID);
}

public void validate(AlertReceiver receiver) throws YWValidateException {
if (receiver.getParams() == null) {
throw new YWValidateException("Incorrect parameters in AlertReceiver.");
}
receiver.getParams().validate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@ public class AlertRouteService {

private final AlertDefinitionGroupService alertDefinitionGroupService;

private final AlertReceiverService alertReceiverService;

@Inject
public AlertRouteService(AlertDefinitionGroupService alertDefinitionGroupService) {
public AlertRouteService(
AlertReceiverService alertReceiverService,
AlertDefinitionGroupService alertDefinitionGroupService) {
this.alertReceiverService = alertReceiverService;
this.alertDefinitionGroupService = alertDefinitionGroupService;
}

Expand Down Expand Up @@ -91,6 +96,11 @@ public AlertRoute save(AlertRoute route) {
"Can't set the alert route as non-default. Make another route as default at first.");
}

AlertRoute valueWithSameName = get(route.getCustomerUUID(), route.getName());
if ((valueWithSameName != null) && !route.getUuid().equals(valueWithSameName.getUuid())) {
throw new YWServiceException(BAD_REQUEST, "Alert route with such name already exists.");
}

AlertRoute defaultRoute = getDefaultRoute(route.getCustomerUUID());
route.save();

Expand All @@ -107,6 +117,13 @@ public AlertRoute save(AlertRoute route) {
return route;
}

private AlertRoute get(UUID customerUUID, String routeName) {
return AlertRoute.createQuery()
.eq("customerUUID", customerUUID)
.eq("name", routeName)
.findOne();
}

public AlertRoute get(UUID customerUUID, UUID routeUUID) {
return AlertRoute.get(customerUUID, routeUUID);
}
Expand All @@ -120,13 +137,13 @@ public AlertRoute getOrBadRequest(UUID customerUUID, UUID routeUUID) {
}

public List<AlertRoute> listByCustomer(UUID customerUUID) {
return AlertRoute.createQuery().eq("customer_uuid", customerUUID).findList();
return AlertRoute.createQuery().eq("customerUUID", customerUUID).findList();
}

public AlertRoute getDefaultRoute(UUID customerUUID) {
return AlertRoute.createQuery()
.eq("customer_uuid", customerUUID)
.eq("default_route", true)
.eq("customerUUID", customerUUID)
.eq("defaultRoute", true)
.findOne();
}

Expand All @@ -144,7 +161,11 @@ public AlertRoute createDefaultRoute(UUID customerUUID) {
defaultParams.defaultSmtpSettings = true;
defaultParams.defaultRecipients = true;
AlertReceiver defaultReceiver =
AlertReceiver.create(customerUUID, "Default Receiver", defaultParams);
new AlertReceiver()
.setCustomerUUID(customerUUID)
.setName("Default Receiver")
.setParams(defaultParams);
defaultReceiver = alertReceiverService.save(defaultReceiver);

AlertRoute route =
new AlertRoute()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,4 @@ public static String getJsonTypeName(AlertReceiverParams params) {
JsonTypeName an = clz.getDeclaredAnnotation(JsonTypeName.class);
return an.value();
}

public static void validate(AlertReceiver receiver) throws YWValidateException {
if (receiver.getParams() == null) {
throw new YWValidateException("Incorrect parameters in AlertReceiver.");
}
receiver.getParams().validate();
}
}
Loading

0 comments on commit 4a20bad

Please sign in to comment.