From 1aa5f2682d88145718f0ad5d893107cea26f46a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E4=BA=91=E5=B3=B0?= Date: Mon, 26 Apr 2021 00:01:19 +0800 Subject: [PATCH] feature:Support alarm to WeLink (#6794) --- CHANGES.md | 1 + docs/en/setup/backend/backend-alarm.md | 18 ++ .../alarm/provider/AlarmRulesWatcher.java | 4 + .../core/alarm/provider/NotifyHandler.java | 2 + .../oap/server/core/alarm/provider/Rules.java | 2 + .../core/alarm/provider/RulesReader.java | 30 +++ .../provider/welink/WeLinkHookCallback.java | 172 ++++++++++++++++++ .../alarm/provider/welink/WeLinkSettings.java | 60 ++++++ .../alarm/provider/AlarmRulesWatcherTest.java | 1 + .../welink/WeLinkHookCallbackTest.java | 154 ++++++++++++++++ .../src/test/resources/alarm-settings.yml | 13 ++ .../src/main/resources/alarm-settings.yml | 13 ++ 12 files changed, 470 insertions(+) create mode 100644 oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkHookCallback.java create mode 100644 oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkSettings.java create mode 100644 oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkHookCallbackTest.java diff --git a/CHANGES.md b/CHANGES.md index da04901984..282e97a330 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,7 @@ Release Notes. * Fix K8s monitoring the incorrect metrics calculate. * Loop alarm into event system. * Support alarm tags. +* Support WeLink as a channel of alarm notification. #### UI * Add logo for kong plugin. diff --git a/docs/en/setup/backend/backend-alarm.md b/docs/en/setup/backend/backend-alarm.md index 21535ac482..21ffb1937e 100644 --- a/docs/en/setup/backend/backend-alarm.md +++ b/docs/en/setup/backend/backend-alarm.md @@ -281,6 +281,24 @@ feishuHooks: secret: dummysecret ``` +## WeLink Hook +To do this you need to follow the [WeLink Webhooks guide](https://open.welink.huaweicloud.com/apiexplorer/#/apiexplorer?type=internal&method=POST&path=/welinkim/v1/im-service/chat/group-chat) and create new Webhooks. +The alarm message will send through HTTP post by `application/json` content type if you configured WeLink Webhooks as following: +```yml +welinkHooks: + textTemplate: "Apache SkyWalking Alarm: \n %s." + webhooks: + # you may find your own client_id and client_secret in your app, below are dummy, need to change. + - client_id: "dummy_client_id" + client_secret: dummy_secret_key + access_token_url: https://open.welink.huaweicloud.com/api/auth/v2/tickets + message_url: https://open.welink.huaweicloud.com/api/welinkim/v1/im-service/chat/group-chat + # if you send to multi group at a time, separate group_ids with commas, e.g. "123xx","456xx" + group_ids: "dummy_group_id" + # make a name you like for the robot, it will display in group + robot_name: robot +``` + ## Update the settings dynamically Since 6.5.0, the alarm settings can be updated dynamically at runtime by [Dynamic Configuration](dynamic-config.md), which will override the settings in `alarm-settings.yml`. 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 6884905fa6..6b1ea47d30 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 @@ -35,6 +35,7 @@ import org.apache.skywalking.oap.server.core.alarm.provider.feishu.FeishuSetting 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.apache.skywalking.oap.server.core.alarm.provider.welink.WeLinkSettings; import org.apache.skywalking.oap.server.library.module.ModuleProvider; /** @@ -139,4 +140,7 @@ public class AlarmRulesWatcher extends ConfigChangeWatcher { return this.rules.getFeishus(); } + public WeLinkSettings getWeLinkSettings() { + return this.rules.getWelinks(); + } } 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 69772aba1f..932a192955 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 @@ -36,6 +36,7 @@ import org.apache.skywalking.oap.server.core.alarm.provider.feishu.FeishuHookCal 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; +import org.apache.skywalking.oap.server.core.alarm.provider.welink.WeLinkHookCallback; import org.apache.skywalking.oap.server.core.analysis.IDManager; import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics; import org.apache.skywalking.oap.server.core.analysis.metrics.MetricsMetaInfo; @@ -170,6 +171,7 @@ public class NotifyHandler implements MetricsNotify { allCallbacks.add(new DingtalkHookCallback(alarmRulesWatcher)); allCallbacks.add(new FeishuHookCallback(alarmRulesWatcher)); allCallbacks.add(new EventHookCallback(this.manager)); + allCallbacks.add(new WeLinkHookCallback(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 1e93acd826..016d46f21c 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 @@ -28,6 +28,7 @@ import org.apache.skywalking.oap.server.core.alarm.provider.feishu.FeishuSetting 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.apache.skywalking.oap.server.core.alarm.provider.welink.WeLinkSettings; @Setter @Getter @@ -41,6 +42,7 @@ public class Rules { private List compositeRules; private DingtalkSettings dingtalks; private FeishuSettings feishus; + private WeLinkSettings welinks; 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 2bf138a0af..ff5d96fb6e 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 @@ -30,6 +30,7 @@ import org.apache.skywalking.oap.server.core.alarm.provider.feishu.FeishuSetting 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.apache.skywalking.oap.server.core.alarm.provider.welink.WeLinkSettings; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -64,6 +65,7 @@ public class RulesReader { readCompositeRuleConfig(rules); readDingtalkConfig(rules); readFeishuConfig(rules); + readWeLinkConfig(rules); } return rules; } @@ -251,4 +253,32 @@ public class RulesReader { rules.setFeishus(feishuSettings); } } + + /** + * Read WeLink hook config into {@link WeLinkSettings} + */ + private void readWeLinkConfig(Rules rules) { + Map welinkConfig = (Map) yamlData.get("welinkHooks"); + if (welinkConfig != null) { + WeLinkSettings welinkSettings = new WeLinkSettings(); + Object textTemplate = welinkConfig.getOrDefault("textTemplate", ""); + welinkSettings.setTextTemplate((String) textTemplate); + List> welinkWebHooks = (List>) welinkConfig.get("webhooks"); + if (welinkWebHooks != null) { + welinkWebHooks.forEach(welinkWebhook -> { + String clientId = (String) welinkWebhook.getOrDefault("client_id", ""); + String clientSecret = (String) welinkWebhook.getOrDefault("client_secret", ""); + String accessTokenUrl = (String) welinkWebhook.getOrDefault("access_token_url", ""); + String messageUrl = (String) welinkWebhook.getOrDefault("message_url", ""); + String groupIds = (String) welinkWebhook.getOrDefault("group_ids", ""); + String rebootName = (String) welinkWebhook.getOrDefault("robot_name", "reboot"); + welinkSettings.getWebhooks() + .add(new WeLinkSettings.WebHookUrl(clientId, clientSecret, accessTokenUrl, messageUrl, + rebootName, groupIds + )); + }); + } + rules.setWelinks(welinkSettings); + } + } } diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkHookCallback.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkHookCallback.java new file mode 100644 index 0000000000..14140f6c1e --- /dev/null +++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkHookCallback.java @@ -0,0 +1,172 @@ +/* + * 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.welink; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.netty.handler.codec.http.HttpHeaderValues; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +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.oap.server.core.alarm.AlarmCallback; +import org.apache.skywalking.oap.server.core.alarm.AlarmMessage; +import org.apache.skywalking.oap.server.core.alarm.provider.AlarmRulesWatcher; + +/** + * Use SkyWalking alarm WeLink webhook API. + */ +@Slf4j +public class WeLinkHookCallback 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 final AlarmRulesWatcher alarmRulesWatcher; + private final RequestConfig requestConfig; + + public WeLinkHookCallback(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 alarmMessages) { + if (this.alarmRulesWatcher.getWeLinkSettings() == null || this.alarmRulesWatcher.getWeLinkSettings() + .getWebhooks() + .isEmpty()) { + return; + } + WeLinkSettings welinkSettings = this.alarmRulesWatcher.getWeLinkSettings(); + welinkSettings.getWebhooks().forEach(webHookUrl -> { + String accessToken = getAccessToken(webHookUrl); + alarmMessages.forEach(alarmMessage -> { + String content = String.format( + Locale.US, + this.alarmRulesWatcher.getWeLinkSettings().getTextTemplate(), + alarmMessage.getAlarmMessage() + ); + sendAlarmMessage(webHookUrl, accessToken, content); + }); + }); + } + + /** + * Send alarm message to remote endpoint + */ + private void sendAlarmMessage(WeLinkSettings.WebHookUrl webHookUrl, String accessToken, String content) { + JsonObject appServiceInfo = new JsonObject(); + appServiceInfo.addProperty("app_service_id", "1"); + appServiceInfo.addProperty("app_service_name", webHookUrl.getRobotName()); + JsonArray groupIds = new JsonArray(); + Arrays.stream(webHookUrl.getGroupIds().split(",")).forEach(groupIds::add); + JsonObject body = new JsonObject(); + body.add("app_service_info", appServiceInfo); + body.addProperty("app_msg_id", UUID.randomUUID().toString()); + body.add("group_id", groupIds); + body.addProperty("content", String.format( + Locale.US, "0<imbody><imagelist/>" + + "<html><![CDATA[<DIV>%s</DIV>]]></html><content><![CDATA[%s]]></content></imbody>", + content, content + )); + body.addProperty("content_type", 0); + body.addProperty("client_app_id", "1"); + sendPostRequest( + webHookUrl.getMessageUrl(), Collections.singletonMap("x-wlk-Authorization", accessToken), body.toString()); + } + + /** + * Get access token from remote endpoint + */ + private String getAccessToken(WeLinkSettings.WebHookUrl webHookUrl) { + String accessTokenUrl = webHookUrl.getAccessTokenUrl(); + String clientId = webHookUrl.getClientId(); + String clientSecret = webHookUrl.getClientSecret(); + String response = sendPostRequest( + accessTokenUrl, Collections.emptyMap(), + String.format(Locale.US, "{\"client_id\":%s,\"client_secret\":%s}", clientId, clientSecret) + ); + Gson gson = new Gson(); + JsonObject responseJson = gson.fromJson(response, JsonObject.class); + return Optional.ofNullable(responseJson) + .map(r -> r.get("access_token")) + .map(JsonElement::getAsString) + .orElse(""); + } + + /** + * Post rest invoke + */ + private String sendPostRequest(String url, Map headers, String requestBody) { + String response = ""; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + 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()); + headers.forEach(post::setHeader); + StringEntity entity = new StringEntity(requestBody, ContentType.APPLICATION_JSON); + post.setEntity(entity); + try (CloseableHttpResponse httpResponse = httpClient.execute(post)) { + StatusLine statusLine = httpResponse.getStatusLine(); + if (statusLine != null) { + if (statusLine.getStatusCode() != HttpStatus.SC_OK) { + log.error("send to {} failure. Response code: {}, Response content: {}", url, + statusLine.getStatusCode(), + EntityUtils.toString(httpResponse.getEntity()) + ); + } else { + response = EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8); + } + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return response; + } +} diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkSettings.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkSettings.java new file mode 100644 index 0000000000..5861ba6adb --- /dev/null +++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkSettings.java @@ -0,0 +1,60 @@ +/* + * 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.welink; + +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; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Setter +@Getter +@ToString +public class WeLinkSettings { + + private String textTemplate; + @Builder.Default + private List webhooks = new ArrayList<>(); + + @AllArgsConstructor + @Setter + @Getter + @ToString + public static class WebHookUrl { + // The unique identity of the application, used for interface authentication to obtain access_token + private final String clientId; + // The application key is used for interface authentication to obtain access_token + private final String clientSecret; + // The url get access token + private final String accessTokenUrl; + // The url to send message + private final String messageUrl; + // Name display in group + private final String robotName; + // The groupIds message to send + private final String groupIds; + } +} 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 af5f29bebb..e10be74ae3 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 @@ -90,6 +90,7 @@ public class AlarmRulesWatcherTest { assertNotNull(alarmRulesWatcher.getDingtalkSettings()); assertNotNull(alarmRulesWatcher.getWechatSettings()); assertNotNull(alarmRulesWatcher.getSlackSettings()); + assertNotNull(alarmRulesWatcher.getWeLinkSettings()); } @Test diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkHookCallbackTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkHookCallbackTest.java new file mode 100644 index 0000000000..8a6e980cb9 --- /dev/null +++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/welink/WeLinkHookCallbackTest.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.welink; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +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 javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +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 static org.junit.Assert.assertTrue; + +public class WeLinkHookCallbackTest implements Servlet { + + private Server server; + private int port; + private volatile boolean isSuccess = false; + private int count; + + @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("/welinkhook"); + server.setHandler(servletContextHandler); + ServletHolder servletHolder = new ServletHolder(); + servletHolder.setServlet(this); + servletContextHandler.addServlet(servletHolder, "/api/auth/v2/tickets"); + servletContextHandler.addServlet(servletHolder, "/api/welinkim/v1/im-service/chat/group-chat"); + server.start(); + port = server.getURI().getPort(); + assertTrue(port > 0); + } + + @Test + public void testWeLinkDoAlarm() { + List webHooks = new ArrayList<>(); + webHooks.add(new WeLinkSettings.WebHookUrl("clientId", "clientSecret", + "http://127.0.0.1:" + port + "/welinkhook/api/auth/v2/tickets", + "http://127.0.0.1:" + port + "/welinkhook/api/welinkim/v1/im-service/chat/group-chat", + "rebootName", "1,2,3" + )); + Rules rules = new Rules(); + String template = "Apache SkyWalking Alarm: \n %s."; + rules.setWelinks(WeLinkSettings.builder().webhooks(webHooks).textTemplate(template).build()); + + AlarmRulesWatcher alarmRulesWatcher = new AlarmRulesWatcher(rules, null); + WeLinkHookCallback welinkHookCallback = new WeLinkHookCallback(alarmRulesWatcher); + List 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); + welinkHookCallback.doAlarm(alarmMessages); + Assert.assertTrue(isSuccess); + } + + @After + public void stop() throws Exception { + server.stop(); + } + + @Override + public void init(ServletConfig servletConfig) { + } + + @Override + public ServletConfig getServletConfig() { + return null; + } + + @Override + public void service(ServletRequest request, ServletResponse response) throws 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); + + if (count == 0) { + String clientId = jsonObject.get("client_id").getAsString(); + count = clientId == null ? count : count + 1; + ((HttpServletResponse) response).setStatus(200); + } else if (count >= 1) { + String appMsgId = jsonObject.get("app_msg_id").getAsString(); + count = appMsgId == null ? count : count + 1; + ((HttpServletResponse) response).setStatus(200); + } else { + ((HttpServletResponse) response).setStatus(500); + } + if (count == 2) { + isSuccess = true; + } + } + } + + @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 0b31ddc2a7..e337686320 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 @@ -121,3 +121,16 @@ feishuHooks: secret: dummysecret - url: https://open.feishu.cn/open-apis/bot/v2/hook/dummy_token2 secret: + +welinkHooks: + textTemplate: "Apache SkyWalking Alarm: \n %s." + webhooks: + # you may find your own client_id and client_secret in your app, below are dummy, need to change. + - client_id: "dummy_client_id" + client_secret: dummy_secret_key + access_token_url: https://open.welink.huaweicloud.com/api/auth/v2/tickets + message_url: https://open.welink.huaweicloud.com/api/welinkim/v1/im-service/chat/group-chat + # if you send to multi group at a time, separate group_ids with commas, e.g. "123xx","456xx" + group_ids: "dummy_group_id" + # make a name you like for the robot, it will display in group + robot_name: robot \ No newline at end of file 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 87ad2f53e2..0efbe267ce 100755 --- a/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml +++ b/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml @@ -97,3 +97,16 @@ feishuHooks: webhooks: # - url: https://open.feishu.cn/open-apis/bot/v2/hook/dummy_token # secret: dummysecret + +welinkHooks: + textTemplate: "Apache SkyWalking Alarm: \n %s." + webhooks: +# # you may find your own client_id and client_secret in your app, below are dummy, need to change. +# - client_id: "dummy_client_id" +# client_secret: dummy_secret_key +# access_token_url: https://open.welink.huaweicloud.com/api/auth/v2/tickets +# message_url: https://open.welink.huaweicloud.com/api/welinkim/v1/im-service/chat/group-chat +# # if you send to multi group at a time, separate group_ids with commas, e.g. "123xx","456xx" +# group_ids: "dummy_group_id" +# # make a name you like for the robot, it will display in group +# robot_name: robot -- GitLab