mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
Merge pull request #177 from cloudflare/hide-annotations
Allow hiding annotations in the UI
This commit is contained in:
56
README.md
56
README.md
@@ -191,6 +191,62 @@ This option can also be set using `-alertmanager.uris` flag. Example:
|
||||
|
||||
This variable is required and there is no default value.
|
||||
|
||||
#### ANNOTATIONS_DEFAULT_HIDDEN
|
||||
|
||||
Enabling this option will hide all annotations in the UI, except for those
|
||||
that are listed in the `ANNOTATIONS_VISIBLE` option.
|
||||
|
||||
Examples:
|
||||
|
||||
ANNOTATIONS_DEFAULT_HIDDEN=true
|
||||
ANNOTATIONS_DEFAULT_HIDDEN=false
|
||||
|
||||
This option can also be set using `-annotations.default.hidden` flag. Example:
|
||||
|
||||
$ unsee -annotations.default.hidden
|
||||
|
||||
Default is `false`, which means that all annotations are visible.
|
||||
|
||||
#### ANNOTATIONS_HIDDEN
|
||||
|
||||
List of annotation names that should be hidden in the UI. Hidden annotations
|
||||
can still be accessed if needed by clicking on a zoom button that will appear
|
||||
if there are any hidden annotations.
|
||||
|
||||
Examples:
|
||||
|
||||
ANNOTATIONS_HIDDEN=summary
|
||||
ANNOTATIONS_HIDDEN="summary owner"
|
||||
|
||||
This option can also be set using `-annotations.hidden` flag. Example:
|
||||
|
||||
$ unsee -annotations.hidden "summary owner"
|
||||
|
||||
This variable is optional and default is not set (all annotations are visible),
|
||||
unless user enables `ANNOTATIONS_DEFAULT_HIDDEN` option.
|
||||
|
||||
#### ANNOTATIONS_VISIBLE
|
||||
|
||||
List of annotation names that should be visible in the UI. This option is only
|
||||
useful when `ANNOTATIONS_DEFAULT_HIDDEN` is set.
|
||||
With `ANNOTATIONS_DEFAULT_HIDDEN` all annotations are hidden by default unless
|
||||
they are present in the `ANNOTATIONS_VISIBLE` option.
|
||||
If `ANNOTATIONS_DEFAULT_HIDDEN` is not enabled this option is no-op.
|
||||
|
||||
Examples:
|
||||
|
||||
ANNOTATIONS_VISIBLE=summary
|
||||
ANNOTATIONS_VISIBLE="summary owner"
|
||||
|
||||
This option can also be set using `-annotations.visible` flag. Example:
|
||||
|
||||
$ unsee -annotations.visible "summary owner"
|
||||
|
||||
This variable is optional and default is not set.
|
||||
If `ANNOTATIONS_HIDDEN` is enabled then all annotations are hidden by default.
|
||||
If `ANNOTATIONS_HIDDEN` is not enabled then all annotations are visible by
|
||||
default.
|
||||
|
||||
#### DEBUG
|
||||
|
||||
Will enable [gin](https://github.com/gin-gonic/gin) debug mode. This will
|
||||
|
||||
152
api_test.go
152
api_test.go
@@ -29,8 +29,9 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"alert": "Memory usage exceeding threshold",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "alert", Value: "Memory usage exceeding threshold"},
|
||||
models.Annotation{Visible: true, Name: "dashboard", Value: "http://localhost/dashboard.html", IsLink: true},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Memory_Usage_Too_High",
|
||||
@@ -48,9 +49,6 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{
|
||||
"dashboard": "http://localhost/dashboard.html",
|
||||
},
|
||||
},
|
||||
},
|
||||
id: "099c5ca6d1c92f615b13056b935d0c8dee70f18c",
|
||||
@@ -69,8 +67,9 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"alert": "Memory usage exceeding threshold",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "alert", Value: "Memory usage exceeding threshold"},
|
||||
models.Annotation{Visible: true, Name: "dashboard", Value: "http://localhost/dashboard.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -86,9 +85,6 @@ var groupTests = []groupTest{
|
||||
"instance": "server2",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
Links: map[string]string{
|
||||
"dashboard": "http://localhost/dashboard.html",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
@@ -109,8 +105,8 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -126,13 +122,12 @@ var groupTests = []groupTest{
|
||||
"instance": "server3",
|
||||
"job": "node_ping",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -148,13 +143,12 @@ var groupTests = []groupTest{
|
||||
"instance": "server4",
|
||||
"job": "node_ping",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -170,7 +164,6 @@ var groupTests = []groupTest{
|
||||
"instance": "server5",
|
||||
"job": "node_ping",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
@@ -191,8 +184,8 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -214,13 +207,12 @@ var groupTests = []groupTest{
|
||||
"instance": "server6",
|
||||
"job": "node_ping",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -245,13 +237,12 @@ var groupTests = []groupTest{
|
||||
"instance": "server7",
|
||||
"job": "node_ping",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -272,7 +263,6 @@ var groupTests = []groupTest{
|
||||
"instance": "server8",
|
||||
"job": "node_ping",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
@@ -292,8 +282,9 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
models.Annotation{Visible: true, Name: "url", Value: "http://localhost/example.html", IsLink: true},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
@@ -311,13 +302,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{
|
||||
"url": "http://localhost/example.html",
|
||||
},
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
@@ -335,11 +323,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{},
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
@@ -357,11 +344,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{},
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
@@ -379,11 +365,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{},
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
@@ -401,11 +386,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{},
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
@@ -428,11 +412,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{},
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
@@ -459,11 +442,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{},
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"alertname": "Host_Down",
|
||||
@@ -486,7 +468,6 @@ var groupTests = []groupTest{
|
||||
},
|
||||
},
|
||||
Receiver: "by-name",
|
||||
Links: map[string]string{},
|
||||
},
|
||||
},
|
||||
id: "58c6a3467cebc53abe68ecbe8643ce478c5a1573",
|
||||
@@ -505,8 +486,9 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"alert": "Less than 10% disk space is free",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "alert", Value: "Less than 10% disk space is free"},
|
||||
models.Annotation{Visible: true, Name: "dashboard", Value: "http://localhost/dashboard.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -522,9 +504,6 @@ var groupTests = []groupTest{
|
||||
"instance": "server5",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
Links: map[string]string{
|
||||
"dashboard": "http://localhost/dashboard.html",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
@@ -545,8 +524,9 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
models.Annotation{Visible: true, Name: "url", Value: "http://localhost/example.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -562,15 +542,12 @@ var groupTests = []groupTest{
|
||||
"instance": "server1",
|
||||
"job": "node_ping",
|
||||
},
|
||||
Links: map[string]string{
|
||||
"url": "http://localhost/example.html",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -586,7 +563,6 @@ var groupTests = []groupTest{
|
||||
"instance": "server2",
|
||||
"job": "node_ping",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
@@ -606,9 +582,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
"help": "Example help annotation",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "help", Value: "Example help annotation"},
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
models.Annotation{Visible: true, Name: "url", Value: "http://localhost/example.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -629,15 +606,12 @@ var groupTests = []groupTest{
|
||||
"instance": "web1",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
Links: map[string]string{
|
||||
"url": "http://localhost/example.html",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-name",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -653,7 +627,6 @@ var groupTests = []groupTest{
|
||||
"instance": "web2",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-name",
|
||||
},
|
||||
@@ -673,8 +646,9 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"alert": "Less than 10% disk space is free",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "alert", Value: "Less than 10% disk space is free"},
|
||||
models.Annotation{Visible: true, Name: "dashboard", Value: "http://localhost/dashboard.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -690,9 +664,6 @@ var groupTests = []groupTest{
|
||||
"instance": "server5",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
Links: map[string]string{
|
||||
"dashboard": "http://localhost/dashboard.html",
|
||||
},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-name",
|
||||
},
|
||||
@@ -713,9 +684,10 @@ var groupTests = []groupTest{
|
||||
},
|
||||
alerts: []models.Alert{
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"help": "Example help annotation",
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "help", Value: "Example help annotation"},
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
models.Annotation{Visible: true, Name: "url", Value: "http://localhost/example.html", IsLink: true},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -736,15 +708,12 @@ var groupTests = []groupTest{
|
||||
"instance": "web1",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
Links: map[string]string{
|
||||
"url": "http://localhost/example.html",
|
||||
},
|
||||
State: models.AlertStateSuppressed,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"summary": "Example summary",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Visible: true, Name: "summary", Value: "Example summary"},
|
||||
},
|
||||
Alertmanager: []models.AlertmanagerInstance{
|
||||
models.AlertmanagerInstance{
|
||||
@@ -760,7 +729,6 @@ var groupTests = []groupTest{
|
||||
"instance": "web2",
|
||||
"job": "node_exporter",
|
||||
},
|
||||
Links: map[string]string{},
|
||||
State: models.AlertStateActive,
|
||||
Receiver: "by-cluster-service",
|
||||
},
|
||||
@@ -875,10 +843,6 @@ func testAlert(version string, t *testing.T, expectedAlert, gotAlert models.Aler
|
||||
t.Errorf("[%s] Labels mismatch on alert receiver='%s', expected labels=%v but got %v",
|
||||
version, expectedAlert.Receiver, expectedAlert.Labels, gotAlert.Labels)
|
||||
}
|
||||
if !reflect.DeepEqual(gotAlert.Links, expectedAlert.Links) {
|
||||
t.Errorf("[%s] Links mismatch on alert receiver='%s' labels=%v expected %v but got %v",
|
||||
version, expectedAlert.Receiver, expectedAlert.Labels, expectedAlert.Links, gotAlert.Links)
|
||||
}
|
||||
if len(gotAlert.Alertmanager) != len(expectedAlert.Alertmanager) {
|
||||
t.Errorf("[%s] Expected %d alertmanager instances but got %d on alert receiver='%s' labels=%v",
|
||||
version, len(expectedAlert.Alertmanager), len(gotAlert.Alertmanager), gotAlert.Receiver, expectedAlert.Labels)
|
||||
|
||||
@@ -33,6 +33,7 @@ AlertGroup.prototype.Added = function() {
|
||||
var elem = $("#" + this.id);
|
||||
ui.setupGroupTooltips(elem);
|
||||
ui.setupGroupLinkHover(elem);
|
||||
ui.setupGroupAnnotationToggles(elem);
|
||||
};
|
||||
|
||||
AlertGroup.prototype.Update = function() {
|
||||
|
||||
@@ -36,8 +36,9 @@ var templates = {},
|
||||
silenceFormFatal: "#silence-form-fatal",
|
||||
silenceFormLoading: "#silence-form-loading",
|
||||
|
||||
// label button
|
||||
// alert partials
|
||||
buttonLabel: "#label-button-filter",
|
||||
alertAnnotation: "#alert-annotation",
|
||||
|
||||
// alert group
|
||||
alertGroup: "#alert-group",
|
||||
|
||||
@@ -5,6 +5,7 @@ const $ = require("jquery");
|
||||
const alerts = require("./alerts");
|
||||
const autocomplete = require("./autocomplete");
|
||||
const filters = require("./filters");
|
||||
const grid = require("./grid");
|
||||
const summary = require("./summary");
|
||||
const templates = require("./templates");
|
||||
const unsee = require("./unsee");
|
||||
@@ -75,6 +76,29 @@ function setupGroupTooltips(groupElem) {
|
||||
});
|
||||
}
|
||||
|
||||
function setupGroupAnnotationToggles(groupElem) {
|
||||
$(groupElem).on("click", "[data-toggle=toggle-hidden-annotation]", function() {
|
||||
var alert = $(this).parent();
|
||||
var icon = $(this).find("i.fa");
|
||||
var showingHidden = icon.hasClass("fa-search-minus");
|
||||
if (showingHidden) {
|
||||
// we're currently showing hidden annotations, so the action is to hide them
|
||||
icon.removeClass("fa-search-minus").addClass("fa-search-plus");
|
||||
$.each(alert.find(".hidden-annotation"), function(i, annotation){
|
||||
$(annotation).addClass("hidden");
|
||||
});
|
||||
} else {
|
||||
// we're currently hiding hidden annotations, so the action is to show them
|
||||
icon.removeClass("fa-search-plus").addClass("fa-search-minus");
|
||||
$.each(alert.find(".hidden-annotation"), function(i, annotation){
|
||||
$(annotation).removeClass("hidden");
|
||||
});
|
||||
}
|
||||
grid.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
exports.setupModal = setupModal;
|
||||
exports.setupGroupTooltips = setupGroupTooltips;
|
||||
exports.setupGroupLinkHover = setupGroupLinkHover;
|
||||
exports.setupGroupAnnotationToggles = setupGroupAnnotationToggles;
|
||||
|
||||
@@ -27,28 +27,32 @@
|
||||
</script>
|
||||
|
||||
<script type="application/json" id="alert-group-annotations">
|
||||
<% _.each(sortMapByKey(alert.annotations), function(annotation) { %>
|
||||
<div class="well well-sm annotation-well">
|
||||
<i class="fa fa-info-circle text-muted" title="<%- annotation.key %>" data-toggle="tooltip" data-placement="top"/>
|
||||
<% if (annotation.value) { %>
|
||||
<%= linkify(_.escape(annotation.value)) %>
|
||||
<% } else { %>
|
||||
<span class="text-muted">
|
||||
[ missing annotation value ]
|
||||
</span>
|
||||
<% } %>
|
||||
</div>
|
||||
<% var hiddenCount = 0 %>
|
||||
<% _.each(alert.annotations, function(annotation) { %>
|
||||
<% if (annotation.isLink === false) { %>
|
||||
<% if (annotation.visible === false) { hiddenCount++ } %>
|
||||
<%= renderTemplate('alertAnnotation', {annotation: annotation}) %>
|
||||
<% } %>
|
||||
<% }) %>
|
||||
<% _.each(alert.links, function(url, text) { %>
|
||||
<a class="label label-list label-default"
|
||||
href="<%= url %>"
|
||||
target="_blank"
|
||||
title="<%= url %>"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top">
|
||||
<i class="fa fa-external-link"/>
|
||||
<%- text %>
|
||||
</a>
|
||||
<% if (hiddenCount) { %>
|
||||
<span class="label label-default label-list cursor-pointer"
|
||||
data-toggle="toggle-hidden-annotation"
|
||||
type="button">
|
||||
<i class="fa fa-search-plus" title="Toggle hidden annotations" data-toggle="tooltip" data-placement="top" />
|
||||
</span>
|
||||
<% } %>
|
||||
<% _.each(alert.annotations, function(annotation) { %>
|
||||
<% if (annotation.isLink) { %>
|
||||
<a class="label label-list label-default"
|
||||
href="<%= annotation.value %>"
|
||||
target="_blank"
|
||||
title="<%= annotation.value %>"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top">
|
||||
<i class="fa fa-external-link"/>
|
||||
<%- annotation.name %>
|
||||
</a>
|
||||
<% } %>
|
||||
<% }) %>
|
||||
</script>
|
||||
|
||||
@@ -308,3 +312,14 @@
|
||||
<%- label.text %>
|
||||
</<%= elem %>>
|
||||
</script>
|
||||
|
||||
<script type="application/json" id="alert-annotation">
|
||||
<% var cls = "" %>
|
||||
<% if (annotation.visible === false) { %>
|
||||
<% cls = "hidden hidden-annotation" %>
|
||||
<% } %>
|
||||
<div class="well well-sm annotation-well <%- cls %>">
|
||||
<i class="fa fa-info-circle text-muted" title="<%- annotation.name %>" data-toggle="tooltip" data-placement="top"/>
|
||||
<%= linkify(_.escape(annotation.value)) %>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
@@ -172,8 +172,6 @@ func (am *Alertmanager) pullAlerts(version string) error {
|
||||
},
|
||||
}
|
||||
|
||||
alert.Annotations, alert.Links = transform.DetectLinks(alert.Annotations)
|
||||
|
||||
transform.ColorLabel(colors, "@receiver", alert.Receiver)
|
||||
for k, v := range alert.Labels {
|
||||
transform.ColorLabel(colors, k, v)
|
||||
|
||||
@@ -24,20 +24,23 @@ func (mvd *spaceSeparatedList) Decode(value string) error {
|
||||
}
|
||||
|
||||
type configEnvs struct {
|
||||
AlertmanagerTimeout time.Duration `envconfig:"ALERTMANAGER_TIMEOUT" default:"40s" help:"Timeout for all request send to Alertmanager"`
|
||||
AlertmanagerTTL time.Duration `envconfig:"ALERTMANAGER_TTL" default:"1m" help:"TTL for Alertmanager alerts and silences"`
|
||||
AlertmanagerURIs spaceSeparatedList `envconfig:"ALERTMANAGER_URIS" required:"true" help:"List of Alertmanager URIs (name:uri)"`
|
||||
ColorLabelsStatic spaceSeparatedList `envconfig:"COLOR_LABELS_STATIC" help:"List of label names that should have the same (but distinct) color"`
|
||||
ColorLabelsUnique spaceSeparatedList `envconfig:"COLOR_LABELS_UNIQUE" help:"List of label names that should have unique color"`
|
||||
Debug bool `envconfig:"DEBUG" default:"false" help:"Enable debug mode"`
|
||||
FilterDefault string `envconfig:"FILTER_DEFAULT" help:"Default filter string"`
|
||||
JiraRegexp spaceSeparatedList `envconfig:"JIRA_REGEX" help:"List of JIRA regex rules"`
|
||||
Port int `envconfig:"PORT" default:"8080" help:"HTTP port to listen on"`
|
||||
SentryDSN string `envconfig:"SENTRY_DSN" help:"Sentry DSN for Go exceptions"`
|
||||
SentryPublicDSN string `envconfig:"SENTRY_PUBLIC_DSN" help:"Sentry DSN for javascript exceptions"`
|
||||
StripLabels spaceSeparatedList `envconfig:"STRIP_LABELS" help:"List of labels to ignore"`
|
||||
KeepLabels spaceSeparatedList `envconfig:"KEEP_LABELS" help:"List of labels to keep, all other labels will be stripped"`
|
||||
WebPrefix string `envconfig:"WEB_PREFIX" default:"/" help:"URL prefix"`
|
||||
AlertmanagerTimeout time.Duration `envconfig:"ALERTMANAGER_TIMEOUT" default:"40s" help:"Timeout for all request send to Alertmanager"`
|
||||
AlertmanagerTTL time.Duration `envconfig:"ALERTMANAGER_TTL" default:"1m" help:"TTL for Alertmanager alerts and silences"`
|
||||
AlertmanagerURIs spaceSeparatedList `envconfig:"ALERTMANAGER_URIS" required:"true" help:"List of Alertmanager URIs (name:uri)"`
|
||||
AnnotationsHidden spaceSeparatedList `envconfig:"ANNOTATIONS_HIDDEN" help:"List of annotations that are hidden by default"`
|
||||
AnnotationsDefaultHidden bool `envconfig:"ANNOTATIONS_DEFAULT_HIDDEN" default:"false" help:"Hide all annotations by default unless listed in ANNOTATIONS_VISIBLE"`
|
||||
AnnotationsVisible spaceSeparatedList `envconfig:"ANNOTATIONS_VISIBLE" help:"List of annotations that are visible by default"`
|
||||
ColorLabelsStatic spaceSeparatedList `envconfig:"COLOR_LABELS_STATIC" help:"List of label names that should have the same (but distinct) color"`
|
||||
ColorLabelsUnique spaceSeparatedList `envconfig:"COLOR_LABELS_UNIQUE" help:"List of label names that should have unique color"`
|
||||
Debug bool `envconfig:"DEBUG" default:"false" help:"Enable debug mode"`
|
||||
FilterDefault string `envconfig:"FILTER_DEFAULT" help:"Default filter string"`
|
||||
JiraRegexp spaceSeparatedList `envconfig:"JIRA_REGEX" help:"List of JIRA regex rules"`
|
||||
Port int `envconfig:"PORT" default:"8080" help:"HTTP port to listen on"`
|
||||
SentryDSN string `envconfig:"SENTRY_DSN" help:"Sentry DSN for Go exceptions"`
|
||||
SentryPublicDSN string `envconfig:"SENTRY_PUBLIC_DSN" help:"Sentry DSN for javascript exceptions"`
|
||||
StripLabels spaceSeparatedList `envconfig:"STRIP_LABELS" help:"List of labels to ignore"`
|
||||
KeepLabels spaceSeparatedList `envconfig:"KEEP_LABELS" help:"List of labels to keep, all other labels will be stripped"`
|
||||
WebPrefix string `envconfig:"WEB_PREFIX" default:"/" help:"URL prefix"`
|
||||
}
|
||||
|
||||
// Config exposes all options required to run
|
||||
|
||||
@@ -27,7 +27,7 @@ func (filter *fuzzyFilter) init(name string, matcher *matcherT, rawText string,
|
||||
func (filter *fuzzyFilter) Match(alert *models.Alert, matches int) bool {
|
||||
if filter.IsValid {
|
||||
for _, val := range alert.Annotations {
|
||||
if filter.Matcher.Compare(val, filter.Value) {
|
||||
if filter.Matcher.Compare(val.Value, filter.Value) {
|
||||
filter.Hits++
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -355,20 +355,32 @@ var tests = []filterTest{
|
||||
filterTest{
|
||||
Expression: "abc",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{Annotations: map[string]string{"key": "abc"}},
|
||||
IsMatch: true,
|
||||
Alert: models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Name: "key", Value: "abc"},
|
||||
},
|
||||
},
|
||||
IsMatch: true,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "abc",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{Annotations: map[string]string{"key": "ccc abc"}},
|
||||
IsMatch: true,
|
||||
Alert: models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Name: "key", Value: "ccc abc"},
|
||||
},
|
||||
},
|
||||
IsMatch: true,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "abc",
|
||||
IsValid: true,
|
||||
Alert: models.Alert{Annotations: map[string]string{"abc": "zzz"}},
|
||||
IsMatch: false,
|
||||
Alert: models.Alert{
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{Name: "abc", Value: "zzz"},
|
||||
},
|
||||
},
|
||||
IsMatch: false,
|
||||
},
|
||||
filterTest{
|
||||
Expression: "abc",
|
||||
|
||||
@@ -103,7 +103,7 @@ func (m AlertMapper) GetAlerts(uri string, timeout time.Duration) ([]models.Aler
|
||||
}
|
||||
a := models.Alert{
|
||||
Receiver: rcv.Name,
|
||||
Annotations: a.Annotations,
|
||||
Annotations: models.AnnotationsFromMap(a.Annotations),
|
||||
Labels: a.Labels,
|
||||
StartsAt: a.StartsAt,
|
||||
EndsAt: a.EndsAt,
|
||||
|
||||
@@ -102,7 +102,7 @@ func (m AlertMapper) GetAlerts(uri string, timeout time.Duration) ([]models.Aler
|
||||
}
|
||||
a := models.Alert{
|
||||
Receiver: rcv.Name,
|
||||
Annotations: a.Annotations,
|
||||
Annotations: models.AnnotationsFromMap(a.Annotations),
|
||||
Labels: a.Labels,
|
||||
StartsAt: a.StartsAt,
|
||||
EndsAt: a.EndsAt,
|
||||
|
||||
@@ -101,7 +101,7 @@ func (m AlertMapper) GetAlerts(uri string, timeout time.Duration) ([]models.Aler
|
||||
}
|
||||
a := models.Alert{
|
||||
Receiver: rcv.Name,
|
||||
Annotations: a.Annotations,
|
||||
Annotations: models.AnnotationsFromMap(a.Annotations),
|
||||
Labels: a.Labels,
|
||||
StartsAt: a.StartsAt,
|
||||
EndsAt: a.EndsAt,
|
||||
|
||||
@@ -105,7 +105,7 @@ func (m AlertMapper) GetAlerts(uri string, timeout time.Duration) ([]models.Aler
|
||||
}
|
||||
a := models.Alert{
|
||||
Receiver: rcv.Name,
|
||||
Annotations: a.Annotations,
|
||||
Annotations: models.AnnotationsFromMap(a.Annotations),
|
||||
Labels: a.Labels,
|
||||
StartsAt: a.StartsAt,
|
||||
EndsAt: a.EndsAt,
|
||||
|
||||
@@ -30,7 +30,7 @@ var AlertStateList = []string{
|
||||
// it's pulled out of annotation map and returned under links field,
|
||||
// unsee UI used this to show links differently than other annotations
|
||||
type Alert struct {
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
Annotations Annotations `json:"annotations"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
StartsAt time.Time `json:"startsAt"`
|
||||
EndsAt time.Time `json:"endsAt"`
|
||||
@@ -44,7 +44,6 @@ type Alert struct {
|
||||
// unsee fields
|
||||
Alertmanager []AlertmanagerInstance `json:"alertmanager"`
|
||||
Receiver string `json:"receiver"`
|
||||
Links map[string]string `json:"links"`
|
||||
// fingerprints are precomputed for speed
|
||||
labelsFP string `hash:"-"`
|
||||
contentFP string `hash:"-"`
|
||||
|
||||
@@ -72,9 +72,17 @@ func BenchmarkLabelsFingerprint(b *testing.B) {
|
||||
|
||||
func BenchmarkLabelsContent(b *testing.B) {
|
||||
alert := models.Alert{
|
||||
Annotations: map[string]string{
|
||||
"foo": "bar",
|
||||
"abc": "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...",
|
||||
Annotations: models.Annotations{
|
||||
models.Annotation{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
Visible: true,
|
||||
},
|
||||
models.Annotation{
|
||||
Name: "abc",
|
||||
Value: "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...",
|
||||
Visible: true,
|
||||
},
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"foo1": "bar1",
|
||||
|
||||
84
internal/models/annotation.go
Normal file
84
internal/models/annotation.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"sort"
|
||||
|
||||
"github.com/cloudflare/unsee/internal/config"
|
||||
"github.com/cloudflare/unsee/internal/slices"
|
||||
)
|
||||
|
||||
// Annotation extends Alertmanager scheme of key:value with additional data
|
||||
// to control how given annotation should be rendered
|
||||
type Annotation struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Visible bool `json:"visible"`
|
||||
IsLink bool `json:"isLink"`
|
||||
}
|
||||
|
||||
// Annotations is a slice of Annotation structs, needed to implement sorting
|
||||
type Annotations []Annotation
|
||||
|
||||
func (a Annotations) Len() int {
|
||||
return len(a)
|
||||
|
||||
}
|
||||
func (a Annotations) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
func (a Annotations) Less(i, j int) bool {
|
||||
return a[i].Name < a[j].Name
|
||||
}
|
||||
|
||||
// AnnotationsFromMap will convert a map[string]string to a list of Annotation
|
||||
// instances, it takes care of setting proper value for Visible attribute
|
||||
func AnnotationsFromMap(m map[string]string) Annotations {
|
||||
annotations := Annotations{}
|
||||
for name, value := range m {
|
||||
a := Annotation{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Visible: isVisible(name),
|
||||
IsLink: isLink(value),
|
||||
}
|
||||
annotations = append(annotations, a)
|
||||
}
|
||||
sort.Sort(annotations)
|
||||
return annotations
|
||||
}
|
||||
|
||||
var linkSchemes = []string{
|
||||
"ftp",
|
||||
"http",
|
||||
"https",
|
||||
}
|
||||
|
||||
func isLink(s string) bool {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if slices.StringInSlice(linkSchemes, u.Scheme) {
|
||||
// parses with url.Parse and scheme is in the list of supported schemes
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isVisible(name string) bool {
|
||||
if slices.StringInSlice(config.Config.AnnotationsVisible, name) {
|
||||
// annotation was explicitly marked as visible
|
||||
return true
|
||||
}
|
||||
if slices.StringInSlice(config.Config.AnnotationsHidden, name) {
|
||||
// annotation was explicitly marked as hidden
|
||||
return false
|
||||
}
|
||||
if config.Config.AnnotationsDefaultHidden {
|
||||
// user specified that default is to hide anything without explicit rules
|
||||
return false
|
||||
}
|
||||
// default to show everything
|
||||
return true
|
||||
}
|
||||
85
internal/models/annotation_test.go
Normal file
85
internal/models/annotation_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package models_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudflare/unsee/internal/models"
|
||||
)
|
||||
|
||||
type annotationMapsTestCase struct {
|
||||
annotationMap map[string]string
|
||||
annotations models.Annotations
|
||||
}
|
||||
|
||||
var annotationMapsTestCases = []annotationMapsTestCase{
|
||||
annotationMapsTestCase{
|
||||
annotationMap: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
annotations: models.Annotations{
|
||||
models.Annotation{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
Visible: true,
|
||||
IsLink: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
annotationMapsTestCase{
|
||||
annotationMap: map[string]string{
|
||||
"foo": "http://localhost",
|
||||
},
|
||||
annotations: models.Annotations{
|
||||
models.Annotation{
|
||||
Name: "foo",
|
||||
Value: "http://localhost",
|
||||
Visible: true,
|
||||
IsLink: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
annotationMapsTestCase{
|
||||
annotationMap: map[string]string{
|
||||
"foo": "ftp://localhost",
|
||||
},
|
||||
annotations: models.Annotations{
|
||||
models.Annotation{
|
||||
Name: "foo",
|
||||
Value: "ftp://localhost",
|
||||
Visible: true,
|
||||
IsLink: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
annotationMapsTestCase{
|
||||
annotationMap: map[string]string{
|
||||
"foo": "https://localhost/xxx",
|
||||
"abc": "xyz",
|
||||
},
|
||||
annotations: models.Annotations{
|
||||
models.Annotation{
|
||||
Name: "abc",
|
||||
Value: "xyz",
|
||||
Visible: true,
|
||||
IsLink: false,
|
||||
},
|
||||
models.Annotation{
|
||||
Name: "foo",
|
||||
Value: "https://localhost/xxx",
|
||||
Visible: true,
|
||||
IsLink: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestAnnotationsFromMap(t *testing.T) {
|
||||
for _, testCase := range annotationMapsTestCases {
|
||||
result := models.AnnotationsFromMap(testCase.annotationMap)
|
||||
if !reflect.DeepEqual(testCase.annotations, result) {
|
||||
t.Errorf("AnnotationsFromMap result mismatch for map %v, expected %v got %v",
|
||||
testCase.annotationMap, testCase.annotations, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package transform
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/cloudflare/unsee/internal/slices"
|
||||
)
|
||||
|
||||
// list of URI schema which we turn into links in the UI
|
||||
var schemes = []string{
|
||||
"ftp",
|
||||
"http",
|
||||
"https",
|
||||
}
|
||||
|
||||
// DetectLinks takes alert annotation dict and returns two dicts:
|
||||
// first with regular annotations
|
||||
// secondd with annotations where values are URLs
|
||||
func DetectLinks(sourceAnnotations map[string]string) (map[string]string, map[string]string) {
|
||||
links := make(map[string]string)
|
||||
annotations := make(map[string]string)
|
||||
|
||||
for k, v := range sourceAnnotations {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
annotations[k] = v
|
||||
} else if slices.StringInSlice(schemes, u.Scheme) {
|
||||
links[k] = v
|
||||
} else {
|
||||
annotations[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return annotations, links
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package transform_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudflare/unsee/internal/transform"
|
||||
)
|
||||
|
||||
type linkTest struct {
|
||||
before map[string]string
|
||||
after map[string]string
|
||||
links map[string]string
|
||||
}
|
||||
|
||||
var linkTests = []linkTest{
|
||||
linkTest{
|
||||
before: map[string]string{},
|
||||
after: map[string]string{},
|
||||
links: map[string]string{},
|
||||
},
|
||||
linkTest{
|
||||
before: map[string]string{
|
||||
"key1": "value 1",
|
||||
"key2": "value2",
|
||||
"level": "info",
|
||||
},
|
||||
after: map[string]string{
|
||||
"key1": "value 1",
|
||||
"key2": "value2",
|
||||
"level": "info",
|
||||
},
|
||||
links: map[string]string{},
|
||||
},
|
||||
linkTest{
|
||||
before: map[string]string{
|
||||
"key1": "value 1",
|
||||
"key2": "http://localhost",
|
||||
"level": "info",
|
||||
},
|
||||
after: map[string]string{
|
||||
"key1": "value 1",
|
||||
"level": "info",
|
||||
},
|
||||
links: map[string]string{
|
||||
"key2": "http://localhost",
|
||||
},
|
||||
},
|
||||
linkTest{
|
||||
before: map[string]string{
|
||||
"key1": "value 1",
|
||||
"key2": "https://example.com/abc",
|
||||
"level": "info",
|
||||
},
|
||||
after: map[string]string{
|
||||
"key1": "value 1",
|
||||
"level": "info",
|
||||
},
|
||||
links: map[string]string{
|
||||
"key2": "https://example.com/abc",
|
||||
},
|
||||
},
|
||||
linkTest{
|
||||
before: map[string]string{
|
||||
"key1": "value 1",
|
||||
"key2": "file://example/abc",
|
||||
"level": "info",
|
||||
},
|
||||
after: map[string]string{
|
||||
"key1": "value 1",
|
||||
"key2": "file://example/abc",
|
||||
"level": "info",
|
||||
},
|
||||
links: map[string]string{},
|
||||
},
|
||||
linkTest{
|
||||
before: map[string]string{
|
||||
"key1": "value 1",
|
||||
"key2": "ftp://example/abc",
|
||||
"level": "info",
|
||||
},
|
||||
after: map[string]string{
|
||||
"key1": "value 1",
|
||||
"level": "info",
|
||||
},
|
||||
links: map[string]string{
|
||||
"key2": "ftp://example/abc",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestDetectLinks(t *testing.T) {
|
||||
for _, testCase := range linkTests {
|
||||
after, links := transform.DetectLinks(testCase.before)
|
||||
if !reflect.DeepEqual(after, testCase.after) {
|
||||
t.Errorf("DetectLinks returned invalid annotation map, expected %v, got %v", testCase.after, after)
|
||||
}
|
||||
if !reflect.DeepEqual(links, testCase.links) {
|
||||
t.Errorf("DetectLinks returned invalid link map, expected %v, got %v", testCase.links, links)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,8 +163,14 @@ func TestAlerts(t *testing.T) {
|
||||
}
|
||||
for _, ag := range ur.AlertGroups {
|
||||
for _, a := range ag.Alerts {
|
||||
if len(a.Links) != 1 {
|
||||
t.Errorf("Invalid number of links, got %d, expected 1, %v", len(a.Links), a)
|
||||
linkCount := 0
|
||||
for _, annotation := range a.Annotations {
|
||||
if annotation.IsLink {
|
||||
linkCount++
|
||||
}
|
||||
}
|
||||
if linkCount != 1 {
|
||||
t.Errorf("Invalid number of links, got %d, expected 1, %v", linkCount, a)
|
||||
}
|
||||
if len(a.Alertmanager) == 0 {
|
||||
t.Errorf("Alertmanager instance list is empty, %v", a)
|
||||
|
||||
Reference in New Issue
Block a user