feat(api): add an enpoint that returns all known label values for given label name

This will be used in the silence form, to provide a list of suggestions
This commit is contained in:
Łukasz Mierzwa
2018-08-05 20:55:45 +01:00
parent 29d05069f9
commit b3709927f7
4 changed files with 119 additions and 1 deletions

View File

@@ -61,3 +61,43 @@ func knownLabelNames(c *gin.Context) {
c.Data(http.StatusOK, gin.MIMEJSON, data.([]byte))
logAlertsView(c, "MIS", time.Since(start))
}
func knownLabelValues(c *gin.Context) {
noCache(c)
start := time.Now()
cacheKey := c.Request.RequestURI
if cacheKey == "" {
// FIXME c.Request.RequestURI is empty when running tests for some reason
// needs checking, below acts as a workaround
cacheKey = c.Request.URL.RawQuery
}
data, found := apiCache.Get(cacheKey)
if found {
c.Data(http.StatusOK, gin.MIMEJSON, data.([]byte))
logAlertsView(c, "HIT", time.Since(start))
return
}
name, found := c.GetQuery("name")
if !found || name == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "missing name=<token> parameter"})
log.Infof("[%s] <%d> %s %s took %s", c.ClientIP(), http.StatusBadRequest, c.Request.Method, c.Request.RequestURI, time.Since(start))
return
}
values := alertmanager.DedupKnownLabelValues(name)
sort.Strings(values)
data, err := json.Marshal(values)
if err != nil {
log.Error(err.Error())
panic(err)
}
apiCache.Set(cacheKey, data, time.Second*15)
c.Data(http.StatusOK, gin.MIMEJSON, data.([]byte))
logAlertsView(c, "MIS", time.Since(start))
}

View File

@@ -34,7 +34,7 @@ var labelTests = []labelTest{
},
}
func TestKnownLabels(t *testing.T) {
func TestKnownLabelNames(t *testing.T) {
mockConfig()
for _, version := range mock.ListAllMocks() {
t.Logf("Testing known labels using mock files from Alertmanager %s", version)
@@ -68,3 +68,58 @@ func TestKnownLabels(t *testing.T) {
}
}
}
type valueTest struct {
Name string
Results []string
}
var valueTests = []valueTest{
{
Name: "foobar",
Results: []string{},
},
{
Name: "alertname",
Results: []string{"Free_Disk_Space_Too_Low", "HTTP_Probe_Failed", "Host_Down", "Memory_Usage_Too_High"},
},
{
Name: "cluster",
Results: []string{"dev", "prod", "staging"},
},
}
func TestKnownLabelValues(t *testing.T) {
mockConfig()
for _, version := range mock.ListAllMocks() {
t.Logf("Testing known label values using mock files from Alertmanager %s", version)
mockAlerts(version)
r := ginTestEngine()
req, _ := http.NewRequest("GET", "/labelValues.json", nil)
resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)
if resp.Code != http.StatusBadRequest {
t.Errorf("Invalid status code for request without any query: %d", resp.Code)
}
for _, testCase := range valueTests {
url := fmt.Sprintf("/labelValues.json?name=%s", testCase.Name)
req, _ := http.NewRequest("GET", url, nil)
resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)
if resp.Code != http.StatusOK {
t.Errorf("GET %s returned status %d", url, resp.Code)
}
ur := []string{}
json.Unmarshal(resp.Body.Bytes(), &ur)
if len(ur) != len(testCase.Results) {
t.Errorf("Invalid number of label values for %s, got %d, expected %d", url, len(ur), len(testCase.Results))
t.Errorf("Results: %s", ur)
}
}
}
}

View File

@@ -180,3 +180,25 @@ func DedupKnownLabels() []string {
}
return flatLabels
}
// DedupKnownLabelValues returns a list of all known values for label $name
func DedupKnownLabelValues(name string) []string {
dedupedValues := map[string]bool{}
upstreams := GetAlertmanagers()
for _, am := range upstreams {
for _, ag := range am.Alerts() {
for _, alert := range ag.Alerts {
if val, found := alert.Labels[name]; found {
dedupedValues[val] = true
}
}
}
}
flatValues := []string{}
for key := range dedupedValues {
flatValues = append(flatValues, key)
}
return flatValues
}

View File

@@ -69,6 +69,7 @@ func setupRouter(router *gin.Engine) {
router.GET(getViewURL("/alerts.json"), alerts)
router.GET(getViewURL("/autocomplete.json"), autocomplete)
router.GET(getViewURL("/labelNames.json"), knownLabelNames)
router.GET(getViewURL("/labelValues.json"), knownLabelValues)
router.NoRoute(notFound)
}