Browse Source

[Improvement][Alert] Add a test send feature when creating an alert instance (#15163)

* add alert test send feature

* update doc

* add alarm instance test send ui

* update

* fix mvn

* fix test

* update

* update

* change to rpc

* fix ut

* fix ut

* update

* update

* change result

* update

* Update docs/docs/en/guide/alert/alert_plugin_user_guide.md

Co-authored-by: Aaron Wang <wangweirao16@gmail.com>

---------

Co-authored-by: Aaron Wang <wangweirao16@gmail.com>
augit-log
旺阳 6 months ago committed by GitHub
parent
commit
6aa6e114a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      docs/docs/en/guide/alert/alert_plugin_user_guide.md
  2. 2
      docs/docs/zh/guide/alert/alert_plugin_user_guide.md
  3. BIN
      docs/img/new_ui/dev/alert/alert_instance01.png
  4. BIN
      docs/img/new_ui/dev/alert/alert_instance02.png
  5. BIN
      docs/img/new_ui/dev/alert/alert_instance03.png
  6. 2
      dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-api/src/main/java/org/apache/dolphinscheduler/alert/api/AlertConstants.java
  7. 11
      dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/rpc/AlertOperatorImpl.java
  8. 58
      dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/AlertBootstrapService.java
  9. 1
      dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/ListenerEventPostService.java
  10. 61
      dolphinscheduler-alert/dolphinscheduler-alert-server/src/test/java/org/apache/dolphinscheduler/alert/runner/AlertBootstrapServiceTest.java
  11. 4
      dolphinscheduler-api/pom.xml
  12. 14
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceController.java
  13. 5
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java
  14. 2
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceService.java
  15. 56
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AlertPluginInstanceServiceImpl.java
  16. 28
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceControllerTest.java
  17. 29
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceServiceTest.java
  18. 4
      dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/IAlertOperator.java
  19. 32
      dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/request/AlertTestSendRequest.java
  20. 4
      dolphinscheduler-registry/dolphinscheduler-registry-api/pom.xml
  21. 1
      dolphinscheduler-registry/dolphinscheduler-registry-api/src/main/java/org/apache/dolphinscheduler/registry/api/RegistryClient.java
  22. 4
      dolphinscheduler-ui/src/locales/en_US/security.ts
  23. 1
      dolphinscheduler-ui/src/locales/zh_CN/security.ts
  24. 11
      dolphinscheduler-ui/src/service/modules/alert-plugin/index.ts
  25. 8
      dolphinscheduler-ui/src/service/modules/alert-plugin/types.ts
  26. 66
      dolphinscheduler-ui/src/views/security/alarm-instance-manage/detail.tsx
  27. 27
      dolphinscheduler-ui/src/views/security/alarm-instance-manage/use-detail.ts

2
docs/docs/en/guide/alert/alert_plugin_user_guide.md

@ -13,6 +13,8 @@ Steps to be used are as follows:
- Select the corresponding alarm plug-in and fill in the relevant alarm parameters.
- Select `Alarm Group Management`, create an alarm group, and choose the corresponding alarm instance.
> You can click `Test Send` button to test whether the alarm instance is configured correctly.
![alert-instance01](../../../../img/new_ui/dev/alert/alert_instance01.png)
![alert-instance02](../../../../img/new_ui/dev/alert/alert_instance02.png)

2
docs/docs/zh/guide/alert/alert_plugin_user_guide.md

@ -10,6 +10,8 @@
然后选择告警组管理,创建告警组,选择相应的告警实例即可。
> 可以使用`测试发送`功能来验证配置的告警实例是否正确。
![alert-instance01](../../../../img/new_ui/dev/alert/alert_instance01.png)
![alert-instance02](../../../../img/new_ui/dev/alert/alert_instance02.png)
![alert-instance03](../../../../img/new_ui/dev/alert/alert_instance03.png)

BIN
docs/img/new_ui/dev/alert/alert_instance01.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 108 KiB

BIN
docs/img/new_ui/dev/alert/alert_instance02.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 145 KiB

BIN
docs/img/new_ui/dev/alert/alert_instance03.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 150 KiB

2
dolphinscheduler-alert/dolphinscheduler-alert-plugins/dolphinscheduler-alert-api/src/main/java/org/apache/dolphinscheduler/alert/api/AlertConstants.java

@ -31,6 +31,8 @@ public final class AlertConstants {
public static final String WARNING_TYPE = "warningType";
public static final String NAME_WARNING_TYPE = "WarningType";
public static final String TEST_TITLE = "DolphinScheduler test alert";
public static final String TEST_CONTENT = "[{\"message\":\" This is a test alert message form DolphinScheduler\"}]";
private AlertConstants() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");

11
dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/rpc/AlertOperatorImpl.java

@ -20,6 +20,7 @@ import org.apache.dolphinscheduler.alert.service.AlertBootstrapService;
import org.apache.dolphinscheduler.extract.alert.IAlertOperator;
import org.apache.dolphinscheduler.extract.alert.request.AlertSendRequest;
import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse;
import org.apache.dolphinscheduler.extract.alert.request.AlertTestSendRequest;
import lombok.extern.slf4j.Slf4j;
@ -44,4 +45,14 @@ public class AlertOperatorImpl implements IAlertOperator {
log.info("Handle AlertSendRequest finish: {}", alertSendResponse);
return alertSendResponse;
}
@Override
public AlertSendResponse sendTestAlert(AlertTestSendRequest alertSendRequest) {
log.info("Received AlertTestSendRequest : {}", alertSendRequest);
AlertSendResponse alertSendResponse = alertBootstrapService.syncTestSend(
alertSendRequest.getPluginDefineId(),
alertSendRequest.getPluginInstanceParams());
log.info("Handle AlertTestSendRequest finish: {}", alertSendResponse);
return alertSendResponse;
}
}

58
dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/AlertBootstrapService.java

@ -38,6 +38,7 @@ import org.apache.dolphinscheduler.dao.entity.Alert;
import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance;
import org.apache.dolphinscheduler.dao.entity.AlertSendStatus;
import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
@ -192,7 +193,7 @@ public final class AlertBootstrapService extends BaseDaemonThread implements Aut
if (alertResult != null) {
AlertSendResponse.AlertSendResponseResult alertSendResponseResult =
new AlertSendResponse.AlertSendResponseResult(
Boolean.parseBoolean(String.valueOf(alertResult.getStatus())),
Boolean.parseBoolean(alertResult.getStatus()),
alertResult.getMessage());
sendResponseStatus = sendResponseStatus && alertSendResponseResult.isSuccess();
sendResponseResults.add(alertSendResponseResult);
@ -302,6 +303,61 @@ public final class AlertBootstrapService extends BaseDaemonThread implements Aut
}
}
public AlertSendResponse syncTestSend(int pluginDefineId, String pluginInstanceParams) {
boolean sendResponseStatus = true;
List<AlertSendResponse.AlertSendResponseResult> sendResponseResults = new ArrayList<>();
Optional<AlertChannel> alertChannelOptional = alertPluginManager.getAlertChannel(pluginDefineId);
if (!alertChannelOptional.isPresent()) {
String message = String.format("Test send alert error: the channel doesn't exist, pluginDefineId: %s",
pluginDefineId);
AlertSendResponse.AlertSendResponseResult alertSendResponseResult =
new AlertSendResponse.AlertSendResponseResult();
alertSendResponseResult.setSuccess(false);
alertSendResponseResult.setMessage(message);
sendResponseResults.add(alertSendResponseResult);
log.error("Test send alert error : not found plugin {}", pluginDefineId);
return new AlertSendResponse(false, sendResponseResults);
}
AlertChannel alertChannel = alertChannelOptional.get();
Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(pluginInstanceParams);
AlertData alertData = AlertData.builder()
.title(AlertConstants.TEST_TITLE)
.content(AlertConstants.TEST_CONTENT)
.warnType(WarningType.ALL.getCode())
.build();
AlertInfo alertInfo = AlertInfo.builder()
.alertData(alertData)
.alertParams(paramsMap)
.build();
try {
AlertResult alertResult = alertChannel.process(alertInfo);
if (alertResult != null) {
AlertSendResponse.AlertSendResponseResult alertSendResponseResult =
new AlertSendResponse.AlertSendResponseResult(
Boolean.parseBoolean(alertResult.getStatus()),
alertResult.getMessage());
sendResponseStatus = alertSendResponseResult.isSuccess();
sendResponseResults.add(alertSendResponseResult);
}
} catch (Exception e) {
log.error("Test send alert error", e);
AlertSendResponse.AlertSendResponseResult alertSendResponseResult =
new AlertSendResponse.AlertSendResponseResult();
alertSendResponseResult.setSuccess(false);
alertSendResponseResult.setMessage(e.getMessage());
sendResponseResults.add(alertSendResponseResult);
return new AlertSendResponse(false, sendResponseResults);
}
return new AlertSendResponse(sendResponseStatus, sendResponseResults);
}
@Override
public void close() {
log.info("Closed AlertBootstrapService...");

1
dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/java/org/apache/dolphinscheduler/alert/service/ListenerEventPostService.java

@ -259,5 +259,4 @@ public final class ListenerEventPostService extends BaseDaemonThread implements
public void close() {
log.info("Closed ListenerEventPostService...");
}
}

