From f0654f9d95ec2ab75ce47f550d2059ba56c7fdb5 Mon Sep 17 00:00:00 2001 From: Joe Harvey <51208233+jharvey10@users.noreply.github.com> Date: Wed, 19 Nov 2025 15:36:53 -0500 Subject: [PATCH 1/6] ci: sync publish workflow with main --- .github/workflows/publish-alloy-release.yml | 43 +++++++++------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/.github/workflows/publish-alloy-release.yml b/.github/workflows/publish-alloy-release.yml index 5ded5cb5f5..746aca8c8b 100644 --- a/.github/workflows/publish-alloy-release.yml +++ b/.github/workflows/publish-alloy-release.yml @@ -223,26 +223,6 @@ jobs: go-version-file: go.mod cache: false - - name: Download dist - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - with: - name: dist - path: dist - - # Overwrite the original Windows Alloy executable with the signed version - - name: Download signed Windows executable - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - with: - name: windows-executables-signed - path: . - - # Overwrite the original Windows Alloy installer with the signed version - - name: Download signed Windows installer - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - with: - name: windows-installer-signed - path: dist - # A GitHub App is used to create the release instead of github-actions so that submit-winget-manifest is triggered when the release is published - name: Get GitHub app secrets id: get-secrets @@ -269,10 +249,25 @@ jobs: token: ${{ steps.app-token.outputs.token }} persist-credentials: false - - name: Configure Git - run: | - git config user.name "$GITHUB_ACTOR" - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + - name: Download dist + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: dist + path: dist + + # Overwrite the original Windows Alloy executable with the signed version + - name: Download signed Windows executable + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: windows-executables-signed + path: . + + # Overwrite the original Windows Alloy installer with the signed version + - name: Download signed Windows installer + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: windows-installer-signed + path: dist - name: Publish run: | From 023ae4ecb1f7306192b9fb6374a9b6b775eb095d Mon Sep 17 00:00:00 2001 From: Paulin Todev Date: Fri, 21 Nov 2025 18:48:49 +0000 Subject: [PATCH 2/6] Vendor Alertmanager config (#4905) * Vendor Alertmanager config * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Makefile | 7 +- .../testdata/expected_1.yml | 9 +- .../testdata/expected_2.yml | 8 +- .../mimir/alerts/kubernetes/alerts.go | 8 +- .../mimir/alerts/kubernetes/events.go | 16 +- .../mimir/alerts/kubernetes/events_test.go | 38 +- internal/mimir/alertmanager/types.go | 515 ++++++++++++++++++ internal/mimir/client/alerts.go | 10 +- internal/mimir/client/alerts_test.go | 11 +- internal/mimir/client/client.go | 2 +- .../testdata/alertmanager/conf.good.yml | 35 +- .../testdata/alertmanager/response.good.yml | 177 +----- 12 files changed, 574 insertions(+), 262 deletions(-) create mode 100644 internal/mimir/alertmanager/types.go diff --git a/Makefile b/Makefile index fdba97cf94..b8f470a481 100644 --- a/Makefile +++ b/Makefile @@ -15,9 +15,10 @@ ## ## Targets for running tests: ## -## test Run tests -## lint Lint code -## integration-test Run integration tests +## test Run tests +## lint Lint code +## integration-test Run integration tests +## integration-test-k8s Run Kubernetes integration tests ## ## Targets for building binaries: ## diff --git a/internal/cmd/integration-tests-k8s/tests/mimir-alerts-kubernetes/testdata/expected_1.yml b/internal/cmd/integration-tests-k8s/tests/mimir-alerts-kubernetes/testdata/expected_1.yml index 3250aad9f4..5b32e9a83f 100644 --- a/internal/cmd/integration-tests-k8s/tests/mimir-alerts-kubernetes/testdata/expected_1.yml +++ b/internal/cmd/integration-tests-k8s/tests/mimir-alerts-kubernetes/testdata/expected_1.yml @@ -12,7 +12,6 @@ alertmanager_config: | smtp_require_tls: true route: receiver: "null" - continue: false routes: - receiver: testing/alertmgr-config1/null matchers: @@ -29,7 +28,6 @@ alertmanager_config: | - receiver: testing/alertmgr-config2/database-pager matchers: - service="webapp" - continue: false group_wait: 10s receivers: - name: "null" @@ -37,14 +35,9 @@ alertmanager_config: | - name: testing/alertmgr-config1/null - name: testing/alertmgr-config1/myamc webhook_configs: - - send_resolved: false + - url: http://test.url http_config: follow_redirects: true - enable_http2: true - url: http://test.url - url_file: "" - max_alerts: 0 - timeout: 0s - name: testing/alertmgr-config2/null - name: testing/alertmgr-config2/database-pager templates: diff --git a/internal/cmd/integration-tests-k8s/tests/mimir-alerts-kubernetes/testdata/expected_2.yml b/internal/cmd/integration-tests-k8s/tests/mimir-alerts-kubernetes/testdata/expected_2.yml index 088f6537d4..0b6890638a 100644 --- a/internal/cmd/integration-tests-k8s/tests/mimir-alerts-kubernetes/testdata/expected_2.yml +++ b/internal/cmd/integration-tests-k8s/tests/mimir-alerts-kubernetes/testdata/expected_2.yml @@ -12,7 +12,6 @@ alertmanager_config: | smtp_require_tls: true route: receiver: "null" - continue: false routes: - receiver: testing/alertmgr-config1/null matchers: @@ -27,13 +26,8 @@ alertmanager_config: | - name: testing/alertmgr-config1/null - name: testing/alertmgr-config1/myamc webhook_configs: - - send_resolved: false + - url: http://test.url http_config: follow_redirects: true - enable_http2: true - url: http://test.url - url_file: "" - max_alerts: 0 - timeout: 0s templates: - default_template diff --git a/internal/component/mimir/alerts/kubernetes/alerts.go b/internal/component/mimir/alerts/kubernetes/alerts.go index db028b9d43..fe2cffc46f 100644 --- a/internal/component/mimir/alerts/kubernetes/alerts.go +++ b/internal/component/mimir/alerts/kubernetes/alerts.go @@ -9,13 +9,12 @@ import ( "time" "github.com/go-kit/log" + alertmgr_cfg "github.com/grafana/alloy/internal/mimir/alertmanager" "github.com/grafana/dskit/backoff" - alertmgr_cfg "github.com/prometheus/alertmanager/config" coreListers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/util/workqueue" _ "k8s.io/component-base/metrics/prometheus/workqueue" controller "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/yaml" "github.com/grafana/alloy/internal/component" "github.com/grafana/alloy/internal/component/mimir/util" @@ -230,13 +229,12 @@ func (c *Component) Startup(ctx context.Context) error { return err } - var baseCfg alertmgr_cfg.Config - err = yaml.Unmarshal([]byte(c.args.GlobalConfig), &baseCfg) + baseCfg, err := alertmgr_cfg.Unmarshal([]byte(c.args.GlobalConfig)) if err != nil { return fmt.Errorf("failed to unmarshal global config: %w", err) } - c.eventProcessor = c.newEventProcessor(queue, informerStopChan, namespaceLister, cfgLister, baseCfg) + c.eventProcessor = c.newEventProcessor(queue, informerStopChan, namespaceLister, cfgLister, *baseCfg) go c.eventProcessor.run(ctx) return nil diff --git a/internal/component/mimir/alerts/kubernetes/events.go b/internal/component/mimir/alerts/kubernetes/events.go index 1d4bcf4648..3bcf528cad 100644 --- a/internal/component/mimir/alerts/kubernetes/events.go +++ b/internal/component/mimir/alerts/kubernetes/events.go @@ -8,6 +8,7 @@ import ( "github.com/blang/semver/v4" "github.com/go-kit/log" + alertmgr_cfg "github.com/grafana/alloy/internal/mimir/alertmanager" "github.com/grafana/dskit/instrument" "github.com/prometheus-operator/prometheus-operator/pkg/alertmanager" validation_v1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/alertmanager/validation/v1alpha1" @@ -15,13 +16,11 @@ import ( promv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" "github.com/prometheus-operator/prometheus-operator/pkg/assets" promListers_v1alpha "github.com/prometheus-operator/prometheus-operator/pkg/client/listers/monitoring/v1alpha1" - alertmgr_cfg "github.com/prometheus/alertmanager/config" "github.com/prometheus/client_golang/prometheus" "k8s.io/apimachinery/pkg/labels" go_k8s "k8s.io/client-go/kubernetes" coreListers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/util/workqueue" - "sigs.k8s.io/yaml" // Used for CRD compatibility instead of gopkg.in/yaml.v2 "github.com/grafana/alloy/internal/component/common/kubernetes" "github.com/grafana/alloy/internal/component/mimir/util" @@ -180,8 +179,12 @@ func (c *eventProcessor) provisionAlertmanagerConfiguration(ctx context.Context, cfgBuilder = alertmanager.NewConfigBuilder(slog.New(logging.NewSlogGoKitHandler(c.logger)), *version, store, &monitoringv1.Alertmanager{}) ) - convertedCfg := c.baseCfg.String() - err := cfgBuilder.InitializeFromRawConfiguration([]byte(convertedCfg)) + convertedCfg, err := c.baseCfg.String() + if err != nil { + return nil, err + } + + err = cfgBuilder.InitializeFromRawConfiguration([]byte(convertedCfg)) if err != nil { return nil, fmt.Errorf("failed to initialize from global AlertmangerConfig: %w", err) } @@ -195,13 +198,12 @@ func (c *eventProcessor) provisionAlertmanagerConfiguration(ctx context.Context, return nil, fmt.Errorf("failed to marshal configuration: %w", err) } - var res alertmgr_cfg.Config - err = yaml.Unmarshal(generatedConfig, &res) + res, err := alertmgr_cfg.Unmarshal(generatedConfig) if err != nil { return nil, fmt.Errorf("failed to unmarshal generated final configuration: %w", err) } - return &res, nil + return res, nil } func (e *eventProcessor) reconcileState(ctx context.Context) error { diff --git a/internal/component/mimir/alerts/kubernetes/events_test.go b/internal/component/mimir/alerts/kubernetes/events_test.go index c7b120f8ee..ce5da66856 100644 --- a/internal/component/mimir/alerts/kubernetes/events_test.go +++ b/internal/component/mimir/alerts/kubernetes/events_test.go @@ -10,10 +10,10 @@ import ( "sigs.k8s.io/yaml" "github.com/go-kit/log" + "github.com/grafana/alloy/internal/mimir/alertmanager" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" promListers_v1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/client/listers/monitoring/v1alpha1" - "github.com/prometheus/alertmanager/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -33,7 +33,7 @@ import ( type fakeMimirClient struct { alertMgrConfigsMut sync.RWMutex - alertMgrConfig config.Config + alertMgrConfig alertmanager.Config templateFiles map[string]string } @@ -43,7 +43,7 @@ func newFakeMimirClient() *fakeMimirClient { return &fakeMimirClient{} } -func (m *fakeMimirClient) CreateAlertmanagerConfigs(ctx context.Context, conf *config.Config, templateFiles map[string]string) error { +func (m *fakeMimirClient) CreateAlertmanagerConfigs(ctx context.Context, conf *alertmanager.Config, templateFiles map[string]string) error { m.alertMgrConfigsMut.Lock() defer m.alertMgrConfigsMut.Unlock() // These are just shallow copies, but it should be sufficient. @@ -52,19 +52,16 @@ func (m *fakeMimirClient) CreateAlertmanagerConfigs(ctx context.Context, conf *c return nil } -func (m *fakeMimirClient) getAlertmanagerConfig() config.Config { +func (m *fakeMimirClient) getAlertmanagerConfig() alertmanager.Config { m.alertMgrConfigsMut.RLock() defer m.alertMgrConfigsMut.RUnlock() return m.alertMgrConfig } -func convertToAlertmanagerType(t *testing.T, alertmanagerConf string) config.Config { - config.MarshalSecretValue = true - - var res config.Config - err := yaml.Unmarshal([]byte(alertmanagerConf), &res) +func convertToAlertmanagerType(t *testing.T, alertmanagerConf string) alertmanager.Config { + cfg, err := alertmanager.Unmarshal([]byte(alertmanagerConf)) assert.NoError(t, err) - return res + return *cfg } // createTestLoggerWithBuffer creates a logger that writes to a thread-safe buffer for testing @@ -94,7 +91,6 @@ global: smtp_require_tls: true route: receiver: "null" - continue: false receivers: - name: "null" templates: []` @@ -152,7 +148,6 @@ spec: smtp_require_tls: true route: receiver: "null" - continue: false routes: - receiver: mynamespace/alertmgr-config1/null matchers: @@ -166,14 +161,9 @@ receivers: - name: mynamespace/alertmgr-config1/null - name: mynamespace/alertmgr-config1/myamc webhook_configs: - - send_resolved: false - http_config: + - http_config: follow_redirects: true - enable_http2: true url: http://test.url - url_file: "" - max_alerts: 0 - timeout: 0s templates: []` final_amConf_1_and_2 := `global: @@ -185,7 +175,6 @@ templates: []` smtp_require_tls: true route: receiver: "null" - continue: false routes: - receiver: mynamespace/alertmgr-config1/null matchers: @@ -202,21 +191,15 @@ route: - receiver: mynamespace/alertmgr-config2/database-pager matchers: - "service=\"webapp\"" - continue: false group_wait: 10s receivers: - name: "null" - name: mynamespace/alertmgr-config1/null - name: mynamespace/alertmgr-config1/myamc webhook_configs: - - send_resolved: false - timeout: 0s - http_config: + - http_config: follow_redirects: true - enable_http2: true url: http://test.url - url_file: "" - max_alerts: 0 - name: mynamespace/alertmgr-config2/null - name: mynamespace/alertmgr-config2/database-pager templates: []` @@ -405,7 +388,8 @@ spec: // Wait for the configs to be added to mimir require.EventuallyWithT(t, func(c *assert.CollectT) { - actual := mimirClient.getAlertmanagerConfig().String() + actual, err := mimirClient.getAlertmanagerConfig().String() + require.NoError(c, err) require.YAMLEq(c, tt.want, actual, "want", tt.want, "actual", actual) }, 10*time.Second, 100*time.Millisecond) diff --git a/internal/mimir/alertmanager/types.go b/internal/mimir/alertmanager/types.go new file mode 100644 index 0000000000..72411a0d4a --- /dev/null +++ b/internal/mimir/alertmanager/types.go @@ -0,0 +1,515 @@ +// Copyright 2020 The prometheus-operator Authors +// +// Licensed 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. + +// This file was copied from: +// https://github.com/prometheus-operator/prometheus-operator/blob/v0.86.1/pkg/alertmanager/types.go +// Some fields were exported by renaming them to uppercase, and some functions were added. +// TODO: Look into importing it from the upstream repo? + +package alertmanager + +import ( + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/common/model" + "gopkg.in/yaml.v2" +) + +// Customization of Config type from alertmanager repo: +// https://github.com/prometheus/alertmanager/blob/main/config/config.go +// +// Custom global type to get around obfuscation of secret values when +// marshalling. See the following issue for details: +// https://github.com/prometheus/alertmanager/issues/1985 +type Config struct { + Global *GlobalConfig `yaml:"global,omitempty" json:"global,omitempty"` + Route *Route `yaml:"route,omitempty" json:"route,omitempty"` + InhibitRules []*InhibitRule `yaml:"inhibit_rules,omitempty" json:"inhibit_rules,omitempty"` + Receivers []*Receiver `yaml:"receivers,omitempty" json:"receivers,omitempty"` + MuteTimeIntervals []*TimeInterval `yaml:"mute_time_intervals,omitempty" json:"mute_time_intervals,omitempty"` + TimeIntervals []*TimeInterval `yaml:"time_intervals,omitempty" json:"time_intervals,omitempty"` + Templates []string `yaml:"templates" json:"templates"` +} + +func (c Config) String() (string, error) { + confBytes, err := yaml.Marshal(c) + if err != nil { + return "", err + } + return string(confBytes), nil +} + +// This is a standard Unmarshal function. +// The benefit of having it here is that it's guaranteed to use the correct "gopkg.in/yaml.v2" package. +func Unmarshal(data []byte) (*Config, error) { + var c Config + err := yaml.Unmarshal(data, &c) + if err != nil { + return nil, err + } + return &c, nil +} + +type GlobalConfig struct { + // ResolveTimeout is the time after which an alert is declared resolved + // if it has not been updated. + ResolveTimeout *model.Duration `yaml:"resolve_timeout,omitempty" json:"resolve_timeout,omitempty"` + + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"` + SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"` + SMTPSmarthost config.HostPort `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"` + SMTPAuthUsername string `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"` + SMTPAuthPassword string `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"` + SMTPAuthPasswordFile string `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"` + SMTPAuthSecret string `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"` + SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"` + SMTPRequireTLS *bool `yaml:"smtp_require_tls,omitempty" json:"smtp_require_tls,omitempty"` + SMTPTLSConfig *TLSConfig `yaml:"smtp_tls_config,omitempty" json:"smtp_tls_config,omitempty"` + SlackAPIURL *config.URL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"` + SlackAPIURLFile string `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"` + PagerdutyURL *config.URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"` + HipchatAPIURL *config.URL `yaml:"hipchat_api_url,omitempty" json:"hipchat_api_url,omitempty"` + HipchatAuthToken string `yaml:"hipchat_auth_token,omitempty" json:"hipchat_auth_token,omitempty"` + OpsGenieAPIURL *config.URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"` + OpsGenieAPIKey string `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"` + OpsGenieAPIKeyFile string `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"` + WeChatAPIURL *config.URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"` + WeChatAPISecret string `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"` + WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"` + VictorOpsAPIURL *config.URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"` + VictorOpsAPIKey string `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"` + VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"` + TelegramAPIURL *config.URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"` + WebexAPIURL *config.URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"` + JiraAPIURL *config.URL `yaml:"jira_api_url,omitempty" json:"jira_api_url,omitempty"` + RocketChatAPIURL *config.URL `yaml:"rocketchat_api_url,omitempty" json:"rocketchat_api_url,omitempty"` + RocketChatToken string `yaml:"rocketchat_token,omitempty" json:"rocketchat_token,omitempty"` + RocketChatTokenFile string `yaml:"rocketchat_token_file,omitempty" json:"rocketchat_token_file,omitempty"` + RocketChatTokenID string `yaml:"rocketchat_token_id,omitempty" json:"rocketchat_token_id,omitempty"` + RocketChatTokenIDFile string `yaml:"rocketchat_token_id_file,omitempty" json:"rocketchat_token_id_file,omitempty"` +} + +type Route struct { + Receiver string `yaml:"receiver,omitempty" json:"receiver,omitempty"` + GroupByStr []string `yaml:"group_by,omitempty" json:"group_by,omitempty"` + Match map[string]string `yaml:"match,omitempty" json:"match,omitempty"` + MatchRE map[string]string `yaml:"match_re,omitempty" json:"match_re,omitempty"` + Matchers []string `yaml:"matchers,omitempty" json:"matchers,omitempty"` + Continue bool `yaml:"continue,omitempty" json:"continue,omitempty"` + Routes []*Route `yaml:"routes,omitempty" json:"routes,omitempty"` + GroupWait string `yaml:"group_wait,omitempty" json:"group_wait,omitempty"` + GroupInterval string `yaml:"group_interval,omitempty" json:"group_interval,omitempty"` + RepeatInterval string `yaml:"repeat_interval,omitempty" json:"repeat_interval,omitempty"` + MuteTimeIntervals []string `yaml:"mute_time_intervals,omitempty" json:"mute_time_intervals,omitempty"` + ActiveTimeIntervals []string `yaml:"active_time_intervals,omitempty" json:"active_time_intervals,omitempty"` +} + +type InhibitRule struct { + TargetMatch map[string]string `yaml:"target_match,omitempty" json:"target_match,omitempty"` + TargetMatchRE map[string]string `yaml:"target_match_re,omitempty" json:"target_match_re,omitempty"` + TargetMatchers []string `yaml:"target_matchers,omitempty" json:"target_matchers,omitempty"` + SourceMatch map[string]string `yaml:"source_match,omitempty" json:"source_match,omitempty"` + SourceMatchRE map[string]string `yaml:"source_match_re,omitempty" json:"source_match_re,omitempty"` + SourceMatchers []string `yaml:"source_matchers,omitempty" json:"source_matchers,omitempty"` + Equal []string `yaml:"equal,omitempty" json:"equal,omitempty"` +} + +type Receiver struct { + Name string `yaml:"name" json:"name"` + OpsgenieConfigs []*OpsgenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` + PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` + SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` + WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` + WeChatConfigs []*WeChatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` + EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` + PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` + VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` + SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` + TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` + DiscordConfigs []*DiscordConfig `yaml:"discord_configs,omitempty"` + WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty"` + MSTeamsConfigs []*MSTeamsConfig `yaml:"msteams_configs,omitempty"` + MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty"` + JiraConfigs []*JiraConfig `yaml:"jira_configs,omitempty"` + RocketChatConfigs []*RocketChatConfig `yaml:"rocketchat_configs,omitempty"` +} + +type WebhookConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + URL string `yaml:"url,omitempty" json:"url,omitempty"` + URLFile string `yaml:"url_file,omitempty" json:"url_file,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + MaxAlerts int32 `yaml:"max_alerts,omitempty" json:"max_alerts,omitempty"` + Timeout *model.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"` +} + +type PagerdutyConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + ServiceKey string `yaml:"service_key,omitempty" json:"service_key,omitempty"` + ServiceKeyFile string `yaml:"service_key_file,omitempty" json:"service_key_file,omitempty"` + RoutingKey string `yaml:"routing_key,omitempty" json:"routing_key,omitempty"` + RoutingKeyFile string `yaml:"routing_key_file,omitempty" json:"routing_key_file,omitempty"` + URL string `yaml:"url,omitempty" json:"url,omitempty"` + Client string `yaml:"client,omitempty" json:"client,omitempty"` + ClientURL string `yaml:"client_url,omitempty" json:"client_url,omitempty"` + Description string `yaml:"description,omitempty" json:"description,omitempty"` + Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"` + Images []PagerdutyImage `yaml:"images,omitempty" json:"images,omitempty"` + Links []PagerdutyLink `yaml:"links,omitempty" json:"links,omitempty"` + Severity string `yaml:"severity,omitempty" json:"severity,omitempty"` + Class string `yaml:"class,omitempty" json:"class,omitempty"` + Component string `yaml:"component,omitempty" json:"component,omitempty"` + Group string `yaml:"group,omitempty" json:"group,omitempty"` + Source string `yaml:"source,omitempty" json:"source,omitempty"` +} + +type OpsgenieConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + APIKey string `yaml:"api_key,omitempty" json:"api_key,omitempty"` + APIKeyFile string `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"` + APIURL string `yaml:"api_url,omitempty" json:"api_url,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + Description string `yaml:"description,omitempty" json:"description,omitempty"` + Source string `yaml:"source,omitempty" json:"source,omitempty"` + Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"` + Responders []OpsgenieResponder `yaml:"responders,omitempty" json:"responders,omitempty"` + Tags string `yaml:"tags,omitempty" json:"tags,omitempty"` + Note string `yaml:"note,omitempty" json:"note,omitempty"` + Priority string `yaml:"priority,omitempty" json:"priority,omitempty"` + UpdateAlerts *bool `yaml:"update_alerts,omitempty" json:"update_alerts,omitempty"` + Entity string `yaml:"entity,omitempty" json:"entity,omitempty"` + Actions string `yaml:"actions,omitempty" json:"actions,omitempty"` +} + +type WeChatConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + APISecret string `yaml:"api_secret,omitempty" json:"api_secret,omitempty"` + APIURL string `yaml:"api_url,omitempty" json:"api_url,omitempty"` + CorpID string `yaml:"corp_id,omitempty" json:"corp_id,omitempty"` + AgentID string `yaml:"agent_id,omitempty" json:"agent_id,omitempty"` + ToUser string `yaml:"to_user,omitempty" json:"to_user,omitempty"` + ToParty string `yaml:"to_party,omitempty" json:"to_party,omitempty"` + ToTag string `yaml:"to_tag,omitempty" json:"to_tag,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + MessageType string `yaml:"message_type,omitempty" json:"message_type,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` +} + +type SlackConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + APIURL string `yaml:"api_url,omitempty" json:"api_url,omitempty"` + APIURLFile string `yaml:"api_url_file,omitempty" json:"api_url_file,omitempty"` + Channel string `yaml:"channel,omitempty" json:"channel,omitempty"` + Username string `yaml:"username,omitempty" json:"username,omitempty"` + Color string `yaml:"color,omitempty" json:"color,omitempty"` + Title string `yaml:"title,omitempty" json:"title,omitempty"` + TitleLink string `yaml:"title_link,omitempty" json:"title_link,omitempty"` + Pretext string `yaml:"pretext,omitempty" json:"pretext,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` + Fields []SlackField `yaml:"fields,omitempty" json:"fields,omitempty"` + ShortFields bool `yaml:"short_fields,omitempty" json:"short_fields,omitempty"` + Footer string `yaml:"footer,omitempty" json:"footer,omitempty"` + Fallback string `yaml:"fallback,omitempty" json:"fallback,omitempty"` + CallbackID string `yaml:"callback_id,omitempty" json:"callback_id,omitempty"` + IconEmoji string `yaml:"icon_emoji,omitempty" json:"icon_emoji,omitempty"` + IconURL string `yaml:"icon_url,omitempty" json:"icon_url,omitempty"` + ImageURL string `yaml:"image_url,omitempty" json:"image_url,omitempty"` + ThumbURL string `yaml:"thumb_url,omitempty" json:"thumb_url,omitempty"` + LinkNames bool `yaml:"link_names,omitempty" json:"link_names,omitempty"` + MrkdwnIn []string `yaml:"mrkdwn_in,omitempty" json:"mrkdwn_in,omitempty"` + Actions []SlackAction `yaml:"actions,omitempty" json:"actions,omitempty"` +} + +type HTTPClientConfig struct { + Authorization *Authorization `yaml:"authorization,omitempty"` + BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"` + OAuth2 *OAuth2 `yaml:"oauth2,omitempty"` + BearerToken string `yaml:"bearer_token,omitempty"` + BearerTokenFile string `yaml:"bearer_token_file,omitempty"` + TLSConfig *TLSConfig `yaml:"tls_config,omitempty"` + FollowRedirects *bool `yaml:"follow_redirects,omitempty"` + EnableHTTP2 *bool `yaml:"enable_http2,omitempty"` + + ProxyConfig `yaml:",inline"` +} + +type ProxyConfig struct { + ProxyURL string `yaml:"proxy_url,omitempty"` + NoProxy string `yaml:"no_proxy,omitempty"` + ProxyFromEnvironment bool `yaml:"proxy_from_environment,omitempty"` + ProxyConnectHeader map[string][]string `yaml:"proxy_connect_header,omitempty"` +} + +type TLSConfig struct { + CAFile string `yaml:"ca_file,omitempty"` + CertFile string `yaml:"cert_file,omitempty"` + KeyFile string `yaml:"key_file,omitempty"` + ServerName string `yaml:"server_name,omitempty"` + InsecureSkipVerify bool `yaml:"insecure_skip_verify"` + MinVersion string `yaml:"min_version,omitempty"` + MaxVersion string `yaml:"max_version,omitempty"` +} + +type Authorization struct { + Type string `yaml:"type,omitempty"` + Credentials string `yaml:"credentials,omitempty"` + CredentialsFile string `yaml:"credentials_file,omitempty"` +} + +type BasicAuth struct { + Username string `yaml:"username"` + Password string `yaml:"password,omitempty"` + PasswordFile string `yaml:"password_file,omitempty"` +} + +type OAuth2 struct { + ClientID string `yaml:"client_id"` + ClientSecret string `yaml:"client_secret"` + ClientSecretFile string `yaml:"client_secret_file,omitempty"` + Scopes []string `yaml:"scopes,omitempty"` + TokenURL string `yaml:"token_url"` + EndpointParams map[string]string `yaml:"endpoint_params,omitempty"` + ProxyConfig `yaml:",inline"` + + TLSConfig *TLSConfig `yaml:"tls_config,omitempty"` +} + +type PagerdutyLink struct { + Href string `yaml:"href,omitempty" json:"href,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` +} + +type PagerdutyImage struct { + Src string `yaml:"src,omitempty" json:"src,omitempty"` + Alt string `yaml:"alt,omitempty" json:"alt,omitempty"` + Href string `yaml:"href,omitempty" json:"href,omitempty"` +} + +type OpsgenieResponder struct { + ID string `yaml:"id,omitempty" json:"id,omitempty"` + Name string `yaml:"name,omitempty" json:"name,omitempty"` + Username string `yaml:"username,omitempty" json:"username,omitempty"` + Type string `yaml:"type,omitempty" json:"type,omitempty"` +} + +type SlackField struct { + Title string `yaml:"title,omitempty" json:"title,omitempty"` + Value string `yaml:"value,omitempty" json:"value,omitempty"` + Short bool `yaml:"short,omitempty" json:"short,omitempty"` +} + +type SlackAction struct { + Type string `yaml:"type,omitempty" json:"type,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` + URL string `yaml:"url,omitempty" json:"url,omitempty"` + Style string `yaml:"style,omitempty" json:"style,omitempty"` + Name string `yaml:"name,omitempty" json:"name,omitempty"` + Value string `yaml:"value,omitempty" json:"value,omitempty"` + ConfirmField *SlackConfirmationField `yaml:"confirm,omitempty" json:"confirm,omitempty"` +} + +type SlackConfirmationField struct { + Text string `yaml:"text,omitempty" json:"text,omitempty"` + Title string `yaml:"title,omitempty" json:"title,omitempty"` + OkText string `yaml:"ok_text,omitempty" json:"ok_text,omitempty"` + DismissText string `yaml:"dismiss_text,omitempty" json:"dismiss_text,omitempty"` +} + +type EmailConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + To string `yaml:"to,omitempty" json:"to,omitempty"` + From string `yaml:"from,omitempty" json:"from,omitempty"` + Hello string `yaml:"hello,omitempty" json:"hello,omitempty"` + Smarthost config.HostPort `yaml:"smarthost,omitempty" json:"smarthost,omitempty"` + AuthUsername string `yaml:"auth_username,omitempty" json:"auth_username,omitempty"` + AuthPassword string `yaml:"auth_password,omitempty" json:"auth_password,omitempty"` + AuthPasswordFile string `yaml:"auth_password_file,omitempty" json:"auth_password_file,omitempty"` + AuthSecret string `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"` + AuthIdentity string `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"` + Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"` + HTML *string `yaml:"html,omitempty" json:"html,omitempty"` + Text *string `yaml:"text,omitempty" json:"text,omitempty"` + RequireTLS *bool `yaml:"require_tls,omitempty" json:"require_tls,omitempty"` + TLSConfig *TLSConfig `yaml:"tls_config,omitempty" json:"tls_config,omitempty"` +} + +type PushoverConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + UserKey string `yaml:"user_key,omitempty" json:"user_key,omitempty"` + UserKeyFile string `yaml:"user_key_file,omitempty" json:"user_key_file,omitempty"` + Token string `yaml:"token,omitempty" json:"token,omitempty"` + TokenFile string `yaml:"token_file,omitempty" json:"token_file,omitempty"` + Title string `yaml:"title,omitempty" json:"title,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + URL string `yaml:"url,omitempty" json:"url,omitempty"` + URLTitle string `yaml:"url_title,omitempty" json:"url_title,omitempty"` + TTL string `yaml:"ttl,omitempty" json:"ttl,omitempty"` + Device string `yaml:"device,omitempty" json:"device,omitempty"` + Sound string `yaml:"sound,omitempty" json:"sound,omitempty"` + Priority string `yaml:"priority,omitempty" json:"priority,omitempty"` + Retry *model.Duration `yaml:"retry,omitempty" json:"retry,omitempty"` + Expire *model.Duration `yaml:"expire,omitempty" json:"expire,omitempty"` + HTML bool `yaml:"html,omitempty" json:"html,omitempty"` +} + +type SNSConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + APIUrl string `yaml:"api_url,omitempty" json:"api_url,omitempty"` + Sigv4 SigV4Config `yaml:"sigv4,omitempty" json:"sigv4,omitempty"` + TopicARN string `yaml:"topic_arn,omitempty" json:"topic_arn,omitempty"` + PhoneNumber string `yaml:"phone_number,omitempty" json:"phone_number,omitempty"` + TargetARN string `yaml:"target_arn,omitempty" json:"target_arn,omitempty"` + Subject string `yaml:"subject,omitempty" json:"subject,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + Attributes map[string]string `yaml:"attributes,omitempty" json:"attributes,omitempty"` +} + +type TelegramConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + APIUrl string `yaml:"api_url,omitempty" json:"api_url,omitempty"` + BotToken string `yaml:"bot_token,omitempty" json:"bot_token,omitempty"` + BotTokenFile string `yaml:"bot_token_file,omitempty" json:"bot_token_file,omitempty"` + ChatID int64 `yaml:"chat_id,omitempty" json:"chat_id,omitempty"` + MessageThreadID int `yaml:"message_thread_id,omitempty" json:"message_thread_id,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + DisableNotifications bool `yaml:"disable_notifications,omitempty" json:"disable_notifications,omitempty"` + ParseMode string `yaml:"parse_mode,omitempty" json:"parse_mode,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` +} + +type DiscordConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty"` + WebhookURL string `yaml:"webhook_url,omitempty"` + Title string `yaml:"title,omitempty"` + Message string `yaml:"message,omitempty"` + Content string `yaml:"content,omitempty"` + Username string `yaml:"username,omitempty"` + AvatarURL string `yaml:"avatar_url,omitempty"` +} + +type WebexConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty"` + APIURL string `yaml:"api_url,omitempty"` + Message string `yaml:"message,omitempty"` + RoomID string `yaml:"room_id"` +} + +type SigV4Config struct { + Region string `yaml:"region,omitempty" json:"region,omitempty"` + AccessKey string `yaml:"access_key,omitempty" json:"access_key,omitempty"` + SecretKey string `yaml:"secret_key,omitempty" json:"secret_key,omitempty"` + Profile string `yaml:"profile,omitempty" json:"profile,omitempty"` + RoleARN string `yaml:"role_arn,omitempty" json:"role_arn,omitempty"` +} + +type VictorOpsConfig struct { + VSendResolved *bool `yaml:"send_resolved,omitempty" json:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + APIKey string `yaml:"api_key,omitempty" json:"api_key,omitempty"` + APIKeyFile string `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"` + APIURL string `yaml:"api_url,omitempty" json:"api_url,omitempty"` + RoutingKey string `yaml:"routing_key,omitempty" json:"routing_key,omitempty"` + MessageType string `yaml:"message_type,omitempty" json:"message_type,omitempty"` + StateMessage string `yaml:"state_message,omitempty" json:"state_message,omitempty"` + EntityDisplayName string `yaml:"entity_display_name,omitempty" json:"entity_display_name,omitempty"` + MonitoringTool string `yaml:"monitoring_tool,omitempty" json:"monitoring_tool,omitempty"` + CustomFields map[string]string `yaml:"custom_fields,omitempty" json:"custom_fields,omitempty"` +} + +type MSTeamsConfig struct { + SendResolved *bool `yaml:"send_resolved,omitempty"` + WebhookURL string `yaml:"webhook_url"` + Title string `yaml:"title,omitempty"` + Summary string `yaml:"summary,omitempty"` + Text string `yaml:"text,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty"` +} + +type MSTeamsV2Config struct { + SendResolved *bool `yaml:"send_resolved,omitempty"` + WebhookURL string `yaml:"webhook_url,omitempty"` + WebhookURLFile string `yaml:"webhook_url_file,omitempty"` + Title string `yaml:"title,omitempty"` + Text string `yaml:"text,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty"` +} + +type JiraConfig struct { + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty"` + SendResolved *bool `yaml:"send_resolved,omitempty"` + APIURL string `yaml:"api_url,omitempty"` + Project string `yaml:"project,omitempty"` + Summary string `yaml:"summary,omitempty"` + Description string `yaml:"description,omitempty"` + Labels []string `yaml:"labels,omitempty"` + Priority string `yaml:"priority,omitempty"` + IssueType string `yaml:"issue_type,omitempty"` + ReopenTransition string `yaml:"reopen_transition,omitempty"` + ResolveTransition string `yaml:"resolve_transition,omitempty"` + WontFixResolution string `yaml:"wont_fix_resolution,omitempty"` + ReopenDuration model.Duration `yaml:"reopen_duration,omitempty"` + Fields map[string]any `yaml:"fields,omitempty"` +} + +type RocketchatAttachmentField struct { + Short *bool `yaml:"short"` + Title string `yaml:"title,omitempty"` + Value string `yaml:"value,omitempty"` +} + +type RocketchatAttachmentAction struct { + Type string `yaml:"type,omitempty"` + Text string `yaml:"text,omitempty"` + URL string `yaml:"url,omitempty"` + ImageURL string `yaml:"image_url,omitempty"` + IsWebView bool `yaml:"is_webview"` + WebviewHeightRatio string `yaml:"webview_height_ratio,omitempty"` + Msg string `yaml:"msg,omitempty"` + MsgInChatWindow bool `yaml:"msg_in_chat_window"` + MsgProcessingType string `yaml:"msg_processing_type,omitempty"` +} + +type RocketChatConfig struct { + SendResolved *bool `yaml:"send_resolved,omitempty"` + HTTPConfig *HTTPClientConfig `yaml:"http_config,omitempty"` + APIURL string `yaml:"api_url,omitempty"` + TokenID *string `yaml:"token_id,omitempty"` + TokenIDFile string `yaml:"token_id_file,omitempty"` + Token *string `yaml:"token,omitempty"` + TokenFile string `yaml:"token_file,omitempty"` + // RocketChat channel override, (like #other-channel or @username). + Channel string `yaml:"channel,omitempty"` + Color string `yaml:"color,omitempty"` + Title string `yaml:"title,omitempty"` + TitleLink string `yaml:"title_link,omitempty"` + Text string `yaml:"text,omitempty"` + Fields []*RocketchatAttachmentField `yaml:"fields,omitempty"` + ShortFields bool `yaml:"short_fields"` + Emoji string `yaml:"emoji,omitempty"` + IconURL string `yaml:"icon_url,omitempty"` + ImageURL string `yaml:"image_url,omitempty"` + ThumbURL string `yaml:"thumb_url,omitempty"` + LinkNames bool `yaml:"link_names"` + Actions []*RocketchatAttachmentAction `yaml:"actions,omitempty"` +} + +type TimeInterval config.TimeInterval diff --git a/internal/mimir/client/alerts.go b/internal/mimir/client/alerts.go index d54b3802d3..e9f7b33923 100644 --- a/internal/mimir/client/alerts.go +++ b/internal/mimir/client/alerts.go @@ -3,8 +3,8 @@ package client import ( "context" + alertmgr_cfg "github.com/grafana/alloy/internal/mimir/alertmanager" "github.com/grafana/alloy/internal/runtime/logging/level" - alertmgr_cfg "github.com/prometheus/alertmanager/config" "gopkg.in/yaml.v3" ) @@ -17,11 +17,13 @@ type configCompat struct { } func (r *MimirClient) CreateAlertmanagerConfigs(ctx context.Context, conf *alertmgr_cfg.Config, templateFiles map[string]string) error { - // If we don't set this, secrets will be obfuscated by being set to "". - alertmgr_cfg.MarshalSecretValue = true + confStr, err := conf.String() + if err != nil { + return err + } payload := configCompat{ - AlertmanagerConfig: conf.String(), + AlertmanagerConfig: confStr, TemplateFiles: templateFiles, } payloadStr, err := yaml.Marshal(&payload) diff --git a/internal/mimir/client/alerts_test.go b/internal/mimir/client/alerts_test.go index d6dc3599ac..7acc25073b 100644 --- a/internal/mimir/client/alerts_test.go +++ b/internal/mimir/client/alerts_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/go-kit/log" - alertmgr_cfg "github.com/prometheus/alertmanager/config" + alertmgr_cfg "github.com/grafana/alloy/internal/mimir/alertmanager" "github.com/stretchr/testify/require" ) @@ -36,9 +36,11 @@ func TestMimirClient_CreateAlertmanagerConfigs(t *testing.T) { // This Alertmanager config was copied from: // https://github.com/prometheus/alertmanager/blob/v0.28.1/config/testdata/conf.good.yml - config, err := alertmgr_cfg.LoadFile("testdata/alertmanager/conf.good.yml") + configBytes, err := os.ReadFile("testdata/alertmanager/conf.good.yml") + require.NoError(t, err) + + config, err := alertmgr_cfg.Unmarshal(configBytes) require.NoError(t, err) - require.NotNil(t, config) templateFiles := map[string]string{ "template1.tmpl": "{{ range .Alerts }}Alert: {{ .Summary }}{{ end }}", @@ -62,5 +64,6 @@ func TestMimirClient_CreateAlertmanagerConfigs(t *testing.T) { require.NoError(t, err) expectedResponse := string(expectedResponseBytes) - require.Equal(t, expectedResponse, string(body)) + actualResponse := string(body) + require.YAMLEq(t, expectedResponse, actualResponse) } diff --git a/internal/mimir/client/client.go b/internal/mimir/client/client.go index 1d901153a2..f131ff7e88 100644 --- a/internal/mimir/client/client.go +++ b/internal/mimir/client/client.go @@ -12,11 +12,11 @@ import ( "strings" "github.com/go-kit/log" + alertmgr_cfg "github.com/grafana/alloy/internal/mimir/alertmanager" "github.com/grafana/alloy/internal/mimir/client/internal" "github.com/grafana/alloy/internal/useragent" "github.com/grafana/dskit/instrument" "github.com/grafana/dskit/user" - alertmgr_cfg "github.com/prometheus/alertmanager/config" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/config" ) diff --git a/internal/mimir/client/testdata/alertmanager/conf.good.yml b/internal/mimir/client/testdata/alertmanager/conf.good.yml index 2429f7961c..f3bb211e1e 100644 --- a/internal/mimir/client/testdata/alertmanager/conf.good.yml +++ b/internal/mimir/client/testdata/alertmanager/conf.good.yml @@ -1,5 +1,4 @@ global: - # The smarthost and SMTP sender used for mail notifications. smtp_smarthost: 'localhost:25' smtp_from: 'alertmanager@example.org' smtp_auth_username: 'alertmanager' @@ -7,43 +6,21 @@ global: smtp_hello: "host.example.org" slack_api_url: "http://mysecret.example.com/" http_config: + follow_redirects: true + enable_http2: true proxy_url: 'http://127.0.0.1:1025' -# The directory from which notification templates are read. templates: - '/etc/alertmanager/template/*.tmpl' -# The root route on which each incoming alert enters. route: - # The labels by which incoming alerts are grouped together. For example, - # multiple alerts coming in for cluster=A and alertname=LatencyHigh would - # be batched into a single group. group_by: ['alertname', 'cluster', 'service'] - # When a new group of alerts is created by an incoming alert, wait at - # least 'group_wait' to send the initial notification. - # This way ensures that you get multiple alerts for the same group that start - # firing shortly after another are batched together on the first - # notification. group_wait: 30s - # When the first notification was sent, wait 'group_interval' to send a batch - # of new alerts that started firing for that group. group_interval: 5m - # If an alert has successfully been sent, wait 'repeat_interval' to - # resend them. repeat_interval: 3h - # A default receiver receiver: team-X-mails - # All the above attributes are inherited by all child routes and can - # overwritten on each. - - # The child route trees. routes: - # This routes performs a regular expression match on alert labels to - # catch alerts that are related to a list of services. - match_re: service: ^(foo1|foo2|baz)$ receiver: team-X-mails - # The service has a sub-route for critical alerts, any alerts - # that do not match, i.e. severity != critical, fall-back to the - # parent node and are sent to 'team-X-mails' routes: - match: severity: critical @@ -55,12 +32,9 @@ route: - match: severity: critical receiver: team-Y-pager - # This route handles all alerts coming from a database service. If there's - # no team to handle it, it defaults to the DB team. - match: service: database receiver: team-DB-pager - # Also group alerts by affected database. group_by: [alertname, cluster, database] routes: - match: @@ -71,16 +45,11 @@ route: owner: team-Y receiver: team-Y-pager # continue: true -# Inhibition rules allow to mute a set of alerts given that another alert is -# firing. -# We use this to mute any warning-level notifications if the same alert is -# already critical. inhibit_rules: - source_match: severity: 'critical' target_match: severity: 'warning' - # Apply inhibition if the alertname is the same. equal: ['alertname', 'cluster', 'service'] receivers: - name: 'team-X-mails' diff --git a/internal/mimir/client/testdata/alertmanager/response.good.yml b/internal/mimir/client/testdata/alertmanager/response.good.yml index 0c96af8d1f..791e70a2da 100644 --- a/internal/mimir/client/testdata/alertmanager/response.good.yml +++ b/internal/mimir/client/testdata/alertmanager/response.good.yml @@ -3,7 +3,6 @@ template_files: template2.tmpl: '{{ .CommonLabels.alertname }}' alertmanager_config: | global: - resolve_timeout: 5m http_config: follow_redirects: true enable_http2: true @@ -15,43 +14,28 @@ alertmanager_config: | smtp_auth_password: |- multiline mysecret - smtp_require_tls: true - smtp_tls_config: - insecure_skip_verify: false slack_api_url: http://mysecret.example.com/ - pagerduty_url: https://events.pagerduty.com/v2/enqueue - opsgenie_api_url: https://api.opsgenie.com/ - wechat_api_url: https://qyapi.weixin.qq.com/cgi-bin/ - victorops_api_url: https://alert.victorops.com/integrations/generic/20131114/alert/ - telegram_api_url: https://api.telegram.org - webex_api_url: https://webexapis.com/v1/messages - rocketchat_api_url: https://open.rocket.chat/ route: receiver: team-X-mails group_by: - alertname - cluster - service - continue: false routes: - receiver: team-X-mails match_re: service: ^(foo1|foo2|baz)$ - continue: false routes: - receiver: team-X-pager match: severity: critical - continue: false - receiver: team-Y-mails match: service: files - continue: false routes: - receiver: team-Y-pager match: severity: critical - continue: false - receiver: team-DB-pager group_by: - alertname @@ -59,7 +43,6 @@ alertmanager_config: | - database match: service: database - continue: false routes: - receiver: team-X-pager match: @@ -68,15 +51,14 @@ alertmanager_config: | - receiver: team-Y-pager match: owner: team-Y - continue: false group_wait: 30s group_interval: 5m repeat_interval: 3h inhibit_rules: - - source_match: - severity: critical - target_match: + - target_match: severity: warning + source_match: + severity: critical equal: - alertname - cluster @@ -84,166 +66,35 @@ alertmanager_config: | receivers: - name: team-X-mails email_configs: - - send_resolved: false - to: team-X+alerts@example.org - from: alertmanager@example.org - hello: host.example.org - smarthost: localhost:25 - auth_username: alertmanager - auth_password: |- - multiline - mysecret - html: '{{ template "email.default.html" . }}' - require_tls: true - tls_config: - insecure_skip_verify: false + - to: team-X+alerts@example.org - name: team-X-pager - email_configs: - - send_resolved: false - to: team-X+alerts-critical@example.org - from: alertmanager@example.org - hello: host.example.org - smarthost: localhost:25 - auth_username: alertmanager - auth_password: |- - multiline - mysecret - html: '{{ template "email.default.html" . }}' - require_tls: true - tls_config: - insecure_skip_verify: false pagerduty_configs: - - send_resolved: true - http_config: - follow_redirects: true - enable_http2: true - proxy_url: http://127.0.0.1:1025 - routing_key: mysecret - url: https://events.pagerduty.com/v2/enqueue - client: '{{ template "pagerduty.default.client" . }}' - client_url: '{{ template "pagerduty.default.clientURL" . }}' - description: '{{ template "pagerduty.default.description" .}}' - details: - firing: '{{ template "pagerduty.default.instances" .Alerts.Firing }}' - num_firing: '{{ .Alerts.Firing | len }}' - num_resolved: '{{ .Alerts.Resolved | len }}' - resolved: '{{ template "pagerduty.default.instances" .Alerts.Resolved }}' - source: '{{ template "pagerduty.default.client" . }}' + - routing_key: mysecret + email_configs: + - to: team-X+alerts-critical@example.org - name: team-Y-mails email_configs: - - send_resolved: false - to: team-Y+alerts@example.org - from: alertmanager@example.org - hello: host.example.org - smarthost: localhost:25 - auth_username: alertmanager - auth_password: |- - multiline - mysecret - html: '{{ template "email.default.html" . }}' - require_tls: true - tls_config: - insecure_skip_verify: false + - to: team-Y+alerts@example.org - name: team-Y-pager pagerduty_configs: - - send_resolved: true - http_config: - follow_redirects: true - enable_http2: true - proxy_url: http://127.0.0.1:1025 - routing_key: mysecret - url: https://events.pagerduty.com/v2/enqueue - client: '{{ template "pagerduty.default.client" . }}' - client_url: '{{ template "pagerduty.default.clientURL" . }}' - description: '{{ template "pagerduty.default.description" .}}' - details: - firing: '{{ template "pagerduty.default.instances" .Alerts.Firing }}' - num_firing: '{{ .Alerts.Firing | len }}' - num_resolved: '{{ .Alerts.Resolved | len }}' - resolved: '{{ template "pagerduty.default.instances" .Alerts.Resolved }}' - source: '{{ template "pagerduty.default.client" . }}' + - routing_key: mysecret - name: team-DB-pager pagerduty_configs: - - send_resolved: true - http_config: - follow_redirects: true - enable_http2: true - proxy_url: http://127.0.0.1:1025 - routing_key: mysecret - url: https://events.pagerduty.com/v2/enqueue - client: '{{ template "pagerduty.default.client" . }}' - client_url: '{{ template "pagerduty.default.clientURL" . }}' - description: '{{ template "pagerduty.default.description" .}}' - details: - firing: '{{ template "pagerduty.default.instances" .Alerts.Firing }}' - num_firing: '{{ .Alerts.Firing | len }}' - num_resolved: '{{ .Alerts.Resolved | len }}' - resolved: '{{ template "pagerduty.default.instances" .Alerts.Resolved }}' - source: '{{ template "pagerduty.default.client" . }}' + - routing_key: mysecret - name: victorOps-receiver victorops_configs: - - send_resolved: true - http_config: - follow_redirects: true - enable_http2: true - proxy_url: http://127.0.0.1:1025 - api_key: mysecret - api_url: https://alert.victorops.com/integrations/generic/20131114/alert/ + - api_key: mysecret routing_key: Sample_route - message_type: CRITICAL - state_message: '{{ template "victorops.default.state_message" . }}' - entity_display_name: '{{ template "victorops.default.entity_display_name" . }}' - monitoring_tool: '{{ template "victorops.default.monitoring_tool" . }}' - name: opsGenie-receiver opsgenie_configs: - - send_resolved: true - http_config: - follow_redirects: true - enable_http2: true - proxy_url: http://127.0.0.1:1025 - api_key: mysecret - api_url: https://api.opsgenie.com/ - message: '{{ template "opsgenie.default.message" . }}' - description: '{{ template "opsgenie.default.description" . }}' - source: '{{ template "opsgenie.default.source" . }}' + - api_key: mysecret - name: pushover-receiver pushover_configs: - - send_resolved: true - http_config: - follow_redirects: true - enable_http2: true - proxy_url: http://127.0.0.1:1025 - user_key: key + - user_key: key token: mysecret - title: '{{ template "pushover.default.title" . }}' - message: '{{ template "pushover.default.message" . }}' - url: '{{ template "pushover.default.url" . }}' - priority: '{{ if eq .Status "firing" }}2{{ else }}0{{ end }}' - retry: 1m0s - expire: 1h0m0s - html: false - name: slack-receiver slack_configs: - - send_resolved: false - http_config: - follow_redirects: true - enable_http2: true - proxy_url: http://127.0.0.1:1025 - api_url: http://mysecret.example.com/ - channel: '#my-channel' - username: '{{ template "slack.default.username" . }}' - color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}' - title: '{{ template "slack.default.title" . }}' - title_link: '{{ template "slack.default.titlelink" . }}' - pretext: '{{ template "slack.default.pretext" . }}' - text: '{{ template "slack.default.text" . }}' - short_fields: false - footer: '{{ template "slack.default.footer" . }}' - fallback: '{{ template "slack.default.fallback" . }}' - callback_id: '{{ template "slack.default.callbackid" . }}' - icon_emoji: '{{ template "slack.default.iconemoji" . }}' - icon_url: '{{ template "slack.default.iconurl" . }}' + - channel: '#my-channel' image_url: http://some.img.com/img.png - link_names: false templates: - /etc/alertmanager/template/*.tmpl From cea65806a70d8d8fb1feaaf29122cd6fa97d8ccf Mon Sep 17 00:00:00 2001 From: Stephen Lang Date: Thu, 20 Nov 2025 16:16:21 +0000 Subject: [PATCH 3/6] beyla: add meta_cache_address to beyla.ebpf.attributes.kubernetes (#4871) * beyla: add meta_cache_address to beyla.ebpf.attributes.kubernetes * chore: update changelog * chore: lint --------- Co-authored-by: Clayton Cornell <131809008+clayton-cornell@users.noreply.github.com> --- CHANGELOG.md | 2 ++ docs/sources/reference/components/beyla/beyla.ebpf.md | 1 + internal/component/beyla/ebpf/args.go | 1 + internal/component/beyla/ebpf/beyla_linux.go | 3 +++ internal/component/beyla/ebpf/beyla_linux_test.go | 4 ++++ 5 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b286c4624..89e6dec3ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,8 @@ v1.12.0-rc.1 - `kubernetes.discovery` Add support for attaching namespace metadata. (@kgeckhart) +- Add `meta_cache_address` to `beyla.ebpf` component. (@skl) + ### Bugfixes - Stop `loki.source.kubernetes` discarding log lines with duplicate timestamps. (@ciaranj) diff --git a/docs/sources/reference/components/beyla/beyla.ebpf.md b/docs/sources/reference/components/beyla/beyla.ebpf.md index 1cd1ec237a..e17a0bb934 100644 --- a/docs/sources/reference/components/beyla/beyla.ebpf.md +++ b/docs/sources/reference/components/beyla/beyla.ebpf.md @@ -146,6 +146,7 @@ This `kubernetes` block configures the decorating of the metrics and traces with | `enable` | `string` | Enable the Kubernetes metadata decoration. | `autodetect` | no | | `informers_resync_period` | `duration` | Period for Kubernetes informers resynchronization. | `"30m"` | no | | `informers_sync_timeout` | `duration` | Timeout for Kubernetes informers synchronization. | `"30s"` | no | +| `meta_cache_address` | `string` | Address of the Kubernetes metadata cache service. | `""` | no | | `meta_restrict_local_node` | `bool` | Restrict Kubernetes metadata collection to local node. | `false` | no | If `cluster_name` isn't set, Beyla tries to detect the cluster name from the Kubernetes API. diff --git a/internal/component/beyla/ebpf/args.go b/internal/component/beyla/ebpf/args.go index a9c9063376..1f508bd78d 100644 --- a/internal/component/beyla/ebpf/args.go +++ b/internal/component/beyla/ebpf/args.go @@ -51,6 +51,7 @@ type KubernetesDecorator struct { InformersResyncPeriod time.Duration `alloy:"informers_resync_period,attr,optional"` DisableInformers []string `alloy:"disable_informers,attr,optional"` MetaRestrictLocalNode bool `alloy:"meta_restrict_local_node,attr,optional"` + MetaCacheAddress string `alloy:"meta_cache_address,attr,optional"` } type InstanceIDConfig struct { diff --git a/internal/component/beyla/ebpf/beyla_linux.go b/internal/component/beyla/ebpf/beyla_linux.go index f9f660c710..34752c6f1a 100644 --- a/internal/component/beyla/ebpf/beyla_linux.go +++ b/internal/component/beyla/ebpf/beyla_linux.go @@ -153,6 +153,9 @@ func (args Attributes) Convert() beyla.Attributes { attrs.Kubernetes.DisableInformers = args.Kubernetes.DisableInformers attrs.Kubernetes.MetaRestrictLocalNode = args.Kubernetes.MetaRestrictLocalNode attrs.Kubernetes.ClusterName = args.Kubernetes.ClusterName + if args.Kubernetes.MetaCacheAddress != "" { + attrs.Kubernetes.MetaCacheAddress = args.Kubernetes.MetaCacheAddress + } // InstanceID if args.InstanceID.HostnameDNSResolution { attrs.InstanceID.HostnameDNSResolution = args.InstanceID.HostnameDNSResolution diff --git a/internal/component/beyla/ebpf/beyla_linux_test.go b/internal/component/beyla/ebpf/beyla_linux_test.go index 1ecc021250..2d2c06c119 100644 --- a/internal/component/beyla/ebpf/beyla_linux_test.go +++ b/internal/component/beyla/ebpf/beyla_linux_test.go @@ -48,6 +48,7 @@ func TestArguments_UnmarshalSyntax(t *testing.T) { cluster_name = "test" disable_informers = ["node"] meta_restrict_local_node = true + meta_cache_address = "localhost:9090" } select { attr = "sql_client_duration" @@ -159,6 +160,7 @@ func TestArguments_UnmarshalSyntax(t *testing.T) { require.Equal(t, "test", cfg.Attributes.Kubernetes.ClusterName) require.Equal(t, []string{"node"}, cfg.Attributes.Kubernetes.DisableInformers) require.True(t, cfg.Attributes.Kubernetes.MetaRestrictLocalNode) + require.Equal(t, "localhost:9090", cfg.Attributes.Kubernetes.MetaCacheAddress) require.Len(t, cfg.Attributes.Select, 1) sel, ok := cfg.Attributes.Select["sql_client_duration"] require.True(t, ok) @@ -701,6 +703,7 @@ func TestConvert_Attributes(t *testing.T) { Kubernetes: KubernetesDecorator{ Enable: "true", InformersSyncTimeout: 15 * time.Second, + MetaCacheAddress: "localhost:9090", }, Select: Selections{ { @@ -720,6 +723,7 @@ func TestConvert_Attributes(t *testing.T) { InformersSyncTimeout: 15 * time.Second, InformersResyncPeriod: 30 * time.Minute, ResourceLabels: beyla.DefaultConfig.Attributes.Kubernetes.ResourceLabels, + MetaCacheAddress: "localhost:9090", }, HostID: beyla.HostIDConfig{ FetchTimeout: 500 * time.Millisecond, From 1a4b97a7b285de254ae227d12ce8a1728e8f1e8b Mon Sep 17 00:00:00 2001 From: Nikola Grcevski <6207777+grcevski@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:10:43 -0500 Subject: [PATCH 4/6] Upgrade Beyla component to 2.7.7 (#4891) * upgrade beyla to 2.7.7 * update to Beyla 2.7.8 * update docs --- CHANGELOG.md | 2 +- docs/sources/_index.md | 2 +- docs/sources/_index.md.t | 2 +- .../reference/components/beyla/beyla.ebpf.md | 16 ++++++++++++++ go.mod | 6 +++--- go.sum | 8 +++---- internal/component/beyla/ebpf/args.go | 2 ++ internal/component/beyla/ebpf/beyla_linux.go | 6 ++++++ .../component/beyla/ebpf/beyla_linux_test.go | 21 +++++++++++++++++++ 9 files changed, 55 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89e6dec3ba..4b320cd6a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,7 +94,7 @@ v1.12.0-rc.1 - `prometheus.exporter.postgres` dependency has been updated to v0.18.1. This includes new `stat_progress_vacuum` and `buffercache_summary` collectors, as well as other bugfixes and enhancements. (@cristiangreco) -- Update Beyla component to 2.7.4. (@grcevski) +- Update Beyla component to 2.7.8. (@grcevski) - Support delimiters in `stage.luhn`. (@dehaansa) diff --git a/docs/sources/_index.md b/docs/sources/_index.md index 16cb8d6898..77335389de 100644 --- a/docs/sources/_index.md +++ b/docs/sources/_index.md @@ -8,7 +8,7 @@ cascade: OTEL_VERSION: v0.139.0 PROM_WIN_EXP_VERSION: v0.31.3 SNMP_VERSION: v0.29.0 - BEYLA_VERSION: v2.5.8 + BEYLA_VERSION: v2.7.8 FULL_PRODUCT_NAME: Grafana Alloy PRODUCT_NAME: Alloy hero: diff --git a/docs/sources/_index.md.t b/docs/sources/_index.md.t index d0f86588ba..d97d73ec3f 100644 --- a/docs/sources/_index.md.t +++ b/docs/sources/_index.md.t @@ -8,7 +8,7 @@ cascade: OTEL_VERSION: v0.139.0 PROM_WIN_EXP_VERSION: v0.31.3 SNMP_VERSION: v0.29.0 - BEYLA_VERSION: v2.5.8 + BEYLA_VERSION: v2.7.8 FULL_PRODUCT_NAME: Grafana Alloy PRODUCT_NAME: Alloy hero: diff --git a/docs/sources/reference/components/beyla/beyla.ebpf.md b/docs/sources/reference/components/beyla/beyla.ebpf.md index e17a0bb934..87e043deb6 100644 --- a/docs/sources/reference/components/beyla/beyla.ebpf.md +++ b/docs/sources/reference/components/beyla/beyla.ebpf.md @@ -535,6 +535,8 @@ The `metrics` block configures which metrics Beyla collects. | `allow_service_graph_self_references` | `bool` | Allow service graph metrics to reference the same service. | `false` | no | | `features` | `list(string)` | List of features to enable for the metrics. | `["application"]` | no | | `instrumentations` | `list(string)` | List of instrumentations to enable for the metrics. | `["*"]` | no | +| `extra_resource_labels` | `list(string)` | List of OTEL resource labels to include on `target_info`. | `[]` | no | +| `extra_span_resource_labels` | `list(string)` | List of OTEL resource labels to include on span metrics. | `["k8s.cluster.name", "k8s.namespace.name", "service.version", "deployment.environment"]` | no | `features` is a list of features to enable for the metrics. The following features are available: @@ -559,6 +561,20 @@ The `metrics` block configures which metrics Beyla collects. * `redis` enables the collection of Redis client/server database metrics. * `sql` enables the collection of SQL database client call metrics. +`extra_resource_labels` is a list of OTEL resource labels, supplied through the `OTEL_RESOURCE_ATTRIBUTES` environment variable +on the service, that you want to include on the `target_info` metric. + +`extra_span_resource_labels` is a list of OTEL resource labels, supplied through the `OTEL_RESOURCE_ATTRIBUTES` environment variable +on the service, that you want to include on the span metrics. The default list includes: + +* `k8s.cluster.name` +* `k8s.namespace.name` +* `service.version` +* `deployment.environment` + +The default list of `extra_span_resource_labels` is set to match the defaults chosen by Application Observability plugin in +Grafana Cloud. + #### `network` metrics The `network` block configures network metrics options for Beyla. You must append `network` to the `features` list in the `metrics` block to enable network metrics. diff --git a/go.mod b/go.mod index 0da1f43ede..93cd05a9da 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/grafana/alloy-remote-config v0.0.12 github.com/grafana/alloy/syntax v0.1.0 - github.com/grafana/beyla/v2 v2.7.6 + github.com/grafana/beyla/v2 v2.7.8 github.com/grafana/catchpoint-prometheus-exporter v0.0.0-20250218151502-6e97feaee761 github.com/grafana/ckit v0.0.0-20251024151910-87043f5a3cf7 github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2 @@ -1082,8 +1082,8 @@ exclude ( k8s.io/client-go v12.0.0+incompatible ) -// replace go.opentelemetry.io/obi => github.com/grafana/opentelemetry-ebpf-instrumentation v1.3.7 -replace go.opentelemetry.io/obi => github.com/grafana/opentelemetry-ebpf-instrumentation v1.3.7 +// replace go.opentelemetry.io/obi => github.com/grafana/opentelemetry-ebpf-instrumentation v1.3.9 +replace go.opentelemetry.io/obi => github.com/grafana/opentelemetry-ebpf-instrumentation v1.3.10 replace go.opentelemetry.io/ebpf-profiler => github.com/grafana/opentelemetry-ebpf-profiler v0.0.202546-0.20251106085643-a00a0ef2a84c diff --git a/go.sum b/go.sum index 824fb67b9e..47ff4c03be 100644 --- a/go.sum +++ b/go.sum @@ -1129,8 +1129,8 @@ github.com/gosnmp/gosnmp v1.41.0 h1:6RI78g2ZsbLvpvJegcV98LapszRQnbvYNKSa5WbCll4= github.com/gosnmp/gosnmp v1.41.0/go.mod h1:CxVS6bXqmWZlafUj9pZUnQX5e4fAltqPcijxWpCitDo= github.com/grafana/alloy-remote-config v0.0.12 h1:zyuDgWaXBITD3OFY/LDh0iShDb3ye4Av39u2xY1JY1s= github.com/grafana/alloy-remote-config v0.0.12/go.mod h1:kHE1usYo2WAVCikQkIXuoG1Clz8BSdiz3kF+DZSCQ4k= -github.com/grafana/beyla/v2 v2.7.6 h1:fqkFSzBpuo81Udn9n2viphAM52LGXl3vXq8eUEqvhAA= -github.com/grafana/beyla/v2 v2.7.6/go.mod h1:isxApXkKInJ+itgn4lDqEfCb0a97TjxWzp1tf+fpWOE= +github.com/grafana/beyla/v2 v2.7.8 h1:GSfv09nOhhbIBP9uR5rEEAwPEpCn9Gq83HR9x2woDfU= +github.com/grafana/beyla/v2 v2.7.8/go.mod h1:isxApXkKInJ+itgn4lDqEfCb0a97TjxWzp1tf+fpWOE= github.com/grafana/cadvisor v0.0.0-20240729082359-1f04a91701e2 h1:ju6EcY2aEobeBg185ETtFCKj5WzaQ48qfkbsSRRQrF4= github.com/grafana/cadvisor v0.0.0-20240729082359-1f04a91701e2/go.mod h1:8sLW/G7rcFe1CKMaA4pYT4mX3P1xQVGqM6luzEzx/2g= github.com/grafana/catchpoint-prometheus-exporter v0.0.0-20250218151502-6e97feaee761 h1:dPJOIEwtQ8uR3Qa79pb/lsSFJQ6j4P9vpCUQ4fKimG4= @@ -1173,8 +1173,8 @@ github.com/grafana/opentelemetry-collector-contrib/processor/k8sattributesproces github.com/grafana/opentelemetry-collector-contrib/processor/k8sattributesprocessor v0.0.0-20251021125353-73458b01ab23/go.mod h1:7+xyIHr2PJNE9kic/D91c9SMxACdGIGCJykzTcqsYv0= github.com/grafana/opentelemetry-collector/featuregate v0.0.0-20240325174506-2fd1623b2ca0 h1:i/Ne0XwoRokYj52ZcSmnvuyID3h/uA91n0Ycg/grHU8= github.com/grafana/opentelemetry-collector/featuregate v0.0.0-20240325174506-2fd1623b2ca0/go.mod h1:mm8+xyQfgDmqhyegZRNIQmoKsNnDTwWKFLsdMoXAb7A= -github.com/grafana/opentelemetry-ebpf-instrumentation v1.3.7 h1:1BIaJoHYtFNCCxW/JCOCAnWR+HyR5ISGkYwtzRb1d8I= -github.com/grafana/opentelemetry-ebpf-instrumentation v1.3.7/go.mod h1:/O9nceaQ+yhwDdosHsQrs1AilAJWAZf2Laa2DssrjJk= +github.com/grafana/opentelemetry-ebpf-instrumentation v1.3.10 h1:35vEGwoeJ379F7J1QejAeqVvD/8KHD5Jhw6/8+wSFJ0= +github.com/grafana/opentelemetry-ebpf-instrumentation v1.3.10/go.mod h1:/O9nceaQ+yhwDdosHsQrs1AilAJWAZf2Laa2DssrjJk= github.com/grafana/opentelemetry-ebpf-profiler v0.0.202546-0.20251106085643-a00a0ef2a84c h1:I9KB024eev8z9NRJZTcmTw0V0+txKLC6RqPUX3ADO2s= github.com/grafana/opentelemetry-ebpf-profiler v0.0.202546-0.20251106085643-a00a0ef2a84c/go.mod h1:eXOwk9x2rmJZ9+Ov5ot7NitRWNmF0jynNEeuIDRnK/Q= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= diff --git a/internal/component/beyla/ebpf/args.go b/internal/component/beyla/ebpf/args.go index 1f508bd78d..ddc0aff2f1 100644 --- a/internal/component/beyla/ebpf/args.go +++ b/internal/component/beyla/ebpf/args.go @@ -118,6 +118,8 @@ type Metrics struct { Instrumentations []string `alloy:"instrumentations,attr,optional"` AllowServiceGraphSelfReferences bool `alloy:"allow_service_graph_self_references,attr,optional"` Network Network `alloy:"network,block,optional"` + ExtraResourceLabels []string `alloy:"extra_resource_labels,attr,optional"` + ExtraSpanResourceLabels []string `alloy:"extra_span_resource_labels,attr,optional"` } type Traces struct { diff --git a/internal/component/beyla/ebpf/beyla_linux.go b/internal/component/beyla/ebpf/beyla_linux.go index 34752c6f1a..04fb983b27 100644 --- a/internal/component/beyla/ebpf/beyla_linux.go +++ b/internal/component/beyla/ebpf/beyla_linux.go @@ -447,6 +447,12 @@ func (args Metrics) Convert() prom.PrometheusConfig { p.Instrumentations = args.Instrumentations } p.AllowServiceGraphSelfReferences = args.AllowServiceGraphSelfReferences + if args.ExtraResourceLabels != nil { + p.ExtraResourceLabels = args.ExtraResourceLabels + } + if args.ExtraSpanResourceLabels != nil { + p.ExtraSpanResourceLabels = args.ExtraSpanResourceLabels + } return p } diff --git a/internal/component/beyla/ebpf/beyla_linux_test.go b/internal/component/beyla/ebpf/beyla_linux_test.go index 2d2c06c119..e594cd00f6 100644 --- a/internal/component/beyla/ebpf/beyla_linux_test.go +++ b/internal/component/beyla/ebpf/beyla_linux_test.go @@ -825,16 +825,37 @@ func TestConvert_Prometheus(t *testing.T) { Features: []string{"application", "network"}, Instrumentations: []string{"redis", "sql"}, AllowServiceGraphSelfReferences: true, + ExtraResourceLabels: nil, + ExtraSpanResourceLabels: []string{"service.version"}, } expectedConfig := beyla.DefaultConfig.Prometheus expectedConfig.Features = args.Features expectedConfig.Instrumentations = args.Instrumentations expectedConfig.AllowServiceGraphSelfReferences = true + expectedConfig.ExtraSpanResourceLabels = args.ExtraSpanResourceLabels config := args.Convert() require.Equal(t, expectedConfig, config) + + args = Metrics{ + Features: []string{"application", "network"}, + Instrumentations: []string{"redis", "sql"}, + AllowServiceGraphSelfReferences: true, + ExtraResourceLabels: []string{"service.version"}, + } + + expectedConfig = beyla.DefaultConfig.Prometheus + expectedConfig.Features = args.Features + expectedConfig.Instrumentations = args.Instrumentations + expectedConfig.AllowServiceGraphSelfReferences = true + expectedConfig.ExtraResourceLabels = args.ExtraResourceLabels + expectedConfig.ExtraSpanResourceLabels = []string{"k8s.cluster.name", "k8s.namespace.name", "service.version", "deployment.environment"} + + config = args.Convert() + + require.Equal(t, expectedConfig, config) } func TestConvert_Network(t *testing.T) { From 30835e325b64353fdcd4e403f28ddb9ad093bf1a Mon Sep 17 00:00:00 2001 From: Christian Simon Date: Mon, 24 Nov 2025 15:50:53 +0000 Subject: [PATCH 5/6] fix(pyroscope.ebpf): Use meter noop in otel-ebpf-profiler (#4920) With a recent change, the default meter provider was no longer initialized. This change disables the otel-ebpf-profiler internal metrics to avoid warnings like this to appear: ``` WARN[0121] Invalid metric id 102, skipping WARN[0121] Invalid metric id 272, skipping ``` --- internal/component/pyroscope/ebpf/ebpf_linux.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/component/pyroscope/ebpf/ebpf_linux.go b/internal/component/pyroscope/ebpf/ebpf_linux.go index 141839a735..33ccb0a2c6 100644 --- a/internal/component/pyroscope/ebpf/ebpf_linux.go +++ b/internal/component/pyroscope/ebpf/ebpf_linux.go @@ -22,10 +22,12 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" "go.opentelemetry.io/ebpf-profiler/interpreter/python" + ebpfmetrics "go.opentelemetry.io/ebpf-profiler/metrics" discovery2 "go.opentelemetry.io/ebpf-profiler/pyroscope/discovery" "go.opentelemetry.io/ebpf-profiler/pyroscope/dynamicprofiling" "go.opentelemetry.io/ebpf-profiler/pyroscope/internalshim/controller" "go.opentelemetry.io/ebpf-profiler/pyroscope/symb/irsymcache" + metricnoop "go.opentelemetry.io/otel/metric/noop" ) func init() { @@ -40,6 +42,8 @@ func init() { }, }) python.NoContinueWithNextUnwinder.Store(true) + // Disable ebpf profiler metrics + ebpfmetrics.Start(metricnoop.Meter{}) } func New(logger log.Logger, reg prometheus.Registerer, id string, args Arguments) (*Component, error) { From dae83bac7c781d19cd7a50f9b618de1dc4cbb3b7 Mon Sep 17 00:00:00 2001 From: Joe Harvey <51208233+jharvey10@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:58:22 -0500 Subject: [PATCH 6/6] bump rc version in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b320cd6a6..c21d499758 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ internal API changes are not present. Main (unreleased) ----------------- -v1.12.0-rc.1 +v1.12.0-rc.2 ----------------- ### Breaking changes