Update silence form to work with multiple Alertmanager instances

This commit is contained in:
Łukasz Mierzwa
2017-06-27 19:35:57 -07:00
parent c5724bb751
commit 721b4f8be4
6 changed files with 103 additions and 60 deletions

View File

@@ -27,7 +27,7 @@ var Silence = (function() {
if ($("#endsAt").data("DateTimePicker")) {
payload.endsAt = $("#endsAt").data("DateTimePicker").date();
}
$.each($("#newSilenceForm .selectpicker"), function(i, elem) {
$.each($("#newSilenceForm select.silence-label-picker"), function(i, elem) {
var labelKey = $(elem).data("label-key");
var values = $(elem).selectpicker("val");
if (values && values.length > 0) {
@@ -74,8 +74,12 @@ var Silence = (function() {
$("#silence-end-description").html(endsAtDesc);
};
var silenceFormAlertmanagerURL = function() {
return $("#newSilenceForm .silence-alertmanager-picker").selectpicker("val");
};
var silenceFormJSONRender = function() {
var d = "curl " + $("#silenceModal").data("silence-api") +
var d = "curl $AlertmanagerURI" +
"\n -X POST --data " +
JSON.stringify(silenceFormData(), undefined, 2);
$("#silenceJSONBlob").html(d);
@@ -127,6 +131,47 @@ var Silence = (function() {
silenceFormCalculateDuration();
};
var sendSilencePOST = function(url, payload) {
$.ajax({
type: "POST",
url: url,
data: JSON.stringify(payload),
error: function(xhr, textStatus, errorThrown) {
// default to whatever error text we can get
var err = xhr.responseText || errorThrown || textStatus;
if (xhr.responseText) {
// if we have a reponse text try to decode it as JSON
// it should be error from Alertmanager (it we were able to connect)
try {
var j = JSON.parse(xhr.responseText);
if (j.error !== undefined) {
err = j.error;
}
} catch (error) {
// can't parse json, do nothing
}
}
var errContent = Templates.Render("silenceFormError", {error: err});
$("#newSilenceAlert").html(errContent).removeClass("hidden");
},
success: function(data) {
// FIXME this is per instance now, so needs to be handled differently
if (data.status == "success") {
$("#newSilenceAlert").addClass("hidden");
$("#newSilenceForm").html(Templates.Render("silenceFormSuccess", {
silenceID: data.data.silenceId
}));
} else {
var err = "Invalid response from Alertmanager API: " + JSON.stringify(data);
var errContent = Templates.Render("silenceFormError", {error: err});
$("#newSilenceAlert").html(errContent).removeClass("hidden");
}
},
dataType: "json"
});
};
// modal form for creating new silences
var setupSilenceForm = function() {
var modal = $("#silenceModal");
@@ -151,6 +196,7 @@ var Silence = (function() {
success: function(data) {
var modal = $("#silenceModal");
var labels = {};
var alertmanagers = {};
$.each(data.groups, function(i, group) {
$.each(group.alerts, function(j, alert) {
$.each(alert.labels, function(labelKey, labelVal) {
@@ -166,21 +212,24 @@ var Silence = (function() {
};
}
});
$.each(alert.alertmanager, function(i, alertmanager){
alertmanagers[alertmanager.name] = alertmanager;
});
});
});
modal.find(".modal-body").html(
Templates.Render("silenceForm", {labels: labels})
Templates.Render("silenceForm", {
labels: labels,
alertmanagers: alertmanagers,
selectedAlertmanagers: elem.data("alertmanagers").split(",")
})
);
$.each($(".selectpicker"), function(i, elem) {
$.each($(".silence-alertmanager-picker"), function(i, elem) {
$(elem).selectpicker();
});
$.each($(".silence-label-picker"), function(i, elem) {
$(elem).selectpicker({
iconBase: "fa",
tickIcon: "fa-check",
width: "fit",
selectAllText: "<i class='fa fa-check-square-o'></i>",
deselectAllText: "<i class='fa fa-square-o'></i>",
noneSelectedText: "<span class='label label-list label-default'>" + $(this).data("label-key") + ": </span>",
multipleSeparator: " ",
selectedTextFormat: "count > 1",
countSelectedText: function (numSelected) {
return "<span class='label label-list label-warning'>" +
$(elem).data("label-key") + ": " + numSelected + " values selected</span>";
@@ -244,43 +293,8 @@ var Silence = (function() {
return false;
}
var url = modal.data("silence-api");
$.ajax({
type: "POST",
url: url,
data: JSON.stringify(payload),
error: function(xhr, textStatus, errorThrown) {
// default to whatever error text we can get
var err = xhr.responseText || errorThrown || textStatus;
if (xhr.responseText) {
// if we have a reponse text try to decode it as JSON
// it should be error from Alertmanager (it we were able to connect)
try {
var j = JSON.parse(xhr.responseText);
if (j.error !== undefined) {
err = j.error;
}
} catch (error) {
// can't parse json, do nothing
}
}
var errContent = Templates.Render("silenceFormError", {error: err});
$("#newSilenceAlert").html(errContent).removeClass("hidden");
},
success: function(data) {
if (data.status == "success") {
$("#newSilenceAlert").addClass("hidden");
$("#newSilenceForm").html(Templates.Render("silenceFormSuccess", {
silenceID: data.data.silenceId
}));
} else {
var err = "Invalid response from Alertmanager API: " + JSON.stringify(data);
var errContent = Templates.Render("silenceFormError", {error: err});
$("#newSilenceAlert").html(errContent).removeClass("hidden");
}
},
dataType: "json"
$.each(silenceFormAlertmanagerURL(), function(i, uri){
sendSilencePOST(uri, payload);
});
event.preventDefault();

View File

@@ -79,12 +79,18 @@
<%= Templates.Render('buttonLabel', {elem: 'span', attrs: attrs, label: {key: '@state', value: alert.state, text: '@state: ' + alert.state}}) %>
<% if (alert.silencedBy.length == 0) { %>
<% var labels = [] %>
<% var alertmanagers = [] %>
<% _.each(Alerts.SortMapByKey(alert.labels), function(label) { %>
<% labels.push(label.key + '=' + label.value) %>
<% }) %>
<% _.each(alert.alertmanager, function(alertmanager){ %>
<% alertmanagers.push(alertmanager.name) %>
<% }) %>
<% alertmanagers.sort() %>
<span class="label label-list label-success cursor-pointer"
type="button"
data-labels="<%= labels.join(',') %>"
data-alertmanagers="<%= alertmanagers.join(',') %>"
data-alertname="<%= alert.labels.alertname %>"
data-toggle="modal"
data-target="#silenceModal">

View File

@@ -162,7 +162,7 @@
</div>
</div>
<div class="modal fade" id="silenceModal" tabindex="-1" role="dialog" data-silence-api="{{ .SilencesApi }}">
<div class="modal fade" id="silenceModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header text-center">

View File

@@ -1,6 +1,29 @@
<script type="application/json" id="silence-form">
<div id="newSilenceAlert" class="alert alert-danger hidden" role="alert"></div>
<form id="newSilenceForm">
<div>
<label class="control-label">Alertmanager</label>
<select class="selectpicker silence-alertmanager-picker"
data-style="silence-label-select"
data-icon-base="fa"
data-tick-icon="fa-check"
data-width="fit"
data-select-all-text="<i class='fa fa-check-square-o'></i>"
data-deselect-all-text="<i class='fa fa-square-o'></i>"
data-multiple-separator=" "
<% if (Object.keys(alertmanagers).length > 1) { %>data-actions-box="true"<% } %>
multiple
required>
<% _.each(alertmanagers, function(am) { %>
<option <% if (_.contains(selectedAlertmanagers, am.name)) { %>selected="selected"<% } %>
value="<%= am.uri %>"
data-content="<span class='label label-list label-primary'><%- am.name %></span>">
<%- am.name %>
</option>
<% }) %>
</select>
</div>
<label class="control-label">Labels to match</label>
<table class="table table-condensed silence-label-selects">
<% if (Object.keys(labels).length > 0) { %>
@@ -15,6 +38,13 @@
<select class="selectpicker silence-label-picker"
data-label-key="<%= label.key %>"
data-style="silence-label-select"
data-icon-base="fa"
data-tick-icon="fa-check"
data-width="fit"
data-select-all-text="<i class='fa fa-check-square-o'></i>"
data-deselect-all-text="<i class='fa fa-square-o'></i>"
data-multiple-separator=" "
data-selected-text-format="count > 1"
<% if (Object.keys(label.value).length > 10) { %>data-live-search="true"<% } %>
<% if (Object.keys(label.value).length > 1) { %>data-actions-box="true"<% } %>
multiple>

File diff suppressed because one or more lines are too long

View File

@@ -39,12 +39,6 @@ func index(c *gin.Context) {
defaultUsed = false
}
// FIXME
//silencesAPI, err := transport.JoinURL(config.Config.AlertmanagerURI, "api/v1/silences")
//if err != nil {
// log.Errorf("Can't generate silences API URL: %s", err)
//}
c.HTML(http.StatusOK, "templates/index.html", gin.H{
"Version": version,
"SentryDSN": config.Config.SentryPublicDSN,
@@ -56,7 +50,6 @@ func index(c *gin.Context) {
"DefaultUsed": defaultUsed,
"StaticColorLabels": strings.Join(config.Config.ColorLabelsStatic, " "),
"WebPrefix": config.Config.WebPrefix,
"SilencesApi": "FIXME",
})
log.Infof("[%s] %s %s took %s", c.ClientIP(), c.Request.Method, c.Request.RequestURI, time.Since(start))