diff --git a/assets/static/silence.js b/assets/static/silence.js index 34d5c50f6..3c465db77 100644 --- a/assets/static/silence.js +++ b/assets/static/silence.js @@ -1,330 +1,328 @@ -/* globals moment */ // moment.js +const $ = require("jquery"); +const moment = require("moment"); -/* globals Alerts, Colors, Templates, Unsee, UI */ +const alerts = require("./alerts"); +const colors = require("./colors"); +const templates = require("./templates"); +const ui = require("./ui"); +const unsee = require("./unsee"); -/* exported Silence */ -var Silence = (function() { +function alertmanagerSilencesAPIUrl(prefix) { + return prefix + "/api/v1/silences"; +} - var alertmanagerSilencesAPIUrl = function(prefix) { - return prefix + "/api/v1/silences"; +function silenceFormData() { + var values = $("#newSilenceForm").serializeArray(); + var payload = { + matchers: [], + startsAt: "", + endsAt: "", + createdBy: "", + comment: "" }; - - var silenceFormData = function() { - var values = $("#newSilenceForm").serializeArray(); - var payload = { - matchers: [], - startsAt: "", - endsAt: "", - createdBy: "", - comment: "" - }; - $.each(values, function(i, elem){ - switch (elem.name) { - case "comment": case "createdBy": - payload[elem.name] = elem.value; - break; - } - }); - if ($("#startsAt").data("DateTimePicker")) { - payload.startsAt = $("#startsAt").data("DateTimePicker").date(); - } - if ($("#endsAt").data("DateTimePicker")) { - payload.endsAt = $("#endsAt").data("DateTimePicker").date(); - } - $.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) { - var pval; - var isRegex = false; - if (values.length > 1) { - pval = "^(?:" + values.join("|") + ")$"; - isRegex = true; - } else { - pval = values[0]; - } - payload.matchers.push({ - name: labelKey, - value: pval, - isRegex: isRegex - }); - } - }); - return payload; - }; - - var silenceFormCalculateDuration = function() { - // skip if datetimepicker isn't ready yet - if (!$("#startsAt").data("DateTimePicker") || !$("#endsAt").data("DateTimePicker")) return false; - - var startsAt = $("#startsAt").data("DateTimePicker").date(); - var endsAt = $("#endsAt").data("DateTimePicker").date(); - - var totalDays = (endsAt.diff(startsAt, "days")); - var totalHours = (endsAt.diff(startsAt, "hours")) % 24; - var totalMinutes = endsAt.diff(startsAt, "minutes") % 60; - $("#silence-duration-days").html(totalDays); - $("#silence-duration-hours").html(totalHours); - $("#silence-duration-minutes").html(totalMinutes); - - var startsAtDesc = moment().to(startsAt); - startsAtDesc = startsAtDesc.replace("in a few seconds", "now"); - startsAtDesc = startsAtDesc.replace("a few seconds ago", "now"); - $("#silence-start-description").html(startsAtDesc); - - var endsAtDesc = moment().to(endsAt); - endsAtDesc = endsAtDesc.replace("in a few seconds", "now"); - endsAtDesc = endsAtDesc.replace("a few seconds ago", "now"); - $("#silence-end-description").html(endsAtDesc); - }; - - var silenceFormAlertmanagerURL = function() { - return $("#newSilenceForm .silence-alertmanager-picker").selectpicker("val"); - }; - - var silenceFormJSONRender = function() { - var d = []; - $.each(silenceFormAlertmanagerURL(), function(i, uri) { - if (i > 0) { - d.push("\n"); - } - d.push("curl " + alertmanagerSilencesAPIUrl(uri)); - }); - d.push("\n -X POST --data "); - d.push(JSON.stringify(silenceFormData(), undefined, 2)); - $("#silenceJSONBlob").html(d.join("")); - }; - - var silenceFormUpdateDuration = function(event) { - // skip if datetimepicker isn't ready yet - if (!$("#startsAt").data("DateTimePicker") || !$("#endsAt").data("DateTimePicker")) return false; - - var startsAt = $("#startsAt").data("DateTimePicker").date(); - var endsAt = $("#endsAt").data("DateTimePicker").date(); - var endsAtMinDate = $("#endsAt").data("DateTimePicker").minDate(); - var action = $(event.target).data("duration-action"); - var unit = $(event.target).data("duration-unit"); - var step = parseInt($(event.target).data("duration-step")); - - // re-calculate step for low values - // if we have 5 minute step and current duration is 1 minute than clicking - // on the increment should give us 5 minute, not 6 minute duration - var totalValue = (endsAt.diff(startsAt, unit)); - switch (unit) { - case "hours": - totalValue = totalValue % 24; - break; - case "minutes": - totalValue = totalValue % 60; + $.each(values, function(i, elem){ + switch (elem.name) { + case "comment": case "createdBy": + payload[elem.name] = elem.value; break; } - - if (action == "increment") { - // if step is 5 minute and current value is 3 than set 5 minutes, not 8 - if (step > 1 && totalValue < step) { - step = step - totalValue; - } - endsAt.add(step, unit); - } else { - // if step is 5 minute and current value is 3 than set 0 minutes - if (totalValue > 0 && step > 1 && totalValue < step) { - step = totalValue; - } - endsAt.subtract(step, unit); - if (endsAt < endsAtMinDate) { - // if decrement would result in a timestamp lower than allowed minimum - // then just reset it to the minimum - endsAt = endsAtMinDate; + }); + if ($("#startsAt").data("DateTimePicker")) { + payload.startsAt = $("#startsAt").data("DateTimePicker").date(); + } + if ($("#endsAt").data("DateTimePicker")) { + payload.endsAt = $("#endsAt").data("DateTimePicker").date(); + } + $.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) { + var pval; + var isRegex = false; + if (values.length > 1) { + pval = "^(?:" + values.join("|") + ")$"; + isRegex = true; + } else { + pval = values[0]; } + payload.matchers.push({ + name: labelKey, + value: pval, + isRegex: isRegex + }); } - $("#endsAt").data("DateTimePicker").date(endsAt); - silenceFormCalculateDuration(); - }; + }); + return payload; +} - var sendSilencePOST = function(url, payload) { - var elem = $(".silence-post-result[data-uri='" + url + "']"); - $.ajax({ - type: "POST", - url: alertmanagerSilencesAPIUrl(url), - data: JSON.stringify(payload), - error: function(xhr, textStatus) { - var err = Unsee.ParseAJAXError(xhr, textStatus); - var errContent = Templates.Render("silenceFormError", {error: err}); +function silenceFormCalculateDuration() { + // skip if datetimepicker isn't ready yet + if (!$("#startsAt").data("DateTimePicker") || !$("#endsAt").data("DateTimePicker")) return false; + + var startsAt = $("#startsAt").data("DateTimePicker").date(); + var endsAt = $("#endsAt").data("DateTimePicker").date(); + + var totalDays = (endsAt.diff(startsAt, "days")); + var totalHours = (endsAt.diff(startsAt, "hours")) % 24; + var totalMinutes = endsAt.diff(startsAt, "minutes") % 60; + $("#silence-duration-days").html(totalDays); + $("#silence-duration-hours").html(totalHours); + $("#silence-duration-minutes").html(totalMinutes); + + var startsAtDesc = moment().to(startsAt); + startsAtDesc = startsAtDesc.replace("in a few seconds", "now"); + startsAtDesc = startsAtDesc.replace("a few seconds ago", "now"); + $("#silence-start-description").html(startsAtDesc); + + var endsAtDesc = moment().to(endsAt); + endsAtDesc = endsAtDesc.replace("in a few seconds", "now"); + endsAtDesc = endsAtDesc.replace("a few seconds ago", "now"); + $("#silence-end-description").html(endsAtDesc); +} + +function silenceFormAlertmanagerURL() { + return $("#newSilenceForm .silence-alertmanager-picker").selectpicker("val"); +} + +function silenceFormJSONRender() { + var d = []; + $.each(silenceFormAlertmanagerURL(), function(i, uri) { + if (i > 0) { + d.push("\n"); + } + d.push("curl " + alertmanagerSilencesAPIUrl(uri)); + }); + d.push("\n -X POST --data "); + d.push(JSON.stringify(silenceFormData(), undefined, 2)); + $("#silenceJSONBlob").html(d.join("")); +} + +function silenceFormUpdateDuration(event) { + // skip if datetimepicker isn't ready yet + if (!$("#startsAt").data("DateTimePicker") || !$("#endsAt").data("DateTimePicker")) return false; + + var startsAt = $("#startsAt").data("DateTimePicker").date(); + var endsAt = $("#endsAt").data("DateTimePicker").date(); + var endsAtMinDate = $("#endsAt").data("DateTimePicker").minDate(); + var action = $(event.target).data("duration-action"); + var unit = $(event.target).data("duration-unit"); + var step = parseInt($(event.target).data("duration-step")); + + // re-calculate step for low values + // if we have 5 minute step and current duration is 1 minute than clicking + // on the increment should give us 5 minute, not 6 minute duration + var totalValue = (endsAt.diff(startsAt, unit)); + switch (unit) { + case "hours": + totalValue = totalValue % 24; + break; + case "minutes": + totalValue = totalValue % 60; + break; + } + + if (action == "increment") { + // if step is 5 minute and current value is 3 than set 5 minutes, not 8 + if (step > 1 && totalValue < step) { + step = step - totalValue; + } + endsAt.add(step, unit); + } else { + // if step is 5 minute and current value is 3 than set 0 minutes + if (totalValue > 0 && step > 1 && totalValue < step) { + step = totalValue; + } + endsAt.subtract(step, unit); + if (endsAt < endsAtMinDate) { + // if decrement would result in a timestamp lower than allowed minimum + // then just reset it to the minimum + endsAt = endsAtMinDate; + } + } + $("#endsAt").data("DateTimePicker").date(endsAt); + silenceFormCalculateDuration(); +} + +function sendSilencePOST(url, payload) { + var elem = $(".silence-post-result[data-uri='" + url + "']"); + $.ajax({ + type: "POST", + url: alertmanagerSilencesAPIUrl(url), + data: JSON.stringify(payload), + error: function(xhr, textStatus) { + var err = unsee.parseAJAXError(xhr, textStatus); + var errContent = templates.renderTemplate("silenceFormError", {error: err}); + $(elem).html(errContent); + }, + success: function(data) { + if (data.status == "success") { + $(elem).html(templates.renderTemplate("silenceFormSuccess", { + silenceID: data.data.silenceId + })); + } else { + var err = "Invalid response from Alertmanager API: " + JSON.stringify(data); + var errContent = templates.renderTemplate("silenceFormError", {error: err}); $(elem).html(errContent); + } + }, + dataType: "json" + }); +} + +// modal form for creating new silences +function setupSilenceForm() { + var modal = $("#silenceModal"); + modal.on("show.bs.modal", function(event) { + var elem = $(event.relatedTarget); + // hide tooltip for button that triggers this modal + elem.find("[data-toggle]").tooltip("hide"); + unsee.pause(); + modal.find(".modal-body").html( + templates.renderTemplate("silenceFormLoading", {}) + ); + var elemLabels = {}; + $.each(elem.data("labels").split(","), function(i, l) { + elemLabels[l.split("=")[0]] = l.split("=")[1]; + }); + $.ajax({ + url: "alerts.json?q=alertname=" + elem.data("alertname"), + error: function(xhr, textStatus) { + var err = unsee.parseAJAXError(xhr, textStatus); + modal.find(".modal-body").html( + templates.renderTemplate("silenceFormFatal", {error: err}) + ); }, success: function(data) { - if (data.status == "success") { - $(elem).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}); - $(elem).html(errContent); - } - }, - dataType: "json" - }); - }; - - // modal form for creating new silences - var setupSilenceForm = function() { - var modal = $("#silenceModal"); - modal.on("show.bs.modal", function(event) { - var elem = $(event.relatedTarget); - // hide tooltip for button that triggers this modal - elem.find("[data-toggle]").tooltip("hide"); - Unsee.Pause(); - modal.find(".modal-body").html( - Templates.Render("silenceFormLoading", {}) - ); - var elemLabels = {}; - $.each(elem.data("labels").split(","), function(i, l) { - elemLabels[l.split("=")[0]] = l.split("=")[1]; - }); - $.ajax({ - url: "alerts.json?q=alertname=" + elem.data("alertname"), - error: function(xhr, textStatus) { - var err = Unsee.ParseAJAXError(xhr, textStatus); - modal.find(".modal-body").html( - Templates.Render("silenceFormFatal", {error: err}) - ); - }, - success: function(data) { - // add colors from the response to global color set - Colors.Merge(data.colors); - 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) { - if (labels[labelKey] === undefined) { - labels[labelKey] = {}; - } - if (labels[labelKey][labelVal] === undefined) { - labels[labelKey][labelVal] = { - key: labelKey, - value: labelVal, - attrs: Alerts.GetLabelAttrs(labelKey, labelVal), - selected: elemLabels[labelKey] == labelVal - }; - } - }); - $.each(alert.alertmanager, function(i, alertmanager){ - alertmanagers[alertmanager.name] = alertmanager; - }); - }); - }); - modal.find(".modal-body").html( - Templates.Render("silenceForm", { - labels: labels, - alertmanagers: alertmanagers, - selectedAlertmanagers: elem.data("alertmanagers").split(",") - }) - ); - $.each($(".silence-alertmanager-picker"), function(i, elem) { - $(elem).selectpicker(); - }); - $.each($(".silence-label-picker"), function(i, elem) { - $(elem).selectpicker({ - noneSelectedText: "" + $(this).data("label-key") + ": ", - countSelectedText: function (numSelected) { - return "" + - $(elem).data("label-key") + ": " + numSelected + " values selected"; + // add colors from the response to global color set + colors.merge(data.colors); + 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) { + if (labels[labelKey] === undefined) { + labels[labelKey] = {}; + } + if (labels[labelKey][labelVal] === undefined) { + labels[labelKey][labelVal] = { + key: labelKey, + value: labelVal, + attrs: alerts.getLabelAttrs(labelKey, labelVal), + selected: elemLabels[labelKey] == labelVal + }; } }); + $.each(alert.alertmanager, function(i, alertmanager){ + alertmanagers[alertmanager.name] = alertmanager; + }); }); - $(".datetime-picker").datetimepicker({ - format: "YYYY-MM-DD HH:mm", - icons: { - time: "fa fa-clock-o", - date: "fa fa-calendar", - up: "fa fa-chevron-up", - down: "fa fa-chevron-down", - previous: "fa fa-chevron-left", - next: "fa fa-chevron-right", - today: "fa fa-asterisk", - clear: "fa fa-undo", - close: "fa fa-close" - }, - minDate: moment(), - sideBySide: true, - inline: true - }); - UI.SetupTooltips($("#newSilenceForm")); - $(".select-label-badge").on("click", function() { - var select = $(this).parent().parent().find("select"); - if (select.selectpicker("val").length) { - // if there's anything selected deselect all - select.selectpicker("deselectAll"); - } else { - // else select all - select.selectpicker("selectAll"); + }); + modal.find(".modal-body").html( + templates.renderTemplate("silenceForm", { + labels: labels, + alertmanagers: alertmanagers, + selectedAlertmanagers: elem.data("alertmanagers").split(",") + }) + ); + $.each($(".silence-alertmanager-picker"), function(i, elem) { + $(elem).selectpicker(); + }); + $.each($(".silence-label-picker"), function(i, elem) { + $(elem).selectpicker({ + noneSelectedText: "" + $(this).data("label-key") + ": ", + countSelectedText: function (numSelected) { + return "" + + $(elem).data("label-key") + ": " + numSelected + " values selected"; } }); - // set endsAt minDate to now + 1 minute - $("#endsAt").data("DateTimePicker").minDate(moment().add(1, "minute")); - // set endsAt time to +1 hour - $("#endsAt").data("DateTimePicker").date(moment().add(1, "hours")); - // whenever startsAt changes set it as the minDate for endsAt - // we can't have endsAt < startsAt - $("#newSilenceForm").on("dp.change", "#startsAt", function(){ - if (!$("#startsAt").data("DateTimePicker")) return false; - var startsAt = $("#startsAt").data("DateTimePicker").date(); - // endsAt needs to be at least 1 minute after startsAt - startsAt.add(1, "minute"); - $("#endsAt").data("DateTimePicker").minDate(startsAt); - }); - $("#newSilenceForm").on("click", "a.silence-duration-btn", silenceFormUpdateDuration); - $("#newSilenceForm").on("show.bs.collapse, dp.change", function () { - silenceFormJSONRender(); - silenceFormCalculateDuration(); - }); - $("#newSilenceForm").on("change", function () { - silenceFormJSONRender(); - }); - $("#newSilenceForm").submit(function(event) { - var payload = silenceFormData(); - if (payload.matchers.length === 0) { - var errContent = Templates.Render("silenceFormValidationError", {error: "Select at least on label"}); - $("#newSilenceAlert").html(errContent).removeClass("hidden"); - return false; - } - $("#newSilenceAlert").addClass("hidden"); - - var selectedAMURIs = silenceFormAlertmanagerURL(); - var selectedAMs = []; - $.each(alertmanagers, function(i, am) { - if ($.inArray(am.uri, selectedAMURIs) >= 0) { - selectedAMs.push(am); - } - }); - modal.find(".modal-body").html( - Templates.Render("silenceFormResults", {alertmanagers: selectedAMs}) - ); - - $.each(selectedAMURIs, function(i, uri){ - sendSilencePOST(uri, payload); - }); - - event.preventDefault(); - }); - silenceFormCalculateDuration(); + }); + $(".datetime-picker").datetimepicker({ + format: "YYYY-MM-DD HH:mm", + icons: { + time: "fa fa-clock-o", + date: "fa fa-calendar", + up: "fa fa-chevron-up", + down: "fa fa-chevron-down", + previous: "fa fa-chevron-left", + next: "fa fa-chevron-right", + today: "fa fa-asterisk", + clear: "fa fa-undo", + close: "fa fa-close" + }, + minDate: moment(), + sideBySide: true, + inline: true + }); + ui.setupGroupTooltips($("#newSilenceForm")); + $(".select-label-badge").on("click", function() { + var select = $(this).parent().parent().find("select"); + if (select.selectpicker("val").length) { + // if there's anything selected deselect all + select.selectpicker("deselectAll"); + } else { + // else select all + select.selectpicker("selectAll"); + } + }); + // set endsAt minDate to now + 1 minute + $("#endsAt").data("DateTimePicker").minDate(moment().add(1, "minute")); + // set endsAt time to +1 hour + $("#endsAt").data("DateTimePicker").date(moment().add(1, "hours")); + // whenever startsAt changes set it as the minDate for endsAt + // we can't have endsAt < startsAt + $("#newSilenceForm").on("dp.change", "#startsAt", function(){ + if (!$("#startsAt").data("DateTimePicker")) return false; + var startsAt = $("#startsAt").data("DateTimePicker").date(); + // endsAt needs to be at least 1 minute after startsAt + startsAt.add(1, "minute"); + $("#endsAt").data("DateTimePicker").minDate(startsAt); + }); + $("#newSilenceForm").on("click", "a.silence-duration-btn", silenceFormUpdateDuration); + $("#newSilenceForm").on("show.bs.collapse, dp.change", function () { silenceFormJSONRender(); - } - }); + silenceFormCalculateDuration(); + }); + $("#newSilenceForm").on("change", function () { + silenceFormJSONRender(); + }); + $("#newSilenceForm").submit(function(event) { + var payload = silenceFormData(); + if (payload.matchers.length === 0) { + var errContent = templates.renderTemplate("silenceFormValidationError", {error: "Select at least on label"}); + $("#newSilenceAlert").html(errContent).removeClass("hidden"); + return false; + } + $("#newSilenceAlert").addClass("hidden"); + var selectedAMURIs = silenceFormAlertmanagerURL(); + var selectedAMs = []; + $.each(alertmanagers, function(i, am) { + if ($.inArray(am.uri, selectedAMURIs) >= 0) { + selectedAMs.push(am); + } + }); + modal.find(".modal-body").html( + templates.renderTemplate("silenceFormResults", {alertmanagers: selectedAMs}) + ); + + $.each(selectedAMURIs, function(i, uri){ + sendSilencePOST(uri, payload); + }); + + event.preventDefault(); + }); + silenceFormCalculateDuration(); + silenceFormJSONRender(); + } }); - modal.on("hidden.bs.modal", function() { - var modal = $(this); - modal.find(".modal-body").children().remove(); - Unsee.WaitForNextReload(); - }); - }; - return { - Init: setupSilenceForm - }; + }); + modal.on("hidden.bs.modal", function() { + var modal = $(this); + modal.find(".modal-body").children().remove(); + unsee.resume(); + }); +} -})(); +exports.setupSilenceForm = setupSilenceForm; diff --git a/assets/static/silence.test.js b/assets/static/silence.test.js new file mode 100644 index 000000000..2ce6d91dd --- /dev/null +++ b/assets/static/silence.test.js @@ -0,0 +1,5 @@ +test("silence setupSilenceForm()", () => { + window.jQuery = require("jquery"); + const silence = require("./silence"); + silence.setupSilenceForm(); +});