mirror of
https://github.com/prymitive/karma
synced 2026-05-17 04:16:42 +00:00
Merge pull request #662 from prymitive/silenceform-strip-labels
feat(ui): add options for ignoring labels when populating silences from alerts
This commit is contained in:
2
Makefile
2
Makefile
@@ -66,6 +66,7 @@ run: $(NAME)
|
||||
LABELS_COLOR_UNIQUE="@receiver instance cluster" \
|
||||
LABELS_COLOR_STATIC="job" \
|
||||
FILTERS_DEFAULT="@state=active @receiver=by-cluster-service" \
|
||||
SILENCEFORM_STRIP_LABELS="job" \
|
||||
PORT=$(PORT) \
|
||||
./$(NAME)
|
||||
|
||||
@@ -86,6 +87,7 @@ run-docker: docker-image
|
||||
-e LABELS_COLOR_UNIQUE="instance cluster" \
|
||||
-e LABELS_COLOR_STATIC="job" \
|
||||
-e FILTERS_DEFAULT="@state=active @receiver=by-cluster-service" \
|
||||
-e SILENCEFORM_STRIP_LABELS="job" \
|
||||
-e PORT=$(PORT) \
|
||||
-p $(PORT):$(PORT) \
|
||||
$(NAME):$(VERSION)
|
||||
|
||||
@@ -60,3 +60,7 @@ sentry:
|
||||
jira:
|
||||
- regex: DEVOPS-[0-9]+
|
||||
uri: https://jira.example.com
|
||||
silenceForm:
|
||||
strip:
|
||||
labels:
|
||||
- job
|
||||
|
||||
@@ -599,6 +599,31 @@ sentry:
|
||||
public: https://<key>:<secret>@sentry.io/<project>
|
||||
```
|
||||
|
||||
## Silence form
|
||||
|
||||
`silenceForm` section allow customizing silence form behavior.
|
||||
Syntax:
|
||||
|
||||
```YAML
|
||||
silenceForm:
|
||||
strip:
|
||||
labels: list of strings
|
||||
```
|
||||
|
||||
- `strip:labels` - list of labels to ignore when populating silence form from
|
||||
individual alerts or group of alerts. This allows to create silences matching
|
||||
only unique labels, like `instance` or `host`, ignoring any common labels like
|
||||
`job`.
|
||||
|
||||
Example:
|
||||
|
||||
```YAML
|
||||
silenceForm:
|
||||
strip:
|
||||
labels:
|
||||
- job
|
||||
```
|
||||
|
||||
## Customizing karma
|
||||
|
||||
In order to keep the core code simple karma doesn't support any way of extending
|
||||
|
||||
@@ -53,3 +53,7 @@ receivers:
|
||||
sentry:
|
||||
private: secret
|
||||
public: 123456789
|
||||
silenceForm:
|
||||
strip:
|
||||
labels:
|
||||
- job
|
||||
|
||||
@@ -76,6 +76,8 @@ func init() {
|
||||
pflag.StringSlice("receivers.strip", []string{},
|
||||
"List of receivers to not display alerts for")
|
||||
|
||||
pflag.StringSlice("silenceform.strip.labels", []string{}, "List of labels to ignore when auto-filling silence form from alerts")
|
||||
|
||||
pflag.String("listen.address", "", "IP/Hostname to listen on")
|
||||
pflag.Int("listen.port", 8080, "HTTP port to listen on")
|
||||
pflag.String("listen.prefix", "/", "URL prefix")
|
||||
@@ -161,6 +163,7 @@ func (config *configSchema) Read() {
|
||||
config.Receivers.Strip = v.GetStringSlice("receivers.strip")
|
||||
config.Sentry.Private = v.GetString("sentry.private")
|
||||
config.Sentry.Public = v.GetString("sentry.public")
|
||||
config.SilenceForm.Strip.Labels = v.GetStringSlice("silenceform.strip.labels")
|
||||
|
||||
err = v.UnmarshalKey("alertmanager.servers", &config.Alertmanager.Servers)
|
||||
if err != nil {
|
||||
|
||||
@@ -119,6 +119,9 @@ receivers:
|
||||
sentry:
|
||||
private: secret key
|
||||
public: public key
|
||||
silenceForm:
|
||||
strip:
|
||||
labels: []
|
||||
`
|
||||
|
||||
configDump, err := yaml.Marshal(Config)
|
||||
|
||||
@@ -92,4 +92,9 @@ type configSchema struct {
|
||||
Private string
|
||||
Public string
|
||||
}
|
||||
SilenceForm struct {
|
||||
Strip struct {
|
||||
Labels []string
|
||||
}
|
||||
} `yaml:"silenceForm" mapstructure:"silenceForm"`
|
||||
}
|
||||
|
||||
@@ -219,13 +219,22 @@ type SortSettings struct {
|
||||
ValueMapping map[string]map[string]int `json:"valueMapping"`
|
||||
}
|
||||
|
||||
type SilenceFormStripSettings struct {
|
||||
Labels []string `json:"labels"`
|
||||
}
|
||||
|
||||
type SilenceFormSettings struct {
|
||||
Strip SilenceFormStripSettings `json:"strip"`
|
||||
}
|
||||
|
||||
// Settings is used to export karma configuration that is used by UI
|
||||
type Settings struct {
|
||||
StaticColorLabels []string `json:"staticColorLabels"`
|
||||
AnnotationsDefaultHidden bool `json:"annotationsDefaultHidden"`
|
||||
AnnotationsHidden []string `json:"annotationsHidden"`
|
||||
AnnotationsVisible []string `json:"annotationsVisible"`
|
||||
Sorting SortSettings `json:"sorting"`
|
||||
StaticColorLabels []string `json:"staticColorLabels"`
|
||||
AnnotationsDefaultHidden bool `json:"annotationsDefaultHidden"`
|
||||
AnnotationsHidden []string `json:"annotationsHidden"`
|
||||
AnnotationsVisible []string `json:"annotationsVisible"`
|
||||
Sorting SortSettings `json:"sorting"`
|
||||
SilenceForm SilenceFormSettings `json:"silenceForm"`
|
||||
}
|
||||
|
||||
// AlertsResponse is the structure of JSON response UI will use to get alert data
|
||||
|
||||
@@ -17,13 +17,18 @@ import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
|
||||
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalLinkAlt";
|
||||
|
||||
import { APIAlert, APIGroup } from "Models/API";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { FetchPauser } from "Components/FetchPauser";
|
||||
import { DropdownSlide } from "Components/Animations/DropdownSlide";
|
||||
|
||||
const onSilenceClick = (silenceFormStore, group, alert) => {
|
||||
const onSilenceClick = (alertStore, silenceFormStore, group, alert) => {
|
||||
silenceFormStore.data.resetProgress();
|
||||
silenceFormStore.data.fillMatchersFromGroup(group, [alert]);
|
||||
silenceFormStore.data.fillMatchersFromGroup(
|
||||
group,
|
||||
alertStore.settings.values.silenceForm.strip.labels,
|
||||
[alert]
|
||||
);
|
||||
silenceFormStore.toggle.show();
|
||||
};
|
||||
|
||||
@@ -35,6 +40,7 @@ const MenuContent = onClickOutside(
|
||||
group,
|
||||
alert,
|
||||
afterClick,
|
||||
alertStore,
|
||||
silenceFormStore
|
||||
}) => {
|
||||
return (
|
||||
@@ -62,7 +68,9 @@ const MenuContent = onClickOutside(
|
||||
<div className="dropdown-divider" />
|
||||
<div
|
||||
className="dropdown-item cursor-pointer"
|
||||
onClick={() => onSilenceClick(silenceFormStore, group, alert)}
|
||||
onClick={() =>
|
||||
onSilenceClick(alertStore, silenceFormStore, group, alert)
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon className="mr-1" icon={faBellSlash} />
|
||||
Silence this alert
|
||||
@@ -86,6 +94,7 @@ const AlertMenu = observer(
|
||||
static propTypes = {
|
||||
group: APIGroup.isRequired,
|
||||
alert: APIAlert.isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
@@ -108,7 +117,7 @@ const AlertMenu = observer(
|
||||
});
|
||||
|
||||
render() {
|
||||
const { group, alert, silenceFormStore } = this.props;
|
||||
const { group, alert, alertStore, silenceFormStore } = this.props;
|
||||
|
||||
const uniqueClass = `components-grid-alert-${group.id}-${hash(
|
||||
alert.labels
|
||||
@@ -148,6 +157,7 @@ const AlertMenu = observer(
|
||||
popperStyle={style}
|
||||
group={group}
|
||||
alert={alert}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
afterClick={this.collapse.hide}
|
||||
handleClickOutside={this.collapse.hide}
|
||||
|
||||
@@ -29,6 +29,7 @@ const MountedAlertMenu = group => {
|
||||
<AlertMenu
|
||||
group={group}
|
||||
alert={alert}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
</Provider>
|
||||
@@ -70,6 +71,7 @@ const MountedMenuContent = group => {
|
||||
group={group}
|
||||
alert={alert}
|
||||
afterClick={MockAfterClick}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
</Provider>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faVolumeMute } from "@fortawesome/free-solid-svg-icons/faVolumeMute";
|
||||
|
||||
import { APIAlert, APIGroup } from "Models/API";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { BorderClassMap } from "Common/Colors";
|
||||
import { StaticLabels } from "Common/Query";
|
||||
@@ -26,6 +27,7 @@ const Alert = observer(
|
||||
showAlertmanagers: PropTypes.bool.isRequired,
|
||||
showReceiver: PropTypes.bool.isRequired,
|
||||
afterUpdate: PropTypes.func.isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
@@ -36,6 +38,7 @@ const Alert = observer(
|
||||
showAlertmanagers,
|
||||
showReceiver,
|
||||
afterUpdate,
|
||||
alertStore,
|
||||
silenceFormStore
|
||||
} = this.props;
|
||||
|
||||
@@ -87,6 +90,7 @@ const Alert = observer(
|
||||
<AlertMenu
|
||||
group={group}
|
||||
alert={alert}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
{alert.alertmanager
|
||||
|
||||
@@ -52,6 +52,7 @@ const MountedAlert = (alert, group, showAlertmanagers, showReceiver) => {
|
||||
showAlertmanagers={showAlertmanagers}
|
||||
showReceiver={showReceiver}
|
||||
afterUpdate={MockAfterUpdate}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
</Provider>
|
||||
|
||||
@@ -16,14 +16,18 @@ import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
|
||||
|
||||
import { APIGroup } from "Models/API";
|
||||
import { FormatAPIFilterQuery } from "Stores/AlertStore";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { QueryOperators, StaticLabels, FormatQuery } from "Common/Query";
|
||||
import { DropdownSlide } from "Components/Animations/DropdownSlide";
|
||||
import { FetchPauser } from "Components/FetchPauser";
|
||||
|
||||
const onSilenceClick = (silenceFormStore, group) => {
|
||||
const onSilenceClick = (alertStore, silenceFormStore, group) => {
|
||||
silenceFormStore.data.resetProgress();
|
||||
silenceFormStore.data.fillMatchersFromGroup(group);
|
||||
silenceFormStore.data.fillMatchersFromGroup(
|
||||
group,
|
||||
alertStore.settings.values.silenceForm.strip.labels
|
||||
);
|
||||
silenceFormStore.toggle.show();
|
||||
};
|
||||
|
||||
@@ -34,6 +38,7 @@ const MenuContent = onClickOutside(
|
||||
popperStyle,
|
||||
group,
|
||||
afterClick,
|
||||
alertStore,
|
||||
silenceFormStore
|
||||
}) => {
|
||||
let groupFilters = Object.keys(group.labels).map(name =>
|
||||
@@ -65,7 +70,7 @@ const MenuContent = onClickOutside(
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item cursor-pointer"
|
||||
onClick={() => onSilenceClick(silenceFormStore, group)}
|
||||
onClick={() => onSilenceClick(alertStore, silenceFormStore, group)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBellSlash} /> Silence this group
|
||||
</div>
|
||||
@@ -86,6 +91,7 @@ const GroupMenu = observer(
|
||||
class GroupMenu extends Component {
|
||||
static propTypes = {
|
||||
group: APIGroup.isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
themed: PropTypes.bool.isRequired
|
||||
};
|
||||
@@ -109,7 +115,7 @@ const GroupMenu = observer(
|
||||
});
|
||||
|
||||
render() {
|
||||
const { group, silenceFormStore, themed } = this.props;
|
||||
const { group, alertStore, silenceFormStore, themed } = this.props;
|
||||
|
||||
return (
|
||||
<Manager>
|
||||
@@ -143,6 +149,7 @@ const GroupMenu = observer(
|
||||
popperRef={ref}
|
||||
popperStyle={style}
|
||||
group={group}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
afterClick={this.collapse.hide}
|
||||
handleClickOutside={this.collapse.hide}
|
||||
|
||||
@@ -26,6 +26,7 @@ const MountedGroupMenu = (group, themed) => {
|
||||
<Provider alertStore={alertStore}>
|
||||
<GroupMenu
|
||||
group={group}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
themed={themed}
|
||||
/>
|
||||
@@ -71,6 +72,7 @@ const MountedMenuContent = group => {
|
||||
popperStyle={{}}
|
||||
group={group}
|
||||
afterClick={MockAfterClick}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
</Provider>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
|
||||
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
||||
|
||||
import { APIGroup } from "Models/API";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { FilteringLabel } from "Components/Labels/FilteringLabel";
|
||||
import { FilteringCounterBadge } from "Components/Labels/FilteringCounterBadge";
|
||||
@@ -22,6 +23,7 @@ const GroupHeader = observer(
|
||||
toggle: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
group: APIGroup.isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
themedCounters: PropTypes.bool.isRequired
|
||||
};
|
||||
@@ -30,6 +32,7 @@ const GroupHeader = observer(
|
||||
const {
|
||||
collapseStore,
|
||||
group,
|
||||
alertStore,
|
||||
silenceFormStore,
|
||||
themedCounters
|
||||
} = this.props;
|
||||
@@ -43,6 +46,7 @@ const GroupHeader = observer(
|
||||
<span className="flex-shrink-0 flex-grow-0">
|
||||
<GroupMenu
|
||||
group={group}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
themed={!themedCounters}
|
||||
/>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { faMinus } from "@fortawesome/free-solid-svg-icons/faMinus";
|
||||
|
||||
import { APIGroup } from "Models/API";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { IsMobile } from "Common/Device";
|
||||
import { BackgroundClassMap } from "Common/Colors";
|
||||
@@ -55,6 +56,7 @@ const AlertGroup = observer(
|
||||
afterUpdate: PropTypes.func.isRequired,
|
||||
group: APIGroup.isRequired,
|
||||
showAlertmanagers: PropTypes.bool.isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
style: PropTypes.object
|
||||
@@ -157,6 +159,7 @@ const AlertGroup = observer(
|
||||
showAlertmanagers,
|
||||
afterUpdate,
|
||||
silenceFormStore,
|
||||
alertStore,
|
||||
settingsStore,
|
||||
style
|
||||
} = this.props;
|
||||
@@ -198,6 +201,7 @@ const AlertGroup = observer(
|
||||
<GroupHeader
|
||||
collapseStore={this.collapse}
|
||||
group={group}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
themedCounters={themedCounters}
|
||||
/>
|
||||
@@ -216,6 +220,7 @@ const AlertGroup = observer(
|
||||
}
|
||||
showReceiver={group.alerts.length === 1}
|
||||
afterUpdate={afterUpdate}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -64,6 +64,7 @@ const MountedAlertGroup = (afterUpdate, showAlertmanagers) => {
|
||||
group={group}
|
||||
showAlertmanagers={showAlertmanagers}
|
||||
settingsStore={settingsStore}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
</Provider>
|
||||
|
||||
@@ -240,6 +240,7 @@ const AlertGrid = observer(
|
||||
Object.keys(alertStore.data.upstreams.clusters).length > 1
|
||||
}
|
||||
afterUpdate={this.masonryRepack}
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
style={{
|
||||
|
||||
@@ -177,6 +177,11 @@ class AlertStore {
|
||||
label: "alertname"
|
||||
},
|
||||
valueMapping: {}
|
||||
},
|
||||
silenceForm: {
|
||||
strip: {
|
||||
labels: []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -114,17 +114,19 @@ class SilenceFormStore {
|
||||
},
|
||||
|
||||
// if alerts argument is not passed all group alerts will be used
|
||||
fillMatchersFromGroup(group, alerts) {
|
||||
fillMatchersFromGroup(group, stripLabels, alerts) {
|
||||
let matchers = [];
|
||||
|
||||
// add matchers for all shared labels in this group
|
||||
for (const [key, value] of Object.entries(
|
||||
Object.assign({}, group.labels, group.shared.labels)
|
||||
)) {
|
||||
const matcher = NewEmptyMatcher();
|
||||
matcher.name = key;
|
||||
matcher.values = [MatcherValueToObject(value)];
|
||||
matchers.push(matcher);
|
||||
if (!stripLabels.includes(key)) {
|
||||
const matcher = NewEmptyMatcher();
|
||||
matcher.name = key;
|
||||
matcher.values = [MatcherValueToObject(value)];
|
||||
matchers.push(matcher);
|
||||
}
|
||||
}
|
||||
|
||||
// add matchers for all unique labels in this group
|
||||
@@ -132,10 +134,12 @@ class SilenceFormStore {
|
||||
const allAlerts = alerts ? alerts : group.alerts;
|
||||
for (const alert of allAlerts) {
|
||||
for (const [key, value] of Object.entries(alert.labels)) {
|
||||
if (!labels[key]) {
|
||||
labels[key] = new Set();
|
||||
if (!stripLabels.includes(key)) {
|
||||
if (!labels[key]) {
|
||||
labels[key] = new Set();
|
||||
}
|
||||
labels[key].add(value);
|
||||
}
|
||||
labels[key].add(value);
|
||||
}
|
||||
}
|
||||
for (const [key, values] of Object.entries(labels)) {
|
||||
|
||||
@@ -120,7 +120,7 @@ describe("SilenceFormStore.data", () => {
|
||||
|
||||
it("fillMatchersFromGroup() creates correct matcher object for a group", () => {
|
||||
const group = MockGroup();
|
||||
store.data.fillMatchersFromGroup(group);
|
||||
store.data.fillMatchersFromGroup(group, []);
|
||||
expect(store.data.matchers).toHaveLength(4);
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
@@ -161,7 +161,7 @@ describe("SilenceFormStore.data", () => {
|
||||
|
||||
it("fillMatchersFromGroup() creates correct matcher object for a group with only a subset of alets passed", () => {
|
||||
const group = MockGroup();
|
||||
store.data.fillMatchersFromGroup(group, [group.alerts[0]]);
|
||||
store.data.fillMatchersFromGroup(group, [], [group.alerts[0]]);
|
||||
expect(store.data.matchers).toHaveLength(4);
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
@@ -193,10 +193,27 @@ describe("SilenceFormStore.data", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("fillMatchersFromGroup() ignores labels from stripLabels list", () => {
|
||||
const group = MockGroup();
|
||||
store.data.fillMatchersFromGroup(
|
||||
group,
|
||||
["job", "instance", "cluster"],
|
||||
[group.alerts[0]]
|
||||
);
|
||||
expect(store.data.matchers).toHaveLength(1);
|
||||
expect(store.data.matchers).toContainEqual(
|
||||
expect.objectContaining({
|
||||
name: "alertname",
|
||||
values: [{ label: "FakeAlert", value: "FakeAlert" }],
|
||||
isRegex: false
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("fillMatchersFromGroup() resets silenceID if set", () => {
|
||||
store.data.silenceID = "12345";
|
||||
const group = MockGroup();
|
||||
store.data.fillMatchersFromGroup(group, [group.alerts[0]]);
|
||||
store.data.fillMatchersFromGroup(group, [], [group.alerts[0]]);
|
||||
expect(store.data.silenceID).toBeNull();
|
||||
});
|
||||
|
||||
@@ -259,7 +276,7 @@ describe("SilenceFormStore.data", () => {
|
||||
|
||||
it("toAlertmanagerPayload creates payload that matches snapshot", () => {
|
||||
const group = MockGroup();
|
||||
store.data.fillMatchersFromGroup(group);
|
||||
store.data.fillMatchersFromGroup(group, []);
|
||||
// add empty matcher so we test empty string rendering
|
||||
store.data.addEmptyMatcher();
|
||||
store.data.startsAt = moment.utc([2000, 1, 1, 0, 0, 0]);
|
||||
|
||||
@@ -42,6 +42,11 @@ const EmptyAPIResponse = () => ({
|
||||
}
|
||||
}
|
||||
},
|
||||
silenceForm: {
|
||||
strip: {
|
||||
labels: []
|
||||
}
|
||||
},
|
||||
staticColorLabels: ["job"],
|
||||
annotationsDefaultHidden: false,
|
||||
annotationsHidden: [],
|
||||
|
||||
5
views.go
5
views.go
@@ -95,6 +95,11 @@ func alerts(c *gin.Context) {
|
||||
AnnotationsDefaultHidden: config.Config.Annotations.Default.Hidden,
|
||||
AnnotationsHidden: config.Config.Annotations.Hidden,
|
||||
AnnotationsVisible: config.Config.Annotations.Visible,
|
||||
SilenceForm: models.SilenceFormSettings{
|
||||
Strip: models.SilenceFormStripSettings{
|
||||
Labels: config.Config.SilenceForm.Strip.Labels,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if config.Config.Grid.Sorting.CustomValues.Labels != nil {
|
||||
|
||||
Reference in New Issue
Block a user