61
dolphinscheduler-alert/dolphinscheduler-alert-server/src/test/java/org/apache/dolphinscheduler/alert/runner/AlertBootstrapServiceTest.java

@ -26,15 +26,19 @@ import org.apache.dolphinscheduler.alert.config.AlertConfig;
import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager;
import org.apache.dolphinscheduler.alert.service.AlertBootstrapService;
import org.apache.dolphinscheduler.common.enums.WarningType;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.dao.AlertDao;
import org.apache.dolphinscheduler.dao.PluginDao;
import org.apache.dolphinscheduler.dao.entity.Alert;
import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance;
import org.apache.dolphinscheduler.dao.entity.ListenerEvent;
import org.apache.dolphinscheduler.dao.entity.PluginDefine;
import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.Assertions;
@ -42,6 +46,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;
@ -63,6 +68,18 @@ public class AlertBootstrapServiceTest {
@InjectMocks
private AlertBootstrapService alertBootstrapService;
private static final String PLUGIN_INSTANCE_PARAMS =
"{\"User\":\"xx\",\"receivers\":\"xx\",\"sender\":\"xx\",\"smtpSslTrust\":\"*\",\"enableSmtpAuth\":\"true\",\"receiverCcs\":null,\"showType\":\"table\",\"starttlsEnable\":\"false\",\"serverPort\":\"25\",\"serverHost\":\"xx\",\"Password\":\"xx\",\"sslEnable\":\"false\"}";
private static final String PLUGIN_INSTANCE_NAME = "alert-instance-mail";
private static final String TITLE = "alert mail test TITLE";
private static final String CONTENT = "alert mail test CONTENT";
private static final List<ListenerEvent> EVENTS = new ArrayList<>();
private static final int PLUGIN_DEFINE_ID = 1;
private static final int ALERT_GROUP_ID = 1;
@BeforeEach
public void before() {
MockitoAnnotations.initMocks(this);
@ -70,17 +87,12 @@ public class AlertBootstrapServiceTest {
@Test
public void testSyncHandler() {
int alertGroupId = 1;
String title = "alert mail test title";
String content = "alert mail test content";
// 1.alert instance does not exist
when(alertDao.listInstanceByAlertGroupId(alertGroupId)).thenReturn(null);
when(alertDao.listInstanceByAlertGroupId(ALERT_GROUP_ID)).thenReturn(null);
when(alertConfig.getWaitTimeout()).thenReturn(0);
AlertSendResponse alertSendResponse =
alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode());
alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode());
Assertions.assertFalse(alertSendResponse.isSuccess());
alertSendResponse.getResResults().forEach(result -> logger
.info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage()));
@ -101,7 +113,7 @@ public class AlertBootstrapServiceTest {
when(pluginDao.getPluginDefineById(pluginDefineId)).thenReturn(pluginDefine);
alertSendResponse =
alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode());
alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode());
Assertions.assertFalse(alertSendResponse.isSuccess());
alertSendResponse.getResResults().forEach(result -> logger
.info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage()));
@ -113,7 +125,7 @@ public class AlertBootstrapServiceTest {
when(alertConfig.getWaitTimeout()).thenReturn(0);
alertSendResponse =
alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode());
alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode());
Assertions.assertFalse(alertSendResponse.isSuccess());
alertSendResponse.getResResults().forEach(result -> logger
.info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage()));
@ -126,7 +138,7 @@ public class AlertBootstrapServiceTest {
when(alertPluginManager.getAlertChannel(1)).thenReturn(Optional.of(alertChannelMock));
alertSendResponse =
alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode());
alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode());
Assertions.assertFalse(alertSendResponse.isSuccess());
alertSendResponse.getResResults().forEach(result -> logger
.info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage()));
@ -140,7 +152,7 @@ public class AlertBootstrapServiceTest {
when(alertConfig.getWaitTimeout()).thenReturn(5000);
alertSendResponse =
alertBootstrapService.syncHandler(alertGroupId, title, content, WarningType.ALL.getCode());
alertBootstrapService.syncHandler(ALERT_GROUP_ID, TITLE, CONTENT, WarningType.ALL.getCode());
Assertions.assertTrue(alertSendResponse.isSuccess());
alertSendResponse.getResResults().forEach(result -> logger
.info("alert send response result, status:{}, message:{}", result.isSuccess(), result.getMessage()));
@ -149,15 +161,12 @@ public class AlertBootstrapServiceTest {
@Test
public void testRun() {
int alertGroupId = 1;
String title = "alert mail test title";
String content = "alert mail test content";
List<Alert> alertList = new ArrayList<>();
Alert alert = new Alert();
alert.setId(1);
alert.setAlertGroupId(alertGroupId);
alert.setTitle(title);
alert.setContent(content);
alert.setAlertGroupId(ALERT_GROUP_ID);
alert.setTitle(TITLE);
alert.setContent(CONTENT);
alert.setWarningType(WarningType.FAILURE);
alertList.add(alert);
@ -170,7 +179,7 @@ public class AlertBootstrapServiceTest {
AlertPluginInstance alertPluginInstance = new AlertPluginInstance(
pluginDefineId, pluginInstanceParams, pluginInstanceName);
alertInstanceList.add(alertPluginInstance);
when(alertDao.listInstanceByAlertGroupId(alertGroupId)).thenReturn(alertInstanceList);
when(alertDao.listInstanceByAlertGroupId(ALERT_GROUP_ID)).thenReturn(alertInstanceList);
String pluginName = "alert-plugin-mail";
PluginDefine pluginDefine = new PluginDefine(pluginName, "1", null);
@ -186,4 +195,20 @@ public class AlertBootstrapServiceTest {
when(alertDao.listInstanceByAlertGroupId(1)).thenReturn(new ArrayList<>());
alertBootstrapService.send(alertList);
}
@Test
public void testSendAlert() {
AlertResult sendResult = new AlertResult();
sendResult.setStatus(String.valueOf(true));
sendResult.setMessage(String.format("Alert Plugin %s send success", PLUGIN_INSTANCE_NAME));
AlertChannel alertChannelMock = mock(AlertChannel.class);
when(alertChannelMock.process(Mockito.any())).thenReturn(sendResult);
when(alertPluginManager.getAlertChannel(1)).thenReturn(Optional.of(alertChannelMock));
Map<String, String> paramsMap = JSONUtils.toMap(PLUGIN_INSTANCE_PARAMS);
MockedStatic<PluginParamsTransfer> pluginParamsTransferMockedStatic =
Mockito.mockStatic(PluginParamsTransfer.class);
pluginParamsTransferMockedStatic.when(() -> PluginParamsTransfer.getPluginParamsMap(PLUGIN_INSTANCE_PARAMS))
.thenReturn(paramsMap);
alertBootstrapService.syncTestSend(PLUGIN_DEFINE_ID, PLUGIN_INSTANCE_PARAMS);
}
}

4
dolphinscheduler-api/pom.xml

@ -228,6 +228,10 @@
<groupId>com.azure.resourcemanager</groupId>
<artifactId>azure-resourcemanager-datafactory</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-extract-alert</artifactId>
</dependency>
</dependencies>
<build>
<testResources>

14
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceController.java

@ -22,6 +22,7 @@ import static org.apache.dolphinscheduler.api.enums.Status.DELETE_ALERT_PLUGIN_I
import static org.apache.dolphinscheduler.api.enums.Status.GET_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.LIST_PAGING_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_ALL_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.SEND_TEST_ALERT_PLUGIN_INSTANCE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_ALERT_PLUGIN_INSTANCE_ERROR;
import org.apache.dolphinscheduler.api.enums.Status;
@ -99,6 +100,19 @@ public class AlertPluginInstanceController extends BaseController {
return returnDataList(result);
}
@Operation(summary = "testSendAlertPluginInstance", description = "TEST_SEND_ALERT_PLUGIN_INSTANCE")
@Parameters({
@Parameter(name = "pluginDefineId", description = "ALERT_PLUGIN_DEFINE_ID", required = true, schema = @Schema(implementation = int.class, example = "100")),
@Parameter(name = "pluginInstanceParams", description = "ALERT_PLUGIN_INSTANCE_PARAMS", required = true, schema = @Schema(implementation = String.class, example = "ALERT_PLUGIN_INSTANCE_PARAMS"))
})
@PostMapping(value = "/test-send")
@ResponseStatus(HttpStatus.OK)
@ApiException(SEND_TEST_ALERT_PLUGIN_INSTANCE_ERROR)
public Result testSendAlertPluginInstance(@RequestParam(value = "pluginDefineId") int pluginDefineId,
@RequestParam(value = "pluginInstanceParams") String pluginInstanceParams) {
return alertPluginInstanceService.testSend(pluginDefineId, pluginInstanceParams);
}
/**
* updateAlertPluginInstance
*

5
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java

@ -472,7 +472,10 @@ public enum Status {
"failed to delete the alert instance, there is an alarm group associated with this alert instance",
"删除告警实例失败,存在与此告警实例关联的警报组"),
PROCESS_DEFINITION_VERSION_IS_USED(110013, "this process definition version is used", "此工作流定义版本被使用"),
ALERT_TEST_SENDING_FAILED(110014, "Alert test sending failed, [{0}]", "alert测试发送失败,[{0}]"),
ALERT_CHANNEL_NOT_EXIST(110015, "Alert channel not exist", "alert channel不存在"),
SEND_TEST_ALERT_PLUGIN_INSTANCE_ERROR(110016, "send test alert plugin instance error", "发送测试告警错误"),
ALERT_SERVER_NOT_EXIST(110017, "Alert server does not exist", "Alert server不存在"),
CREATE_ENVIRONMENT_ERROR(120001, "create environment error", "创建环境失败"),
ENVIRONMENT_NAME_EXISTS(120002, "this environment name [{0}] already exists", "环境名称[{0}]已经存在"),
ENVIRONMENT_NAME_IS_NULL(120003, "this environment name shouldn't be empty.", "环境名称不能为空"),

2
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceService.java

@ -94,4 +94,6 @@ public interface AlertPluginInstanceService {
* @return plugins
*/
Result listPaging(User loginUser, String searchVal, int pageNo, int pageSize);
Result<Void> testSend(int pluginDefineId, String pluginInstanceParams);
}

56
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AlertPluginInstanceServiceImpl.java

@ -31,6 +31,7 @@ import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.common.enums.AlertPluginInstanceType;
import org.apache.dolphinscheduler.common.enums.AuthorizationType;
import org.apache.dolphinscheduler.common.enums.WarningType;
import org.apache.dolphinscheduler.common.model.Server;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.dao.entity.AlertGroup;
import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance;
@ -39,6 +40,13 @@ import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.AlertGroupMapper;
import org.apache.dolphinscheduler.dao.mapper.AlertPluginInstanceMapper;
import org.apache.dolphinscheduler.dao.mapper.PluginDefineMapper;
import org.apache.dolphinscheduler.extract.alert.IAlertOperator;
import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse;
import org.apache.dolphinscheduler.extract.alert.request.AlertTestSendRequest;
import org.apache.dolphinscheduler.extract.base.client.SingletonJdkDynamicRpcClientProxyFactory;
import org.apache.dolphinscheduler.extract.base.utils.Host;
import org.apache.dolphinscheduler.registry.api.RegistryClient;
import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType;
import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
import org.apache.commons.collections4.CollectionUtils;
@ -82,6 +90,9 @@ public class AlertPluginInstanceServiceImpl extends BaseServiceImpl implements A
private final Integer GLOBAL_ALERT_GROUP_ID = 2;
@Autowired
private RegistryClient registryClient;
/**
* creat alert plugin instance
*
@ -352,4 +363,49 @@ public class AlertPluginInstanceServiceImpl extends BaseServiceImpl implements A
return first.isPresent();
}
public Optional<Host> getAlertServerAddress() {
List<Server> serverList = registryClient.getServerList(RegistryNodeType.ALERT_SERVER);
if (CollectionUtils.isEmpty(serverList)) {
return Optional.empty();
}
Server server = serverList.get(0);
return Optional.of(new Host(server.getHost(), server.getPort()));
}
@Override
public Result<Void> testSend(int pluginDefineId, String pluginInstanceParams) {
Result<Void> result = new Result<>();
Optional<Host> alertServerAddressOptional = getAlertServerAddress();
if (!alertServerAddressOptional.isPresent()) {
log.error("Cannot get alert server address, please check the alert server is running");
putMsg(result, Status.ALERT_SERVER_NOT_EXIST);
return result;
}
Host alertServerAddress = alertServerAddressOptional.get();
AlertTestSendRequest alertTestSendRequest = new AlertTestSendRequest(
pluginDefineId,
pluginInstanceParams);
AlertSendResponse alertSendResponse;
try {
IAlertOperator alertOperator = SingletonJdkDynamicRpcClientProxyFactory
.getProxyClient(alertServerAddress.getAddress(), IAlertOperator.class);
alertSendResponse = alertOperator.sendTestAlert(alertTestSendRequest);
log.info("Send alert to: {} successfully, response: {}", alertServerAddress, alertSendResponse);
} catch (Exception e) {
log.error("Send alert: {} to: {} failed", alertTestSendRequest, alertServerAddress, e);
putMsg(result, Status.ALERT_TEST_SENDING_FAILED, e.getMessage());
return result;
}
if (alertSendResponse.isSuccess()) {
putMsg(result, Status.SUCCESS);
} else {
putMsg(result, Status.ALERT_TEST_SENDING_FAILED, alertSendResponse.getResResults().get(0).getMessage());
}
return result;
}
}

28
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AlertPluginInstanceControllerTest.java

@ -92,6 +92,34 @@ public class AlertPluginInstanceControllerTest extends AbstractControllerTest {
assertThat(actualResponseContent.toString()).isEqualTo(expectResponseContent.toString());
}
@Test
public void testSendAlertPluginInstance() throws Exception {
// Given
Result result = JSONUtils.parseObject(
"{\"code\":0,\"msg\":\"success\",\"data\":\"Test Data\",\"success\":true,\"failed\":false}",
Result.class);
final MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
paramsMap.add("pluginDefineId", String.valueOf(pluginDefineId));
paramsMap.add("pluginInstanceParams", pluginInstanceParams);
when(alertPluginInstanceService.testSend(eq(pluginDefineId), eq(pluginInstanceParams)))
.thenReturn(result);
// When
final MvcResult mvcResult = mockMvc.perform(post("/alert-plugin-instances/test-send")
.header(SESSION_ID, sessionId)
.params(paramsMap))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
// Then
final Result actualResponseContent =
JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class);
assertThat(actualResponseContent.toString()).isEqualTo(expectResponseContent.toString());
}
@Test
public void testUpdateAlertPluginInstance() throws Exception {
// Given

29
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AlertPluginInstanceServiceTest.java

@ -25,11 +25,13 @@ import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.permission.ResourcePermissionCheckService;
import org.apache.dolphinscheduler.api.service.impl.AlertPluginInstanceServiceImpl;
import org.apache.dolphinscheduler.api.service.impl.BaseServiceImpl;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.common.enums.AlertPluginInstanceType;
import org.apache.dolphinscheduler.common.enums.AuthorizationType;
import org.apache.dolphinscheduler.common.enums.UserType;
import org.apache.dolphinscheduler.common.enums.WarningType;
import org.apache.dolphinscheduler.common.model.Server;
import org.apache.dolphinscheduler.dao.entity.AlertGroup;
import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance;
import org.apache.dolphinscheduler.dao.entity.PluginDefine;
@ -37,6 +39,10 @@ import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.AlertGroupMapper;
import org.apache.dolphinscheduler.dao.mapper.AlertPluginInstanceMapper;
import org.apache.dolphinscheduler.dao.mapper.PluginDefineMapper;
import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse;
import org.apache.dolphinscheduler.extract.alert.request.AlertTestSendRequest;
import org.apache.dolphinscheduler.registry.api.RegistryClient;
import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType;
import java.util.ArrayList;
import java.util.Arrays;
@ -78,6 +84,9 @@ public class AlertPluginInstanceServiceTest {
@Mock
private AlertGroupMapper alertGroupMapper;
@Mock
private RegistryClient registryClient;
private List<AlertPluginInstance> alertPluginInstances;
private User user;
@ -190,6 +199,26 @@ public class AlertPluginInstanceServiceTest {
Assertions.assertNotNull(result.get(Constants.DATA_LIST));
}
@Test
public void testSendAlert() {
Result<Void> result;
Mockito.when(registryClient.getServerList(RegistryNodeType.ALERT_SERVER)).thenReturn(new ArrayList<>());
result = alertPluginInstanceService.testSend(1, uiParams);
Assertions.assertEquals(Status.ALERT_SERVER_NOT_EXIST.getCode(), result.getCode());
AlertSendResponse.AlertSendResponseResult alertResult = new AlertSendResponse.AlertSendResponseResult();
alertResult.setSuccess(true);
AlertTestSendRequest alertTestSendRequest = new AlertTestSendRequest(
1,
uiParams);
Server server = new Server();
server.setPort(50052);
server.setHost("127.0.0.1");
Mockito.when(registryClient.getServerList(RegistryNodeType.ALERT_SERVER))
.thenReturn(Collections.singletonList(server));
result = alertPluginInstanceService.testSend(1, uiParams);
Assertions.assertEquals(Status.ALERT_TEST_SENDING_FAILED.getCode(), result.getCode());
}
@Test
public void testDelete() {
List<String> ids = Arrays.asList("11,2,3", "5,96", null, "98,1");

4
dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/IAlertOperator.java

@ -19,6 +19,7 @@ package org.apache.dolphinscheduler.extract.alert;
import org.apache.dolphinscheduler.extract.alert.request.AlertSendRequest;
import org.apache.dolphinscheduler.extract.alert.request.AlertSendResponse;
import org.apache.dolphinscheduler.extract.alert.request.AlertTestSendRequest;
import org.apache.dolphinscheduler.extract.base.RpcMethod;
import org.apache.dolphinscheduler.extract.base.RpcService;
@ -28,4 +29,7 @@ public interface IAlertOperator {
@RpcMethod
AlertSendResponse sendAlert(AlertSendRequest alertSendRequest);
@RpcMethod
AlertSendResponse sendTestAlert(AlertTestSendRequest alertSendRequest);
}

32
dolphinscheduler-extract/dolphinscheduler-extract-alert/src/main/java/org/apache/dolphinscheduler/extract/alert/request/AlertTestSendRequest.java

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.extract.alert.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AlertTestSendRequest {
private int pluginDefineId;
private String pluginInstanceParams;
}

4
dolphinscheduler-registry/dolphinscheduler-registry-api/pom.xml

@ -33,5 +33,9 @@
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-common</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-extract-base</artifactId>
</dependency>
</dependencies>
</project>

1
dolphinscheduler-registry/dolphinscheduler-registry-api/src/main/java/org/apache/dolphinscheduler/registry/api/RegistryClient.java

@ -238,5 +238,4 @@ public class RegistryClient {
private Collection<String> getServerNodes(RegistryNodeType nodeType) {
return getChildrenKeys(nodeType.getRegistryPath());
}
}

4
dolphinscheduler-ui/src/locales/en_US/security.ts

@ -168,7 +168,8 @@ export default {
user_password: 'Password',
user_password_tips:
'Please enter a password containing letters and numbers with a length between 6 and 20',
confirm_password_tips: 'The both of password and confirm password are not same.',
confirm_password_tips:
'The both of password and confirm password are not same.',
user_type: 'User Type',
ordinary_user: 'Ordinary users',
administrator: 'Administrator',
@ -216,6 +217,7 @@ export default {
confirm: 'Confirm',
cancel: 'Cancel',
submit: 'Submit',
test_send: 'Test Send',
create_alarm_instance: 'Create Alarm Instance',
select_plugin: 'Select plugin',
select_plugin_tips: 'Select Alarm plugin',

1
dolphinscheduler-ui/src/locales/zh_CN/security.ts

@ -213,6 +213,7 @@ export default {
confirm: '确定',
cancel: '取消',
submit: '提交',
test_send: '测试发送',
create_alarm_instance: '创建告警实例',
select_plugin: '选择插件',
select_plugin_tips: '请选择告警插件',

11
dolphinscheduler-ui/src/service/modules/alert-plugin/index.ts

@ -21,7 +21,8 @@ import {
PluginInstanceReq,
InstanceNameReq,
IdReq,
UpdatePluginInstanceReq
UpdatePluginInstanceReq,
TestPluginInstanceReq
} from './types'
export function queryAlertPluginInstanceListPaging(params: ListReq): any {
@ -40,6 +41,14 @@ export function createAlertPluginInstance(data: PluginInstanceReq): any {
})
}
export function testAlertPluginInstance(data: TestPluginInstanceReq): any {
return axios({
url: '/alert-plugin-instances/test-send',
method: 'post',
data
})
}
export function verifyAlertInstanceName(params: InstanceNameReq): any {
return axios({
url: '/alert-plugin-instances/verify-name',

8
dolphinscheduler-ui/src/service/modules/alert-plugin/types.ts

@ -29,6 +29,11 @@ interface PluginInstanceReq {
pluginInstanceParams: string
}
interface TestPluginInstanceReq {
pluginDefineId: number
pluginInstanceParams: string
}
interface InstanceNameReq {
alertInstanceName: string
}
@ -58,5 +63,6 @@ export {
InstanceNameReq,
IdReq,
UpdatePluginInstanceReq,
AlertPluginItem
AlertPluginItem,
TestPluginInstanceReq
}

66
dolphinscheduler-ui/src/views/security/alarm-instance-manage/detail.tsx

@ -23,7 +23,15 @@ import {
ref,
getCurrentInstance
} from 'vue'
import { NSelect, NInput, NSwitch, NRadioGroup, NSpace, NRadio } from 'naive-ui'
import {
NSelect,
NInput,
NSwitch,
NRadioGroup,
NSpace,
NRadio,
NButton
} from 'naive-ui'
import { isFunction } from 'lodash'
import { useI18n } from 'vue-i18n'
import { useForm } from './use-form'
@ -69,7 +77,7 @@ const DetailModal = defineComponent({
changePlugin
} = useForm()
const { status, createOrUpdate } = useDetail(getFormValues)
const { status, createOrUpdate, testSend } = useDetail(getFormValues)
const onCancel = () => {
resetForm()
@ -86,6 +94,11 @@ const DetailModal = defineComponent({
ctx.emit('update')
}
}
const onTest = async () => {
await state.detailFormRef.validate()
testSend(state.json)
}
const onChangePlugin = changePlugin
const trim = getCurrentInstance()?.appContext.config.globalProperties.trim
@ -98,7 +111,9 @@ const DetailModal = defineComponent({
)
watch(
() => state.detailForm.instanceType,
() => warningTypeSpan.value = state.detailForm.instanceType === 'GLOBAL' ? 0 : 24
() =>
(warningTypeSpan.value =
state.detailForm.instanceType === 'GLOBAL' ? 0 : 24)
)
watch(
() => state.json,
@ -131,6 +146,7 @@ const DetailModal = defineComponent({
elements,
onChangePlugin,
onSubmit,
onTest,
onCancel,
trim
}
@ -150,7 +166,9 @@ const DetailModal = defineComponent({
saving,
onChangePlugin,
onCancel,
onSubmit
onSubmit,
onTest,
testing
} = this
const { currentRecord } = props
return (
@ -195,11 +213,11 @@ const DetailModal = defineComponent({
label: t('security.alarm_instance.is_global_instance'),
widget: (
<NSwitch
checkedValue={'GLOBAL'}
uncheckedValue={'NORMAL'}
disabled={!!currentRecord?.id}
v-model:value={detailForm.instanceType}
/>
checkedValue={'GLOBAL'}
uncheckedValue={'NORMAL'}
disabled={!!currentRecord?.id}
v-model:value={detailForm.instanceType}
/>
)
},
{
@ -208,18 +226,12 @@ const DetailModal = defineComponent({
span: warningTypeSpan,
widget: (
<NRadioGroup v-model:value={detailForm.warningType}>
<NSpace>
<NRadio value={'SUCCESS'}>
{"success"}
</NRadio>
<NRadio value={'FAILURE'} >
{"failure"}
</NRadio>
<NRadio value={'ALL'} >
{"all"}
</NRadio>
</NSpace>
</NRadioGroup>
<NSpace>
<NRadio value={'SUCCESS'}>{'success'}</NRadio>
<NRadio value={'FAILURE'}>{'failure'}</NRadio>
<NRadio value={'ALL'}>{'all'}</NRadio>
</NSpace>
</NRadioGroup>
)
},
{
@ -244,6 +256,18 @@ const DetailModal = defineComponent({
cols: 24
}}
/>
),
'btn-middle': () => (
<NButton
class='btn-test-send'
type='primary'
size='small'
onClick={onTest}
loading={testing || loading}
>
{t('security.alarm_instance.test_send')}
</NButton>
)
}}
</Modal>

27
dolphinscheduler-ui/src/views/security/alarm-instance-manage/use-detail.ts

@ -19,14 +19,19 @@ import { reactive } from 'vue'
import { isFunction } from 'lodash'
import {
createAlertPluginInstance,
testAlertPluginInstance,
updateAlertPluginInstance,
verifyAlertInstanceName
} from '@/service/modules/alert-plugin'
import type { IJsonItem, IRecord } from './types'
import { useI18n } from 'vue-i18n'
export function useDetail(getFormValues: Function) {
const { t } = useI18n()
const status = reactive({
saving: false,
testing: false,
loading: false
})
@ -41,6 +46,26 @@ export function useDetail(getFormValues: Function) {
return JSON.stringify(json)
}
const testSend = async (json?: IJsonItem[]) => {
const values = getFormValues()
if (status.testing) return
status.testing = true
try {
const res = await testAlertPluginInstance({
pluginDefineId: values.pluginDefineId,
pluginInstanceParams: formatParams(json, values)
})
window.$message.success(
res
? res.msg
: `${t('security.alarm_instance.test_send')} ${t('home.success')}`
)
} finally {
status.testing = false
}
}
const createOrUpdate = async (currentRecord: IRecord, json?: IJsonItem[]) => {
const values = getFormValues()
if (status.saving) return false
@ -79,5 +104,5 @@ export function useDetail(getFormValues: Function) {
}
}
return { status, createOrUpdate }
return { status, createOrUpdate, testSend }
}

Loading…
Cancel
Save