mirror of
https://github.com/prymitive/karma
synced 2026-08-23 11:56:20 +00:00
feat(backend): use cluster name from config
This commit is contained in:
committed by
Łukasz Mierzwa
parent
e52cc91964
commit
4f58ff2e58
+36
-9
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"vbom.ml/util/sortorder"
|
||||
@@ -14,8 +15,6 @@ import (
|
||||
"github.com/prymitive/karma/internal/models"
|
||||
"github.com/prymitive/karma/internal/slices"
|
||||
"github.com/prymitive/karma/internal/uri"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func getFiltersFromQuery(filterStrings []string) ([]filters.FilterT, bool) {
|
||||
@@ -89,6 +88,23 @@ func countersToLabelStats(counters map[string]map[string]int) models.LabelNameSt
|
||||
return data
|
||||
}
|
||||
|
||||
func clusterMembersFromConfig(am *alertmanager.Alertmanager) []string {
|
||||
members := []string{}
|
||||
|
||||
upstreams := alertmanager.GetAlertmanagers()
|
||||
for _, upstream := range upstreams {
|
||||
if upstream.Cluster == am.Cluster {
|
||||
members = append(members, upstream.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return members
|
||||
}
|
||||
|
||||
func clusterMembersFromAPI(am *alertmanager.Alertmanager) []string {
|
||||
return am.ClusterMemberNames()
|
||||
}
|
||||
|
||||
func getUpstreams() models.AlertmanagerAPISummary {
|
||||
summary := models.AlertmanagerAPISummary{}
|
||||
|
||||
@@ -96,13 +112,24 @@ func getUpstreams() models.AlertmanagerAPISummary {
|
||||
upstreams := alertmanager.GetAlertmanagers()
|
||||
for _, upstream := range upstreams {
|
||||
members := upstream.ClusterMemberNames()
|
||||
key, err := slices.StringSliceToSHA1(members)
|
||||
if err != nil {
|
||||
log.Errorf("slices.StringSliceToSHA1 error: %s", err)
|
||||
continue
|
||||
|
||||
var clusterName string
|
||||
if upstream.Cluster != "" {
|
||||
configPeers := clusterMembersFromConfig(upstream)
|
||||
apiPeers := clusterMembersFromAPI(upstream)
|
||||
missing, extra := slices.StringSliceDiff(configPeers, apiPeers)
|
||||
|
||||
if len(missing) == 0 && len(extra) == 0 {
|
||||
clusterName = upstream.Cluster
|
||||
} else {
|
||||
clusterName = fmt.Sprintf("%s @ %s", strings.Join(members, " | "), upstream.Cluster)
|
||||
}
|
||||
} else {
|
||||
clusterName = strings.Join(members, " | ")
|
||||
}
|
||||
if _, found := clusters[key]; !found {
|
||||
clusters[key] = members
|
||||
|
||||
if _, found := clusters[clusterName]; !found {
|
||||
clusters[clusterName] = members
|
||||
}
|
||||
|
||||
u := models.AlertmanagerAPIStatus{
|
||||
@@ -114,7 +141,7 @@ func getUpstreams() models.AlertmanagerAPISummary {
|
||||
CORSCredentials: upstream.CORSCredentials,
|
||||
Error: upstream.Error(),
|
||||
Version: upstream.Version(),
|
||||
Cluster: upstream.ClusterID(),
|
||||
Cluster: clusterName,
|
||||
ClusterMembers: members,
|
||||
}
|
||||
if !upstream.ProxyRequests {
|
||||
|
||||
@@ -68,6 +68,7 @@ func TestAuthHeader(t *testing.T) {
|
||||
apiCache = cache.New(cache.NoExpiration, 10*time.Second)
|
||||
|
||||
am, err := alertmanager.NewAlertmanager(
|
||||
"cluster",
|
||||
fmt.Sprintf("testAuthHeader/%s", version),
|
||||
testCase.alertmanagerURI,
|
||||
alertmanager.WithRequestTimeout(time.Second*5),
|
||||
|
||||
@@ -178,6 +178,7 @@ func setupUpstreams() error {
|
||||
}
|
||||
|
||||
am, err := alertmanager.NewAlertmanager(
|
||||
s.Cluster,
|
||||
s.Name,
|
||||
s.URI,
|
||||
alertmanager.WithExternalURI(s.ExternalURI),
|
||||
|
||||
@@ -105,6 +105,7 @@ func TestProxy(t *testing.T) {
|
||||
|
||||
r := ginTestEngine()
|
||||
am, err := alertmanager.NewAlertmanager(
|
||||
"cluster",
|
||||
"dummy",
|
||||
"http://localhost:9093",
|
||||
alertmanager.WithRequestTimeout(time.Second*5),
|
||||
@@ -207,6 +208,7 @@ func TestProxyHeaders(t *testing.T) {
|
||||
testCase := testCase //scopelint pin
|
||||
r := ginTestEngine()
|
||||
am, err := alertmanager.NewAlertmanager(
|
||||
"cluster",
|
||||
"dummy",
|
||||
testCase.alertmanagerURI,
|
||||
alertmanager.WithRequestTimeout(time.Second*5),
|
||||
@@ -310,6 +312,7 @@ func TestProxyToSubURIAlertmanager(t *testing.T) {
|
||||
r := ginTestEngine()
|
||||
|
||||
am, err := alertmanager.NewAlertmanager(
|
||||
"cluster",
|
||||
"suburi",
|
||||
testCase.alertmanagerURI,
|
||||
alertmanager.WithRequestTimeout(time.Second*5),
|
||||
@@ -505,6 +508,7 @@ func TestProxyUserRewrite(t *testing.T) {
|
||||
r := ginTestEngine()
|
||||
|
||||
am, err := alertmanager.NewAlertmanager(
|
||||
"cluster",
|
||||
"proxyAuth",
|
||||
"http://localhost",
|
||||
alertmanager.WithRequestTimeout(time.Second*5),
|
||||
@@ -997,6 +1001,7 @@ func TestProxySilenceACL(t *testing.T) {
|
||||
r := ginTestEngine()
|
||||
|
||||
am, err := alertmanager.NewAlertmanager(
|
||||
"cluster",
|
||||
"proxyACL",
|
||||
"http://localhost",
|
||||
alertmanager.WithRequestTimeout(time.Second*5),
|
||||
|
||||
@@ -85,7 +85,8 @@ level=info msg=" silences: \"\""
|
||||
level=info msg="alertmanager:"
|
||||
level=info msg=" interval: 10s"
|
||||
level=info msg=" servers:"
|
||||
level=info msg=" - name: ro"
|
||||
level=info msg=" - cluster: \"\""
|
||||
level=info msg=" name: ro"
|
||||
level=info msg=" uri: http://localhost:9093"
|
||||
level=info msg=" external_uri: http://localhost:9093"
|
||||
level=info msg=" timeout: 10s"
|
||||
|
||||
@@ -20,11 +20,13 @@ authorization:
|
||||
alertmanager:
|
||||
interval: 10s
|
||||
servers:
|
||||
- name: ha1
|
||||
- cluster: HA
|
||||
name: ha1
|
||||
uri: "http://localhost:9093"
|
||||
timeout: 10s
|
||||
proxy: true
|
||||
- name: ha2
|
||||
- cluster: HA
|
||||
name: ha2
|
||||
uri: "http://localhost:9094"
|
||||
timeout: 10s
|
||||
readonly: true
|
||||
@@ -266,7 +268,8 @@ level=info msg=" silences: \"\""
|
||||
level=info msg="alertmanager:"
|
||||
level=info msg=" interval: 10s"
|
||||
level=info msg=" servers:"
|
||||
level=info msg=" - name: ha1"
|
||||
level=info msg=" - cluster: HA"
|
||||
level=info msg=" name: ha1"
|
||||
level=info msg=" uri: http://localhost:9093"
|
||||
level=info msg=" external_uri: \"\""
|
||||
level=info msg=" timeout: 10s"
|
||||
@@ -280,7 +283,8 @@ level=info msg=" insecureSkipVerify: false"
|
||||
level=info msg=" headers: {}"
|
||||
level=info msg=" cors:"
|
||||
level=info msg=" credentials: include"
|
||||
level=info msg=" - name: ha2"
|
||||
level=info msg=" - cluster: HA"
|
||||
level=info msg=" name: ha2"
|
||||
level=info msg=" uri: http://localhost:9094"
|
||||
level=info msg=" external_uri: \"\""
|
||||
level=info msg=" timeout: 10s"
|
||||
@@ -294,7 +298,8 @@ level=info msg=" insecureSkipVerify: false"
|
||||
level=info msg=" headers: {}"
|
||||
level=info msg=" cors:"
|
||||
level=info msg=" credentials: omit"
|
||||
level=info msg=" - name: local"
|
||||
level=info msg=" - cluster: \"\""
|
||||
level=info msg=" name: local"
|
||||
level=info msg=" uri: http://localhost:9095"
|
||||
level=info msg=" external_uri: \"\""
|
||||
level=info msg=" timeout: 40s"
|
||||
@@ -309,7 +314,8 @@ level=info msg=" headers:"
|
||||
level=info msg=" X-Auth-Test: some-token-or-other-string"
|
||||
level=info msg=" cors:"
|
||||
level=info msg=" credentials: same-origin"
|
||||
level=info msg=" - name: client-auth"
|
||||
level=info msg=" - cluster: \"\""
|
||||
level=info msg=" name: client-auth"
|
||||
level=info msg=" uri: https://localhost:9096"
|
||||
level=info msg=" external_uri: \"\""
|
||||
level=info msg=" timeout: 10s"
|
||||
|
||||
+553
-4
@@ -1025,12 +1025,558 @@ func TestUpstreamStatus(t *testing.T) {
|
||||
CORSCredentials: "include",
|
||||
Error: `^unknown error \(status 404\): .+`,
|
||||
Version: "",
|
||||
Cluster: "843c4a11660fe38ea61e6960a29d4f4796da6488",
|
||||
Cluster: "default",
|
||||
ClusterMembers: []string{"default"},
|
||||
},
|
||||
},
|
||||
Clusters: map[string][]string{
|
||||
"843c4a11660fe38ea61e6960a29d4f4796da6488": {"default"},
|
||||
"default": {"default"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "HA Cluster",
|
||||
mocks: []mockT{
|
||||
{
|
||||
uri: "http://ha1.example.com/metrics",
|
||||
code: 200,
|
||||
body: `alertmanager_build_info{version="0.20.0"} 1`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/metrics",
|
||||
code: 200,
|
||||
body: `alertmanager_build_info{version="0.19.0"} 1`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/status",
|
||||
code: 200,
|
||||
body: `{
|
||||
"cluster": {
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"peers": [
|
||||
{
|
||||
"address": "10.16.0.1:9094",
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
},
|
||||
{
|
||||
"address": "10.16.0.2:9094",
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
}
|
||||
],
|
||||
"status": "ready"
|
||||
},
|
||||
"versionInfo": {
|
||||
"version":"0.20.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/status",
|
||||
code: 200,
|
||||
body: `{
|
||||
"cluster": {
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"peers": [
|
||||
{
|
||||
"address": "10.16.0.1:9094",
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
},
|
||||
{
|
||||
"address": "10.16.0.2:9094",
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
}
|
||||
],
|
||||
"status": "ready"
|
||||
},
|
||||
"versionInfo": {
|
||||
"version":"0.19.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/alerts/groups",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/silences",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/alerts/groups",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/silences",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
},
|
||||
upstreams: []config.AlertmanagerConfig{
|
||||
{
|
||||
Cluster: "HA",
|
||||
Name: "ha1",
|
||||
URI: "http://ha1.example.com",
|
||||
Proxy: false,
|
||||
ReadOnly: false,
|
||||
Headers: map[string]string{},
|
||||
CORS: config.AlertmanagerCORS{
|
||||
Credentials: "omit",
|
||||
},
|
||||
},
|
||||
{
|
||||
Cluster: "HA",
|
||||
Name: "ha2",
|
||||
URI: "http://ha2.example.com",
|
||||
Proxy: false,
|
||||
ReadOnly: true,
|
||||
Headers: map[string]string{},
|
||||
CORS: config.AlertmanagerCORS{
|
||||
Credentials: "omit",
|
||||
},
|
||||
},
|
||||
},
|
||||
status: models.AlertmanagerAPISummary{
|
||||
Counters: models.AlertmanagerAPICounters{
|
||||
Total: 2,
|
||||
Healthy: 2,
|
||||
Failed: 0,
|
||||
},
|
||||
Instances: []models.AlertmanagerAPIStatus{
|
||||
{
|
||||
Name: "ha1",
|
||||
URI: "http://ha1.example.com",
|
||||
PublicURI: "http://ha1.example.com",
|
||||
ReadOnly: false,
|
||||
Headers: map[string]string{},
|
||||
CORSCredentials: "omit",
|
||||
Error: "",
|
||||
Version: "0.20.0",
|
||||
Cluster: "HA",
|
||||
ClusterMembers: []string{"ha1", "ha2"},
|
||||
},
|
||||
{
|
||||
Name: "ha2",
|
||||
URI: "http://ha2.example.com",
|
||||
PublicURI: "http://ha2.example.com",
|
||||
ReadOnly: true,
|
||||
Headers: map[string]string{},
|
||||
CORSCredentials: "omit",
|
||||
Error: "",
|
||||
Version: "0.19.0",
|
||||
Cluster: "HA",
|
||||
ClusterMembers: []string{"ha1", "ha2"},
|
||||
},
|
||||
},
|
||||
Clusters: map[string][]string{
|
||||
"HA": {"ha1", "ha2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "HA Cluster Without Name",
|
||||
mocks: []mockT{
|
||||
{
|
||||
uri: "http://ha1.example.com/metrics",
|
||||
code: 200,
|
||||
body: `alertmanager_build_info{version="0.20.0"} 1`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/metrics",
|
||||
code: 200,
|
||||
body: `alertmanager_build_info{version="0.19.0"} 1`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/status",
|
||||
code: 200,
|
||||
body: `{
|
||||
"cluster": {
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"peers": [
|
||||
{
|
||||
"address": "10.16.0.1:9094",
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
},
|
||||
{
|
||||
"address": "10.16.0.2:9094",
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
}
|
||||
],
|
||||
"status": "ready"
|
||||
},
|
||||
"versionInfo": {
|
||||
"version":"0.20.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/status",
|
||||
code: 200,
|
||||
body: `{
|
||||
"cluster": {
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"peers": [
|
||||
{
|
||||
"address": "10.16.0.1:9094",
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
},
|
||||
{
|
||||
"address": "10.16.0.2:9094",
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
}
|
||||
],
|
||||
"status": "ready"
|
||||
},
|
||||
"versionInfo": {
|
||||
"version":"0.19.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/alerts/groups",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/silences",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/alerts/groups",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/silences",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
},
|
||||
upstreams: []config.AlertmanagerConfig{
|
||||
{
|
||||
Name: "ha1",
|
||||
URI: "http://ha1.example.com",
|
||||
Proxy: false,
|
||||
ReadOnly: false,
|
||||
Headers: map[string]string{},
|
||||
CORS: config.AlertmanagerCORS{
|
||||
Credentials: "same-site",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ha2",
|
||||
URI: "http://ha2.example.com",
|
||||
Proxy: false,
|
||||
ReadOnly: true,
|
||||
Headers: map[string]string{},
|
||||
CORS: config.AlertmanagerCORS{
|
||||
Credentials: "same-site",
|
||||
},
|
||||
},
|
||||
},
|
||||
status: models.AlertmanagerAPISummary{
|
||||
Counters: models.AlertmanagerAPICounters{
|
||||
Total: 2,
|
||||
Healthy: 2,
|
||||
Failed: 0,
|
||||
},
|
||||
Instances: []models.AlertmanagerAPIStatus{
|
||||
{
|
||||
Name: "ha1",
|
||||
URI: "http://ha1.example.com",
|
||||
PublicURI: "http://ha1.example.com",
|
||||
ReadOnly: false,
|
||||
Headers: map[string]string{},
|
||||
CORSCredentials: "same-site",
|
||||
Error: "",
|
||||
Version: "0.20.0",
|
||||
Cluster: "ha1 | ha2",
|
||||
ClusterMembers: []string{"ha1", "ha2"},
|
||||
},
|
||||
{
|
||||
Name: "ha2",
|
||||
URI: "http://ha2.example.com",
|
||||
PublicURI: "http://ha2.example.com",
|
||||
ReadOnly: true,
|
||||
Headers: map[string]string{},
|
||||
CORSCredentials: "same-site",
|
||||
Error: "",
|
||||
Version: "0.19.0",
|
||||
Cluster: "ha1 | ha2",
|
||||
ClusterMembers: []string{"ha1", "ha2"},
|
||||
},
|
||||
},
|
||||
Clusters: map[string][]string{
|
||||
"ha1 | ha2": {"ha1", "ha2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Broken Cluster",
|
||||
mocks: []mockT{
|
||||
{
|
||||
uri: "http://ha1.example.com/metrics",
|
||||
code: 200,
|
||||
body: `alertmanager_build_info{version="0.20.0"} 1`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/metrics",
|
||||
code: 200,
|
||||
body: `alertmanager_build_info{version="0.19.0"} 1`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/status",
|
||||
code: 200,
|
||||
body: `{
|
||||
"cluster": {
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"peers": [
|
||||
{
|
||||
"address": "10.16.0.1:9094",
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
}
|
||||
],
|
||||
"status": "ready"
|
||||
},
|
||||
"versionInfo": {
|
||||
"version":"0.20.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/status",
|
||||
code: 200,
|
||||
body: `{
|
||||
"cluster": {
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"peers": [
|
||||
{
|
||||
"address": "10.16.0.2:9094",
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
}
|
||||
],
|
||||
"status": "ready"
|
||||
},
|
||||
"versionInfo": {
|
||||
"version":"0.19.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/alerts/groups",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/silences",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/alerts/groups",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/silences",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
},
|
||||
upstreams: []config.AlertmanagerConfig{
|
||||
{
|
||||
Cluster: "Broken HA",
|
||||
Name: "ha1",
|
||||
URI: "http://ha1.example.com",
|
||||
Proxy: false,
|
||||
ReadOnly: false,
|
||||
Headers: map[string]string{},
|
||||
CORS: config.AlertmanagerCORS{
|
||||
Credentials: "omit",
|
||||
},
|
||||
},
|
||||
{
|
||||
Cluster: "Broken HA",
|
||||
Name: "ha2",
|
||||
URI: "http://ha2.example.com",
|
||||
Proxy: false,
|
||||
ReadOnly: true,
|
||||
Headers: map[string]string{},
|
||||
CORS: config.AlertmanagerCORS{
|
||||
Credentials: "omit",
|
||||
},
|
||||
},
|
||||
},
|
||||
status: models.AlertmanagerAPISummary{
|
||||
Counters: models.AlertmanagerAPICounters{
|
||||
Total: 2,
|
||||
Healthy: 2,
|
||||
Failed: 0,
|
||||
},
|
||||
Instances: []models.AlertmanagerAPIStatus{
|
||||
{
|
||||
Name: "ha1",
|
||||
URI: "http://ha1.example.com",
|
||||
PublicURI: "http://ha1.example.com",
|
||||
ReadOnly: false,
|
||||
Headers: map[string]string{},
|
||||
CORSCredentials: "omit",
|
||||
Error: "",
|
||||
Version: "0.20.0",
|
||||
Cluster: "ha1 @ Broken HA",
|
||||
ClusterMembers: []string{"ha1"},
|
||||
},
|
||||
{
|
||||
Name: "ha2",
|
||||
URI: "http://ha2.example.com",
|
||||
PublicURI: "http://ha2.example.com",
|
||||
ReadOnly: true,
|
||||
Headers: map[string]string{},
|
||||
CORSCredentials: "omit",
|
||||
Error: "",
|
||||
Version: "0.19.0",
|
||||
Cluster: "ha2 @ Broken HA",
|
||||
ClusterMembers: []string{"ha2"},
|
||||
},
|
||||
},
|
||||
Clusters: map[string][]string{
|
||||
"ha1 @ Broken HA": {"ha1"},
|
||||
"ha2 @ Broken HA": {"ha2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Broken Cluster Without Name",
|
||||
mocks: []mockT{
|
||||
{
|
||||
uri: "http://ha1.example.com/metrics",
|
||||
code: 200,
|
||||
body: `alertmanager_build_info{version="0.20.0"} 1`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/metrics",
|
||||
code: 200,
|
||||
body: `alertmanager_build_info{version="0.19.0"} 1`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/status",
|
||||
code: 200,
|
||||
body: `{
|
||||
"cluster": {
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"peers": [
|
||||
{
|
||||
"address": "10.16.0.1:9094",
|
||||
"name": "AAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
}
|
||||
],
|
||||
"status": "ready"
|
||||
},
|
||||
"versionInfo": {
|
||||
"version":"0.20.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/status",
|
||||
code: 200,
|
||||
body: `{
|
||||
"cluster": {
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"peers": [
|
||||
{
|
||||
"address": "10.16.0.2:9094",
|
||||
"name": "BBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
}
|
||||
],
|
||||
"status": "ready"
|
||||
},
|
||||
"versionInfo": {
|
||||
"version":"0.19.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/alerts/groups",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha1.example.com/api/v2/silences",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/alerts/groups",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
{
|
||||
uri: "http://ha2.example.com/api/v2/silences",
|
||||
code: 200,
|
||||
body: "[]",
|
||||
},
|
||||
},
|
||||
upstreams: []config.AlertmanagerConfig{
|
||||
{
|
||||
Name: "ha1",
|
||||
URI: "http://ha1.example.com",
|
||||
Proxy: false,
|
||||
ReadOnly: false,
|
||||
Headers: map[string]string{},
|
||||
CORS: config.AlertmanagerCORS{
|
||||
Credentials: "omit",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ha2",
|
||||
URI: "http://ha2.example.com",
|
||||
Proxy: false,
|
||||
ReadOnly: true,
|
||||
Headers: map[string]string{},
|
||||
CORS: config.AlertmanagerCORS{
|
||||
Credentials: "omit",
|
||||
},
|
||||
},
|
||||
},
|
||||
status: models.AlertmanagerAPISummary{
|
||||
Counters: models.AlertmanagerAPICounters{
|
||||
Total: 2,
|
||||
Healthy: 2,
|
||||
Failed: 0,
|
||||
},
|
||||
Instances: []models.AlertmanagerAPIStatus{
|
||||
{
|
||||
Name: "ha1",
|
||||
URI: "http://ha1.example.com",
|
||||
PublicURI: "http://ha1.example.com",
|
||||
ReadOnly: false,
|
||||
Headers: map[string]string{},
|
||||
CORSCredentials: "omit",
|
||||
Error: "",
|
||||
Version: "0.20.0",
|
||||
Cluster: "ha1",
|
||||
ClusterMembers: []string{"ha1"},
|
||||
},
|
||||
{
|
||||
Name: "ha2",
|
||||
URI: "http://ha2.example.com",
|
||||
PublicURI: "http://ha2.example.com",
|
||||
ReadOnly: true,
|
||||
Headers: map[string]string{},
|
||||
CORSCredentials: "omit",
|
||||
Error: "",
|
||||
Version: "0.19.0",
|
||||
Cluster: "ha2",
|
||||
ClusterMembers: []string{"ha2"},
|
||||
},
|
||||
},
|
||||
Clusters: map[string][]string{
|
||||
"ha1": {"ha1"},
|
||||
"ha2": {"ha2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1047,7 +1593,10 @@ func TestUpstreamStatus(t *testing.T) {
|
||||
alertmanager.UnregisterAll()
|
||||
mockConfig()
|
||||
config.Config.Alertmanager.Servers = testCase.upstreams
|
||||
_ = setupUpstreams()
|
||||
err := setupUpstreams()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
log.SetLevel(log.FatalLevel)
|
||||
pullFromAlertmanager()
|
||||
r := ginTestEngine()
|
||||
@@ -1060,7 +1609,7 @@ func TestUpstreamStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
ur := models.AlertsResponse{}
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &ur)
|
||||
err = json.Unmarshal(resp.Body.Bytes(), &ur)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to unmarshal response: %s", err)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func init() {
|
||||
for _, version := range mock.ListAllMocks() {
|
||||
name := fmt.Sprintf("dedup-mock-%s", version)
|
||||
uri := fmt.Sprintf("http://%s.localhost", version)
|
||||
am, err := alertmanager.NewAlertmanager(name, uri, alertmanager.WithRequestTimeout(time.Second))
|
||||
am, err := alertmanager.NewAlertmanager("cluster", name, uri, alertmanager.WithRequestTimeout(time.Second))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -185,7 +185,7 @@ func TestClearData(t *testing.T) {
|
||||
for _, version := range mock.ListAllMocks() {
|
||||
name := fmt.Sprintf("clear-data-mock-%s", version)
|
||||
uri := fmt.Sprintf("http://localhost/clear/%s", version)
|
||||
am, _ := alertmanager.NewAlertmanager(name, uri, alertmanager.WithRequestTimeout(time.Second))
|
||||
am, _ := alertmanager.NewAlertmanager("cluster", name, uri, alertmanager.WithRequestTimeout(time.Second))
|
||||
|
||||
mock.RegisterURL(fmt.Sprintf("%s/metrics", uri), version, "metrics")
|
||||
_ = am.Pull()
|
||||
|
||||
@@ -87,7 +87,7 @@ var uriTests = []uriTest{
|
||||
|
||||
func TestAlertmanagerURI(t *testing.T) {
|
||||
for i, test := range uriTests {
|
||||
am, err := NewAlertmanager("test", test.rawURI, WithExternalURI(test.extURI), WithProxy(test.proxy))
|
||||
am, err := NewAlertmanager("cluster", "test", test.rawURI, WithExternalURI(test.extURI), WithProxy(test.proxy))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type Alertmanager struct {
|
||||
URI string `json:"uri"`
|
||||
ExternalURI string `json:"-"`
|
||||
RequestTimeout time.Duration `json:"timeout"`
|
||||
Cluster string `json:"cluster"`
|
||||
Name string `json:"name"`
|
||||
// whenever this instance should be proxied
|
||||
ProxyRequests bool `json:"proxyRequests"`
|
||||
|
||||
@@ -22,11 +22,12 @@ var (
|
||||
)
|
||||
|
||||
// NewAlertmanager creates a new Alertmanager instance
|
||||
func NewAlertmanager(name, upstreamURI string, opts ...Option) (*Alertmanager, error) {
|
||||
func NewAlertmanager(cluster, name, upstreamURI string, opts ...Option) (*Alertmanager, error) {
|
||||
am := &Alertmanager{
|
||||
URI: upstreamURI,
|
||||
ExternalURI: "",
|
||||
RequestTimeout: time.Second * 10,
|
||||
Cluster: cluster,
|
||||
Name: name,
|
||||
lock: sync.RWMutex{},
|
||||
alertGroups: []models.AlertGroup{},
|
||||
|
||||
@@ -15,6 +15,7 @@ type testCase struct {
|
||||
var testCases = []testCase{
|
||||
{
|
||||
config: config.AlertmanagerConfig{
|
||||
Cluster: "cluster",
|
||||
Name: "name",
|
||||
URI: "http://localhost:9093",
|
||||
ExternalURI: "http://localhost:9093",
|
||||
@@ -26,6 +27,7 @@ var testCases = []testCase{
|
||||
},
|
||||
{
|
||||
config: config.AlertmanagerConfig{
|
||||
Cluster: "cluster",
|
||||
Name: "proxy",
|
||||
URI: "http://localhost:9094",
|
||||
ExternalURI: "http://localhost:9094",
|
||||
@@ -37,6 +39,7 @@ var testCases = []testCase{
|
||||
},
|
||||
{
|
||||
config: config.AlertmanagerConfig{
|
||||
Cluster: "cluster",
|
||||
Name: "name",
|
||||
URI: "http://localhost:9095",
|
||||
ExternalURI: "http://localhost:9095",
|
||||
@@ -60,6 +63,7 @@ func TestOptions(t *testing.T) {
|
||||
}
|
||||
|
||||
am, err := NewAlertmanager(
|
||||
tc.config.Cluster,
|
||||
tc.config.Name,
|
||||
tc.config.URI,
|
||||
WithExternalURI(tc.config.ExternalURI),
|
||||
|
||||
@@ -34,3 +34,38 @@ func StringSliceToSHA1(stringArray []string) (string, error) {
|
||||
}
|
||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func StringSliceDiff(slice1 []string, slice2 []string) ([]string, []string) {
|
||||
missing := []string{}
|
||||
extra := []string{}
|
||||
|
||||
var found bool
|
||||
|
||||
for _, s1 := range slice1 {
|
||||
found = false
|
||||
for _, s2 := range slice2 {
|
||||
if s1 == s2 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
missing = append(missing, s1)
|
||||
}
|
||||
}
|
||||
|
||||
for _, s2 := range slice2 {
|
||||
found = false
|
||||
for _, s1 := range slice1 {
|
||||
if s2 == s1 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
extra = append(extra, s2)
|
||||
}
|
||||
}
|
||||
|
||||
return missing, extra
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/prymitive/karma/internal/slices"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
type stringSliceTest struct {
|
||||
@@ -121,3 +123,49 @@ func TestStringSliceToSHA1(t *testing.T) {
|
||||
t.Errorf("StringSliceToSHA1() returned empty string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceDiff(t *testing.T) {
|
||||
type testCaseT struct {
|
||||
a []string
|
||||
b []string
|
||||
missing []string
|
||||
extra []string
|
||||
}
|
||||
|
||||
testCases := []testCaseT{
|
||||
{
|
||||
a: []string{"a"},
|
||||
b: []string{"a"},
|
||||
missing: []string{},
|
||||
extra: []string{},
|
||||
},
|
||||
{
|
||||
a: []string{},
|
||||
b: []string{"a"},
|
||||
missing: []string{},
|
||||
extra: []string{"a"},
|
||||
},
|
||||
{
|
||||
a: []string{"a", "b"},
|
||||
b: []string{"a"},
|
||||
missing: []string{"b"},
|
||||
extra: []string{},
|
||||
},
|
||||
{
|
||||
a: []string{"a", "b"},
|
||||
b: []string{"c"},
|
||||
missing: []string{"a", "b"},
|
||||
extra: []string{"c"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
missing, extra := slices.StringSliceDiff(testCase.a, testCase.b)
|
||||
if diff := cmp.Diff(testCase.missing, missing); diff != "" {
|
||||
t.Errorf("Incorrect slice diff missing (-want +got):\n%s", diff)
|
||||
}
|
||||
if diff := cmp.Diff(testCase.extra, extra); diff != "" {
|
||||
t.Errorf("Incorrect slice diff extra (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user