Alerting: Add support for Redis Sentinel for Alerting HA (#106322)

* Alerting: Add support for Redis Sentinel

* docs

* docs

* Use minisentinel in test

* Apply suggestions from code review

Co-authored-by: Johnny Kartheiser <140559259+JohnnyK-Grafana@users.noreply.github.com>
Co-authored-by: Fayzal Ghantiwala <114010985+fayzal-g@users.noreply.github.com>

* "address(es)" -> "address or addresses"

* make update-workspace

* make lint-go-diff

---------

Co-authored-by: Johnny Kartheiser <140559259+JohnnyK-Grafana@users.noreply.github.com>
Co-authored-by: Fayzal Ghantiwala <114010985+fayzal-g@users.noreply.github.com>
This commit is contained in:
Vadim Stepanov
2025-06-05 15:02:40 +01:00
committed by GitHub
co-authored by Johnny Kartheiser Fayzal Ghantiwala
parent 941162ca79
commit 5137995830
11 changed files with 292 additions and 80 deletions
+19
View File
@@ -69,6 +69,11 @@ const (
lokiDefaultMaxQuerySize = 65536 // 64kb
)
var (
errHARedisBothClusterAndSentinel = fmt.Errorf("'ha_redis_cluster_mode_enabled' and 'ha_redis_sentinel_mode_enabled' are mutually exclusive")
errHARedisSentinelMasterNameRequired = fmt.Errorf("'ha_redis_sentinel_master_name' is required when 'ha_redis_sentinel_mode_enabled' is true")
)
type UnifiedAlertingSettings struct {
AdminConfigPollInterval time.Duration
AlertmanagerConfigPollInterval time.Duration
@@ -83,6 +88,10 @@ type UnifiedAlertingSettings struct {
HAPushPullInterval time.Duration
HALabel string
HARedisClusterModeEnabled bool
HARedisSentinelModeEnabled bool
HARedisSentinelMasterName string
HARedisSentinelUsername string
HARedisSentinelPassword string
HARedisAddr string
HARedisPeerName string
HARedisPrefix string
@@ -276,6 +285,16 @@ func (cfg *Cfg) ReadUnifiedAlertingSettings(iniFile *ini.File) error {
uaCfg.HAAdvertiseAddr = ua.Key("ha_advertise_address").MustString("")
uaCfg.HALabel = ua.Key("ha_label").MustString("")
uaCfg.HARedisClusterModeEnabled = ua.Key("ha_redis_cluster_mode_enabled").MustBool(false)
uaCfg.HARedisSentinelModeEnabled = ua.Key("ha_redis_sentinel_mode_enabled").MustBool(false)
if uaCfg.HARedisClusterModeEnabled && uaCfg.HARedisSentinelModeEnabled {
return errHARedisBothClusterAndSentinel
}
uaCfg.HARedisSentinelMasterName = ua.Key("ha_redis_sentinel_master_name").MustString("")
if uaCfg.HARedisSentinelModeEnabled && uaCfg.HARedisSentinelMasterName == "" {
return errHARedisSentinelMasterNameRequired
}
uaCfg.HARedisSentinelUsername = ua.Key("ha_redis_sentinel_username").MustString("")
uaCfg.HARedisSentinelPassword = ua.Key("ha_redis_sentinel_password").MustString("")
uaCfg.HARedisAddr = ua.Key("ha_redis_address").MustString("")
uaCfg.HARedisPeerName = ua.Key("ha_redis_peer_name").MustString("")
uaCfg.HARedisPrefix = ua.Key("ha_redis_prefix").MustString("")
@@ -350,3 +350,72 @@ func TestHARedisTLSSettings(t *testing.T) {
require.Equal(t, cipherSuites, cfg.UnifiedAlerting.HARedisTLSConfig.CipherSuites)
require.Equal(t, minVersion, cfg.UnifiedAlerting.HARedisTLSConfig.MinVersion)
}
func TestHARedisSentinelModeSettings(t *testing.T) {
testCases := []struct {
desc string
haRedisSentinelModeEnabled bool
haRedisClusterModeEnabled bool
haRedisSentinelMasterName string
haRedisSentinelUsername string
haRedisSentinelPassword string
expectedErr error
}{
{
desc: "should not fail when Sentinel mode is enabled and master name is set",
haRedisSentinelModeEnabled: true,
haRedisSentinelMasterName: "exampleMasterName",
},
{
desc: "should not fail when Sentinel mode is enabled, master name is set, and Sentinel username and password are provided",
haRedisSentinelModeEnabled: true,
haRedisSentinelMasterName: "exampleMasterName",
haRedisSentinelUsername: "exampleSentinelUsername",
haRedisSentinelPassword: "exampleSentinelPassword",
},
{
desc: "should fail when Sentinel mode is enabled but master name is not set",
haRedisSentinelModeEnabled: true,
expectedErr: errHARedisSentinelMasterNameRequired,
},
{
desc: "should fail when both Sentinel mode and Cluster mode are enabled",
haRedisSentinelModeEnabled: true,
haRedisClusterModeEnabled: true,
expectedErr: errHARedisBothClusterAndSentinel,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
f := ini.Empty()
section, err := f.NewSection("unified_alerting")
require.NoError(t, err)
_, err = section.NewKey("ha_redis_sentinel_mode_enabled", strconv.FormatBool(tc.haRedisSentinelModeEnabled))
require.NoError(t, err)
_, err = section.NewKey("ha_redis_cluster_mode_enabled", strconv.FormatBool(tc.haRedisClusterModeEnabled))
require.NoError(t, err)
_, err = section.NewKey("ha_redis_sentinel_master_name", tc.haRedisSentinelMasterName)
require.NoError(t, err)
_, err = section.NewKey("ha_redis_sentinel_username", tc.haRedisSentinelUsername)
require.NoError(t, err)
_, err = section.NewKey("ha_redis_sentinel_password", tc.haRedisSentinelPassword)
require.NoError(t, err)
cfg := NewCfg()
err = cfg.ReadUnifiedAlertingSettings(f)
if tc.expectedErr == nil {
require.NoError(t, err)
} else {
require.ErrorIs(t, err, tc.expectedErr)
return
}
require.Equal(t, tc.haRedisSentinelModeEnabled, cfg.UnifiedAlerting.HARedisSentinelModeEnabled)
require.Equal(t, tc.haRedisSentinelMasterName, cfg.UnifiedAlerting.HARedisSentinelMasterName)
require.Equal(t, tc.haRedisSentinelUsername, cfg.UnifiedAlerting.HARedisSentinelUsername)
require.Equal(t, tc.haRedisSentinelPassword, cfg.UnifiedAlerting.HARedisSentinelPassword)
})
}
}