From d838277f4b90da9e1032468240b2c3a8ab21223c Mon Sep 17 00:00:00 2001 From: Sherlock Yin Date: Mon, 17 Mar 2025 20:49:17 +0800 Subject: [PATCH] Add HTTP headers configuration for webhook (#13106) Co-authored-by: yinyijun --- docs/en/changes/changes.md | 1 + docs/en/setup/backend/backend-alarm.md | 19 ++++++++ .../core/alarm/provider/RulesReader.java | 3 +- .../provider/webhook/WebhookCallback.java | 2 +- .../provider/webhook/WebhookSettings.java | 4 ++ .../core/alarm/provider/RulesReaderTest.java | 4 ++ .../provider/webhook/WebhookCallbackTest.java | 44 +++++++++++++------ .../src/test/resources/alarm-settings.yml | 6 +++ .../src/main/resources/alarm-settings.yml | 16 +++++++ 9 files changed, 84 insertions(+), 15 deletions(-) diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md index 503f193ca9..eadee42fa8 100644 --- a/docs/en/changes/changes.md +++ b/docs/en/changes/changes.md @@ -84,6 +84,7 @@ * Bump Armeria to 1.32.0 and some transitive dependencies. * Skip persisting metrics/record data that have been expired. * Fix the issue of missing Last Ping data. +* Add HTTP headers configuration for the alarm webhook. #### UI diff --git a/docs/en/setup/backend/backend-alarm.md b/docs/en/setup/backend/backend-alarm.md index 5347da270a..1ef45343ff 100644 --- a/docs/en/setup/backend/backend-alarm.md +++ b/docs/en/setup/backend/backend-alarm.md @@ -189,6 +189,25 @@ webhook: urls: - http://ip:port/xxx - http://ip:port/yyy + custom1: + urls: + - http://127.0.0.1/custom1 + # headers config is provided to add custom configurations or authentications that are required from the server side. + headers: + Authorization: Bearer bearer_token + custom2: + urls: + - http://127.0.0.1/custom2 + # headers config is provided to add custom configurations or authentications that are required from the server + headers: + Authorization: Basic basic_token + custom3: + urls: + - http://127.0.0.1/internal-hook + # headers config is provided to add custom configurations or authentications that are required from the server + headers: + x-company-token: whatever-token-defined-internally-within-the-company + x-company-header: arbitrary-additional-http-headers ``` The JSON format is based on `List` with the following key information: 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 fde8422c91..b48c94b1c2 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 @@ -157,11 +157,12 @@ public class RulesReader { Map config = (Map) v; WebhookSettings settings = new WebhookSettings( k.toString(), AlarmHooksType.webhook, (Boolean) config.getOrDefault("is-default", false)); - List urls = (List) config.get("urls"); if (urls != null) { settings.getUrls().addAll(urls); } + Map headers = (Map) config.getOrDefault("headers", new HashMap<>()); + settings.setHeaders(headers); rules.getWebhookSettingsMap().put(settings.getFormattedName(), settings); if (settings.isDefault()) { this.defaultHooks.add(settings.getFormattedName()); diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookCallback.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookCallback.java index e41d6296fe..0fd0dbe304 100644 --- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookCallback.java +++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookCallback.java @@ -56,7 +56,7 @@ public class WebhookCallback extends HttpAlarmCallback { } for (final var url : setting.getUrls()) { try { - post(URI.create(url), gson.toJson(messages), Map.of()); + post(URI.create(url), gson.toJson(messages), setting.getHeaders()); } catch (Exception e) { log.error("Failed to send alarm message to Webhook: {}", url, e); } diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookSettings.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookSettings.java index 4daa76075f..813bbf4c57 100644 --- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookSettings.java +++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookSettings.java @@ -19,7 +19,10 @@ package org.apache.skywalking.oap.server.core.alarm.provider.webhook; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; + import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -31,6 +34,7 @@ import org.apache.skywalking.oap.server.core.alarm.provider.AlarmHooksType; @ToString public class WebhookSettings extends AlarmHookSettings { private List urls = new ArrayList<>(); + private Map headers = new HashMap<>(); public WebhookSettings(final String name, final AlarmHooksType type, diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java index 2d017c6401..9a5f2aba39 100644 --- a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java +++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java @@ -74,6 +74,10 @@ public class RulesReaderTest { WebhookSettings rulesWebhooks = rules.getWebhookSettingsMap().get(AlarmHooksType.webhook.name() + ".default"); Assertions.assertEquals(2, rulesWebhooks.getUrls().size()); Assertions.assertEquals("http://127.0.0.1/go-wechat/", rulesWebhooks.getUrls().get(1)); + WebhookSettings rulesWebhooks2 = rules.getWebhookSettingsMap().get(AlarmHooksType.webhook.name() + ".custom1"); + Assertions.assertEquals(2, rulesWebhooks2.getHeaders().size()); + Assertions.assertEquals("Bearer bearer_token", rulesWebhooks2.getHeaders().get("Authorization")); + Assertions.assertEquals("arbitrary-additional-http-headers", rulesWebhooks2.getHeaders().get("x-company-header")); GRPCAlarmSetting grpcAlarmSetting = rules.getGrpcAlarmSettingMap().get(AlarmHooksType.gRPC.name() + ".default"); assertNotNull(grpcAlarmSetting); diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookCallbackTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookCallbackTest.java index 3c85f35bc2..a9d40e2a25 100644 --- a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookCallbackTest.java +++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/webhook/WebhookCallbackTest.java @@ -18,13 +18,13 @@ package org.apache.skywalking.oap.server.core.alarm.provider.webhook; +import com.google.common.reflect.TypeToken; import com.google.gson.Gson; -import com.google.gson.JsonArray; import com.linecorp.armeria.common.HttpResponse; import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.common.RequestHeaders; import com.linecorp.armeria.server.ServerBuilder; import com.linecorp.armeria.testing.junit5.server.ServerExtension; -import java.util.concurrent.atomic.AtomicInteger; import org.apache.skywalking.oap.server.core.alarm.AlarmMessage; import org.apache.skywalking.oap.server.core.alarm.provider.AlarmHooksType; import org.apache.skywalking.oap.server.core.alarm.provider.AlarmRulesWatcher; @@ -33,9 +33,13 @@ import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.testcontainers.shaded.com.google.common.collect.ImmutableMap; + import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; public class WebhookCallbackTest { private static final AtomicBoolean IS_SUCCESS = new AtomicBoolean(); @@ -45,19 +49,30 @@ public class WebhookCallbackTest { public static final ServerExtension SERVER = new ServerExtension() { @Override protected void configure(ServerBuilder sb) { - sb.service("/webhook/receiveAlarm", (ctx, req) -> HttpResponse.from( - req.aggregate().thenApply(r -> { - final String content = r.content().toStringUtf8(); - final JsonArray elements = new Gson().fromJson(content, JsonArray.class); - if (elements.size() == 1) { - COUNTER.getAndIncrement(); + sb.service("/webhook/receiveAlarm", (ctx, req) -> HttpResponse.from(req.aggregate().thenApply(r -> { + final String content = r.content().toStringUtf8(); + final RequestHeaders headers = r.headers(); + List alarmMessages = new Gson().fromJson(content, new TypeToken>() { + }.getType()); + if (alarmMessages.size() != 1) { + IS_SUCCESS.set(false); + return HttpResponse.of(HttpStatus.INTERNAL_SERVER_ERROR); + } + if (Objects.equals(alarmMessages.get(0).getId0(), "1")) { + IS_SUCCESS.set(true); + COUNTER.incrementAndGet(); + return HttpResponse.of(HttpStatus.OK); + } else if (Objects.equals(alarmMessages.get(0).getId0(), "2")) { + if (Objects.equals(headers.get("Authorization"), "Bearer bearer_token") + && Objects.equals(headers.get("x-company-header"), "arbitrary-additional-http-headers")) { IS_SUCCESS.set(true); + COUNTER.incrementAndGet(); return HttpResponse.of(HttpStatus.OK); } - - return HttpResponse.of(HttpStatus.INTERNAL_SERVER_ERROR); - })) - ); + } + IS_SUCCESS.set(false); + return HttpResponse.of(HttpStatus.INTERNAL_SERVER_ERROR); + }))); } }; @@ -72,16 +87,19 @@ public class WebhookCallbackTest { setting2.setUrls(remoteEndpoints); rules.getWebhookSettingsMap().put(setting1.getFormattedName(), setting1); rules.getWebhookSettingsMap().put(setting2.getFormattedName(), setting2); + setting2.setHeaders(ImmutableMap.of("Authorization", " Bearer bearer_token", "x-company-header", "arbitrary-additional-http-headers")); AlarmRulesWatcher alarmRulesWatcher = new AlarmRulesWatcher(rules, null, null); WebhookCallback webhookCallback = new WebhookCallback(alarmRulesWatcher); List alarmMessages = new ArrayList<>(2); AlarmMessage alarmMessage = new AlarmMessage(); + alarmMessage.setId0("1"); alarmMessage.setScopeId(DefaultScopeDefine.SERVICE); alarmMessage.setRuleName("service_resp_time_rule"); alarmMessage.setAlarmMessage("alarmMessage with [DefaultScopeDefine.All]"); alarmMessage.getHooks().add(setting1.getFormattedName()); alarmMessages.add(alarmMessage); AlarmMessage anotherAlarmMessage = new AlarmMessage(); + anotherAlarmMessage.setId0("2"); anotherAlarmMessage.setRuleName("service_resp_time_rule_2"); anotherAlarmMessage.setScopeId(DefaultScopeDefine.ENDPOINT); anotherAlarmMessage.setAlarmMessage("anotherAlarmMessage with [DefaultScopeDefine.Endpoint]"); @@ -92,4 +110,4 @@ public class WebhookCallbackTest { Assertions.assertTrue(IS_SUCCESS.get()); Assertions.assertEquals(2, COUNTER.get()); } -} +} \ No newline at end of file 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 f621021ea2..9b0bae54eb 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 @@ -66,6 +66,12 @@ hooks: urls: - http://127.0.0.1/notify/ - http://127.0.0.1/go-wechat/ + custom1: + urls: + - http://127.0.0.1/custom1 + headers: + Authorization: Bearer bearer_token + x-company-header: arbitrary-additional-http-headers gRPC: default: diff --git a/oap-server/server-starter/src/main/resources/alarm-settings.yml b/oap-server/server-starter/src/main/resources/alarm-settings.yml index 4d726c9326..b764564f7e 100755 --- a/oap-server/server-starter/src/main/resources/alarm-settings.yml +++ b/oap-server/server-starter/src/main/resources/alarm-settings.yml @@ -47,6 +47,22 @@ hooks: # urls: # - http://127.0.0.1/notify/ # - http://127.0.0.1/go-wechat/ +# custom1: +# urls: +# - http://127.0.0.1/custom1 +# headers: +# Authorization: Bearer bearer_token +# custom2: +# urls: +# - http://127.0.0.1/custom2 +# headers: +# Authorization: Basic basic_token +# custom3: +# urls: +# - http://127.0.0.1/internal-hook +# headers: +# x-company-token: whatever-token-defined-internally-within-the-company +# x-company-header: arbitrary-additional-http-headers # # gRPC: # default: