You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2020/10/05 12:16:41 UTC
[skywalking] branch master updated: Support Alarm to dingtalk
(#5628)
This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git
The following commit(s) were added to refs/heads/master by this push:
new 117afff Support Alarm to dingtalk (#5628)
117afff is described below
commit 117afff36fc2bbfafeeb9c02f584ef45539c12b5
Author: xbkaishui <xb...@126.com>
AuthorDate: Mon Oct 5 20:16:18 2020 +0800
Support Alarm to dingtalk (#5628)
* Add dingtalk webhook
Co-authored-by: 吴晟 Wu Sheng <wu...@foxmail.com>
---
docs/en/setup/backend/backend-alarm.md | 18 ++
.../core/alarm/provider/AlarmRulesWatcher.java | 6 +
.../server/core/alarm/provider/NotifyHandler.java | 2 +
.../oap/server/core/alarm/provider/Rules.java | 2 +
.../server/core/alarm/provider/RulesReader.java | 34 +++-
.../provider/dingtalk/DingtalkHookCallback.java | 154 +++++++++++++++++
.../{Rules.java => dingtalk/DingtalkSettings.java} | 39 +++--
.../core/alarm/provider/AlarmRulesWatcherTest.java | 3 +
...AlarmRuleInitTest.java => RulesReaderTest.java} | 47 +++++-
.../dingtalk/DingtalkHookCallbackTest.java | 184 +++++++++++++++++++++
.../src/test/resources/alarm-settings.yml | 36 ++++
.../src/main/resources/alarm-settings.yml | 12 ++
12 files changed, 510 insertions(+), 27 deletions(-)
diff --git a/docs/en/setup/backend/backend-alarm.md b/docs/en/setup/backend/backend-alarm.md
index 345d7cd..e3a730d 100644
--- a/docs/en/setup/backend/backend-alarm.md
+++ b/docs/en/setup/backend/backend-alarm.md
@@ -219,6 +219,24 @@ wechatHooks:
- https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dummy_key
```
+## Dingtalk Hook
+To do this you need to follow the [Dingtalk Webhooks guide](https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq/uKPlK) and create new Webhooks.
+For security issue, you can config optional secret for individual webhook url.
+The alarm message will send through HTTP post by `application/json` content type if you configured Dingtalk Webhooks as following:
+```yml
+dingtalkHooks:
+ textTemplate: |-
+ {
+ "msgtype": "text",
+ "text": {
+ "content": "Apache SkyWalking Alarm: \n %s."
+ }
+ }
+ webhooks:
+ - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token
+ secret: dummysecret
+```
+
## Update the settings dynamically
Since 6.5.0, the alarm settings can be updated dynamically at runtime by [Dynamic Configuration](dynamic-config.md),
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcher.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcher.java
index c312b45..af6364a 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcher.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcher.java
@@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.oap.server.configuration.api.ConfigChangeWatcher;
import org.apache.skywalking.oap.server.core.Const;
import org.apache.skywalking.oap.server.core.alarm.AlarmModule;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
import org.apache.skywalking.oap.server.core.alarm.provider.expression.Expression;
import org.apache.skywalking.oap.server.core.alarm.provider.expression.ExpressionContext;
import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
@@ -128,4 +129,9 @@ public class AlarmRulesWatcher extends ConfigChangeWatcher {
public WechatSettings getWechatSettings() {
return this.rules.getWecchats();
}
+
+ public DingtalkSettings getDingtalkSettings() {
+ return this.rules.getDingtalks();
+ }
+
}
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/NotifyHandler.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/NotifyHandler.java
index 2f95b5c..755bc08 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/NotifyHandler.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/NotifyHandler.java
@@ -31,6 +31,7 @@ import org.apache.skywalking.oap.server.core.alarm.MetaInAlarm;
import org.apache.skywalking.oap.server.core.alarm.MetricsNotify;
import org.apache.skywalking.oap.server.core.alarm.ServiceInstanceMetaInAlarm;
import org.apache.skywalking.oap.server.core.alarm.ServiceMetaInAlarm;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkHookCallback;
import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCCallback;
import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackhookCallback;
import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatHookCallback;
@@ -162,6 +163,7 @@ public class NotifyHandler implements MetricsNotify {
allCallbacks.add(new GRPCCallback(alarmRulesWatcher));
allCallbacks.add(new SlackhookCallback(alarmRulesWatcher));
allCallbacks.add(new WechatHookCallback(alarmRulesWatcher));
+ allCallbacks.add(new DingtalkHookCallback(alarmRulesWatcher));
core.start(allCallbacks);
}
}
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
index 04ba1b1..7831bd6 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
@@ -23,6 +23,7 @@ import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
@@ -37,6 +38,7 @@ public class Rules {
private SlackSettings slacks;
private WechatSettings wecchats;
private List<CompositeAlarmRule> compositeRules;
+ private DingtalkSettings dingtalks;
public Rules() {
this.rules = new ArrayList<>();
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java
index 9290f2f..350254e 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java
@@ -18,6 +18,12 @@
package org.apache.skywalking.oap.server.core.alarm.provider;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
+import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
+import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
+import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
+import org.yaml.snakeyaml.Yaml;
+
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
@@ -25,11 +31,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
-import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
-import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
-import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
-import org.yaml.snakeyaml.Yaml;
-
/**
* Rule Reader parses the given `alarm-settings.yml` config file, to the target {@link Rules}.
*/
@@ -59,10 +60,12 @@ public class RulesReader {
readSlackConfig(rules);
readWechatConfig(rules);
readCompositeRuleConfig(rules);
+ readDingtalkConfig(rules);
}
return rules;
}
+
/**
* Read rule config into {@link AlarmRule}
*/
@@ -203,4 +206,25 @@ public class RulesReader {
}
});
}
+
+ /**
+ * Read dingtalk hook config into {@link DingtalkSettings}
+ */
+ private void readDingtalkConfig(Rules rules) {
+ Map dingtalkConfig = (Map) yamlData.get("dingtalkHooks");
+ if (dingtalkConfig != null) {
+ DingtalkSettings dingtalkSettings = new DingtalkSettings();
+ Object textTemplate = dingtalkConfig.getOrDefault("textTemplate", "");
+ dingtalkSettings.setTextTemplate((String) textTemplate);
+ List<Map<String, Object>> wechatWebhooks = (List<Map<String, Object>>) dingtalkConfig.get("webhooks");
+ if (wechatWebhooks != null) {
+ wechatWebhooks.forEach(wechatWebhook -> {
+ Object secret = wechatWebhook.getOrDefault("secret", "");
+ Object url = wechatWebhook.getOrDefault("url", "");
+ dingtalkSettings.getWebhooks().add(new DingtalkSettings.WebHookUrl((String) secret, (String) url));
+ });
+ }
+ rules.setDingtalks(dingtalkSettings);
+ }
+ }
}
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallback.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallback.java
new file mode 100644
index 0000000..5acc63a
--- /dev/null
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallback.java
@@ -0,0 +1,154 @@
+/*
+ * 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.skywalking.oap.server.core.alarm.provider.dingtalk;
+
+import io.netty.handler.codec.http.HttpHeaderValues;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpStatus;
+import org.apache.http.StatusLine;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.skywalking.apm.util.StringUtil;
+import org.apache.skywalking.oap.server.core.alarm.AlarmCallback;
+import org.apache.skywalking.oap.server.core.alarm.AlarmMessage;
+import org.apache.skywalking.oap.server.core.alarm.provider.AlarmRulesWatcher;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+import java.util.List;
+
+/**
+ * Use SkyWalking alarm dingtalk webhook API.
+ */
+@Slf4j
+public class DingtalkHookCallback implements AlarmCallback {
+
+ private static final int HTTP_CONNECT_TIMEOUT = 1000;
+ private static final int HTTP_CONNECTION_REQUEST_TIMEOUT = 1000;
+ private static final int HTTP_SOCKET_TIMEOUT = 10000;
+ private AlarmRulesWatcher alarmRulesWatcher;
+ private RequestConfig requestConfig;
+
+ public DingtalkHookCallback(final AlarmRulesWatcher alarmRulesWatcher) {
+ this.alarmRulesWatcher = alarmRulesWatcher;
+ this.requestConfig = RequestConfig.custom()
+ .setConnectTimeout(HTTP_CONNECT_TIMEOUT)
+ .setConnectionRequestTimeout(HTTP_CONNECTION_REQUEST_TIMEOUT)
+ .setSocketTimeout(HTTP_SOCKET_TIMEOUT)
+ .build();
+ }
+
+ /**
+ * Send alarm message if the settings not empty
+ */
+ @Override
+ public void doAlarm(List<AlarmMessage> alarmMessages) {
+ if (this.alarmRulesWatcher.getDingtalkSettings() == null || this.alarmRulesWatcher.getDingtalkSettings().getWebhooks().isEmpty()) {
+ return;
+ }
+ CloseableHttpClient httpClient = HttpClients.custom().build();
+ try {
+ DingtalkSettings dingtalkSettings = this.alarmRulesWatcher.getDingtalkSettings();
+ dingtalkSettings.getWebhooks().forEach(webHookUrl -> {
+ alarmMessages.forEach(alarmMessage -> {
+ String url = getUrl(webHookUrl);
+ String requestBody = String.format(
+ this.alarmRulesWatcher.getDingtalkSettings().getTextTemplate(), alarmMessage.getAlarmMessage()
+ );
+ sendAlarmMessage(httpClient, url, requestBody);
+ });
+ });
+ } finally {
+ try {
+ httpClient.close();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Get webhook url, sign the url when secret is not empty.
+ */
+ private String getUrl(DingtalkSettings.WebHookUrl webHookUrl) {
+ if (StringUtil.isEmpty(webHookUrl.getSecret())) {
+ return webHookUrl.getUrl();
+ }
+ return getSignUrl(webHookUrl);
+ }
+
+ /**
+ * Sign webhook url using secret and timestamp
+ */
+ private String getSignUrl(DingtalkSettings.WebHookUrl webHookUrl) {
+ try {
+ Long timestamp = System.currentTimeMillis();
+ return String.format("%s×tamp=%s&sign=%s", webHookUrl.getUrl(), timestamp, sign(timestamp, webHookUrl.getSecret()));
+ } catch (NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeyException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Sign webhook url using HmacSHA256 algorithm
+ */
+ private String sign(final Long timestamp, String secret) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
+ String stringToSign = timestamp + "\n" + secret;
+ Mac mac = Mac.getInstance("HmacSHA256");
+ mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
+ byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
+ return URLEncoder.encode(new String(Base64.getEncoder().encode(signData)), StandardCharsets.UTF_8.name());
+ }
+
+ /**
+ * Send alarm message to remote endpoint
+ */
+ private void sendAlarmMessage(CloseableHttpClient httpClient, String url, String requestBody) {
+ try {
+ HttpPost post = new HttpPost(url);
+ post.setConfig(requestConfig);
+ post.setHeader(HttpHeaders.ACCEPT, HttpHeaderValues.APPLICATION_JSON.toString());
+ post.setHeader(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON.toString());
+ StringEntity entity = new StringEntity(requestBody, ContentType.APPLICATION_JSON);
+ post.setEntity(entity);
+ CloseableHttpResponse httpResponse = httpClient.execute(post);
+ StatusLine statusLine = httpResponse.getStatusLine();
+ if (statusLine != null && statusLine.getStatusCode() != HttpStatus.SC_OK) {
+ log.error("send dingtalk alarm to {} failure. Response code: {}, Response content: {}", url, statusLine.getStatusCode(),
+ EntityUtils.toString(httpResponse.getEntity()));
+ }
+ } catch (Throwable e) {
+ log.error("send dingtalk alarm to {} failure.", url, e);
+ }
+ }
+}
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkSettings.java
similarity index 57%
copy from oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
copy to oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkSettings.java
index 04ba1b1..ba1d750 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkSettings.java
@@ -16,31 +16,36 @@
*
*/
-package org.apache.skywalking.oap.server.core.alarm.provider;
+package org.apache.skywalking.oap.server.core.alarm.provider.dingtalk;
-import java.util.ArrayList;
-import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.Getter;
+import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
-import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
-import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
-import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
+import java.util.ArrayList;
+import java.util.List;
+
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
@Setter
@Getter
@ToString
-public class Rules {
- private List<AlarmRule> rules;
- private List<String> webhooks;
- private GRPCAlarmSetting grpchookSetting;
- private SlackSettings slacks;
- private WechatSettings wecchats;
- private List<CompositeAlarmRule> compositeRules;
+public class DingtalkSettings {
+
+ private String textTemplate;
+ @Builder.Default
+ private List<WebHookUrl> webhooks = new ArrayList<>();
- public Rules() {
- this.rules = new ArrayList<>();
- this.webhooks = new ArrayList<>();
- this.compositeRules = new ArrayList<>();
+ @AllArgsConstructor
+ @Setter
+ @Getter
+ @ToString
+ public static class WebHookUrl {
+ private final String secret;
+ private final String url;
}
}
diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcherTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcherTest.java
index c2ed807..e9277a6 100644
--- a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcherTest.java
+++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcherTest.java
@@ -82,6 +82,9 @@ public class AlarmRulesWatcherTest {
assertNotNull(alarmRulesWatcher.getGrpchookSetting());
assertEquals(9888, alarmRulesWatcher.getGrpchookSetting().getTargetPort());
assertEquals(2, alarmRulesWatcher.getRunningContext().size());
+ assertNotNull(alarmRulesWatcher.getDingtalkSettings());
+ assertNotNull(alarmRulesWatcher.getWechatSettings());
+ assertNotNull(alarmRulesWatcher.getSlackSettings());
}
@Test
diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleInitTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java
similarity index 50%
rename from oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleInitTest.java
rename to oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java
index 9541476..9a81990 100644
--- a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleInitTest.java
+++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java
@@ -18,16 +18,27 @@
package org.apache.skywalking.oap.server.core.alarm.provider;
-import java.util.List;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
+import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
+import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
+import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
import org.junit.Assert;
import org.junit.Test;
-public class AlarmRuleInitTest {
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.any;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+public class RulesReaderTest {
@Test
- public void testInit() {
+ public void testReadRules() {
RulesReader reader = new RulesReader(this.getClass()
- .getClassLoader()
- .getResourceAsStream("alarm-settings.yml"));
+ .getClassLoader()
+ .getResourceAsStream("alarm-settings.yml"));
Rules rules = reader.readRules();
List<AlarmRule> ruleList = rules.getRules();
@@ -46,8 +57,34 @@ public class AlarmRuleInitTest {
Assert.assertEquals(2, rulesWebhooks.size());
Assert.assertEquals("http://127.0.0.1/go-wechat/", rulesWebhooks.get(1));
+ GRPCAlarmSetting grpcAlarmSetting = rules.getGrpchookSetting();
+ assertNotNull(grpcAlarmSetting);
+ assertThat(grpcAlarmSetting.getTargetHost(), is("127.0.0.1"));
+ assertThat(grpcAlarmSetting.getTargetPort(), is(9888));
+
+ SlackSettings slackSettings = rules.getSlacks();
+ assertNotNull(slackSettings);
+ assertThat(slackSettings.getWebhooks().size(), is(1));
+ assertThat(slackSettings.getWebhooks().get(0), is("https://hooks.slack.com/services/x/y/zssss"));
+ assertThat(slackSettings.getTextTemplate(), any(String.class));
+
+ WechatSettings wechatSettings = rules.getWecchats();
+ assertNotNull(wechatSettings);
+ assertThat(wechatSettings.getWebhooks().size(), is(1));
+ assertThat(wechatSettings.getWebhooks().get(0), is("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dummy_key"));
+ assertThat(slackSettings.getTextTemplate(), any(String.class));
+
List<CompositeAlarmRule> compositeRules = rules.getCompositeRules();
Assert.assertEquals(1, compositeRules.size());
Assert.assertEquals("endpoint_percent_more_rule && endpoint_percent_rule", compositeRules.get(0).getExpression());
+
+ DingtalkSettings dingtalkSettings = rules.getDingtalks();
+ assertThat(dingtalkSettings.getTextTemplate(), any(String.class));
+ List<DingtalkSettings.WebHookUrl> webHookUrls = dingtalkSettings.getWebhooks();
+ assertThat(webHookUrls.size(), is(2));
+ assertThat(webHookUrls.get(0).getUrl(), is("https://oapi.dingtalk.com/robot/send?access_token=dummy_token"));
+ assertThat(webHookUrls.get(0).getSecret(), is("dummysecret"));
+ assertThat(webHookUrls.get(1).getUrl(), is("https://oapi.dingtalk.com/robot/send?access_token=dummy_token2"));
+ assertNull(webHookUrls.get(1).getSecret());
}
}
diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallbackTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallbackTest.java
new file mode 100644
index 0000000..e092ed9
--- /dev/null
+++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallbackTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.skywalking.oap.server.core.alarm.provider.dingtalk;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import org.apache.skywalking.apm.util.StringUtil;
+import org.apache.skywalking.oap.server.core.alarm.AlarmMessage;
+import org.apache.skywalking.oap.server.core.alarm.provider.AlarmRulesWatcher;
+import org.apache.skywalking.oap.server.core.alarm.provider.Rules;
+import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
+public class DingtalkHookCallbackTest implements Servlet {
+
+ private Server server;
+ private int port;
+ private volatile boolean isSuccess = false;
+ private int count;
+ private volatile boolean checkSign = false;
+ private final String secret = "dummy-secret";
+
+ @Before
+ public void init() throws Exception {
+ server = new Server(new InetSocketAddress("127.0.0.1", 0));
+ ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+ servletContextHandler.setContextPath("/dingtalkhook");
+ server.setHandler(servletContextHandler);
+ ServletHolder servletHolder = new ServletHolder();
+ servletHolder.setServlet(this);
+ servletContextHandler.addServlet(servletHolder, "/receiveAlarm");
+ server.start();
+ port = server.getURI().getPort();
+ assertTrue(port > 0);
+ }
+
+ @Test
+ public void testDingtalkWebhookWithoutSign() {
+ List<DingtalkSettings.WebHookUrl> webHooks = new ArrayList<>();
+ webHooks.add(new DingtalkSettings.WebHookUrl("", "http://127.0.0.1:" + port + "/dingtalkhook/receiveAlarm?token=dummy_token"));
+ Rules rules = new Rules();
+ String template = "{\"msgtype\":\"text\",\"text\":{\"content\":\"Skywaling alarm: %s\"}}";
+ rules.setDingtalks(DingtalkSettings.builder().webhooks(webHooks).textTemplate(template).build());
+
+ AlarmRulesWatcher alarmRulesWatcher = new AlarmRulesWatcher(rules, null);
+ DingtalkHookCallback dingtalkCallBack = new DingtalkHookCallback(alarmRulesWatcher);
+ List<AlarmMessage> alarmMessages = new ArrayList<>(2);
+ AlarmMessage alarmMessage = new AlarmMessage();
+ alarmMessage.setScopeId(DefaultScopeDefine.ALL);
+ alarmMessage.setRuleName("service_resp_time_rule");
+ alarmMessage.setAlarmMessage("alarmMessage with [DefaultScopeDefine.All]");
+ alarmMessages.add(alarmMessage);
+ AlarmMessage anotherAlarmMessage = new AlarmMessage();
+ anotherAlarmMessage.setRuleName("service_resp_time_rule_2");
+ anotherAlarmMessage.setScopeId(DefaultScopeDefine.ENDPOINT);
+ anotherAlarmMessage.setAlarmMessage("anotherAlarmMessage with [DefaultScopeDefine.Endpoint]");
+ alarmMessages.add(anotherAlarmMessage);
+ dingtalkCallBack.doAlarm(alarmMessages);
+ Assert.assertTrue(isSuccess);
+ }
+
+ @Test
+ public void testDingtalkWebhookWithSign() {
+ checkSign = true;
+ List<DingtalkSettings.WebHookUrl> webHooks = new ArrayList<>();
+ webHooks.add(new DingtalkSettings.WebHookUrl(secret, "http://127.0.0.1:" + port + "/dingtalkhook/receiveAlarm?token=dummy_token"));
+ Rules rules = new Rules();
+ String template = "{\"msgtype\":\"text\",\"text\":{\"content\":\"Skywaling alarm: %s\"}}";
+ rules.setDingtalks(DingtalkSettings.builder().webhooks(webHooks).textTemplate(template).build());
+
+ AlarmRulesWatcher alarmRulesWatcher = new AlarmRulesWatcher(rules, null);
+ DingtalkHookCallback dingtalkCallBack = new DingtalkHookCallback(alarmRulesWatcher);
+ List<AlarmMessage> alarmMessages = new ArrayList<>(2);
+ AlarmMessage alarmMessage = new AlarmMessage();
+ alarmMessage.setScopeId(DefaultScopeDefine.ALL);
+ alarmMessage.setRuleName("service_resp_time_rule");
+ alarmMessage.setAlarmMessage("alarmMessage with [DefaultScopeDefine.All]");
+ alarmMessages.add(alarmMessage);
+ AlarmMessage anotherAlarmMessage = new AlarmMessage();
+ anotherAlarmMessage.setRuleName("service_resp_time_rule_2");
+ anotherAlarmMessage.setScopeId(DefaultScopeDefine.ENDPOINT);
+ anotherAlarmMessage.setAlarmMessage("anotherAlarmMessage with [DefaultScopeDefine.Endpoint]");
+ alarmMessages.add(anotherAlarmMessage);
+ dingtalkCallBack.doAlarm(alarmMessages);
+ Assert.assertTrue(isSuccess);
+ }
+
+ @After
+ public void stop() throws Exception {
+ server.stop();
+ }
+
+ @Override
+ public void init(ServletConfig servletConfig) throws ServletException {
+ }
+
+ @Override
+ public ServletConfig getServletConfig() {
+ return null;
+ }
+
+ @Override
+ public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ if (httpServletRequest.getContentType().equals("application/json")) {
+ InputStream inputStream = request.getInputStream();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buffer = new byte[2048];
+ int readCntOnce;
+
+ while ((readCntOnce = inputStream.read(buffer)) >= 0) {
+ out.write(buffer, 0, readCntOnce);
+ }
+
+ JsonObject jsonObject = new Gson().fromJson(new String(out.toByteArray()), JsonObject.class);
+ String type = jsonObject.get("msgtype").getAsString();
+ if (checkSign) {
+ String timestamp = request.getParameter("timestamp");
+ String sign = request.getParameter("sign");
+ if (StringUtil.isEmpty(timestamp) || StringUtil.isEmpty(sign)) {
+ ((HttpServletResponse) response).setStatus(500);
+ return;
+ }
+ }
+ if (type.equalsIgnoreCase("text")) {
+ ((HttpServletResponse) response).setStatus(200);
+ count = count + 1;
+ if (count == 2) {
+ isSuccess = true;
+ }
+ return;
+ }
+
+ ((HttpServletResponse) response).setStatus(500);
+ }
+ }
+
+ @Override
+ public String getServletInfo() {
+ return null;
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
diff --git a/oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml b/oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml
index 4679bcc..9dd339f 100755
--- a/oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml
+++ b/oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml
@@ -71,3 +71,39 @@ gRPCHook:
target_host: 127.0.0.1
target_port: 9888
+slackHooks:
+ textTemplate: |-
+ {
+ "type": "section",
+ "text": {
+ "type": "mrkdwn",
+ "text": ":alarm_clock: *Apache Skywalking Alarm* \n **%s**."
+ }
+ }
+ webhooks:
+ - https://hooks.slack.com/services/x/y/zssss
+
+wechatHooks:
+ textTemplate: |-
+ {
+ "msgtype": "text",
+ "text": {
+ "content": "Apache SkyWalking Alarm: \n %s."
+ }
+ }
+ webhooks:
+ - https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dummy_key
+
+dingtalkHooks:
+ textTemplate: |-
+ {
+ "msgtype": "text",
+ "text": {
+ "content": "Apache SkyWalking Alarm: \n %s."
+ }
+ }
+ webhooks:
+ - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token
+ secret: dummysecret
+ - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token2
+ secret:
diff --git a/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml b/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml
index 00b7ae9..f78427d 100755
--- a/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml
+++ b/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml
@@ -69,3 +69,15 @@ wechatHooks:
}
webhooks:
# - https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dummy_key
+
+dingtalkHooks:
+ textTemplate: |-
+ {
+ "msgtype": "text",
+ "text": {
+ "content": "Apache SkyWalking Alarm: \n %s."
+ }
+ }
+ webhooks:
+# - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token
+# secret: dummysecret