dashboard/edit/monitoring.coreos.com.prome.../AlertingRule.vue

485 lines
14 KiB
Vue

<script>
import debounce from 'lodash/debounce';
import has from 'lodash/has';
import isEmpty from 'lodash/isEmpty';
import pickBy from 'lodash/pickBy';
import Checkbox from '@/components/form/Checkbox';
import CodeMirror from '@/components/CodeMirror';
import KeyValue from '@/components/form/KeyValue';
import LabeledInput from '@/components/form/LabeledInput';
import LabeledSelect from '@/components/form/LabeledSelect';
import UnitInput from '@/components/form/UnitInput';
import { _VIEW } from '@/config/query-params';
const INGORED_ANNOTATIONS = [
'summary',
'message',
'description',
'runbook_url',
];
export default {
components: {
Checkbox,
CodeMirror,
KeyValue,
LabeledInput,
LabeledSelect,
UnitInput,
},
props: {
value: {
type: Object,
default: () => ({}),
},
mode: {
type: String,
default: 'create',
},
},
data() {
return {
selectedSeverityLabel: null,
ignoredAnnotations: INGORED_ANNOTATIONS,
severityOptions: [
this.t('prometheusRule.alertingRules.labels.severity.choices.critical'),
this.t('prometheusRule.alertingRules.labels.severity.choices.warning'),
this.t('prometheusRule.alertingRules.labels.severity.choices.none'),
],
};
},
computed: {
isView() {
return this.mode === _VIEW;
},
severityLabelChecked: {
get() {
if (has(this.value?.labels, 'severity')) {
return true;
}
return false;
},
set(value) {
if (value) {
if (isEmpty(this.value?.labels)) {
this.$set(this.value, 'labels', { severity: 'none' });
} else {
this.$set(this.value.labels, 'severity', 'none');
}
} else {
this.$set(this.value.labels, 'severity', '');
delete this.value.labels.severity;
}
},
},
summaryAnnotationChecked: {
get() {
if (has(this.value?.annotations, 'summary')) {
return true;
}
return false;
},
set(value) {
if (value) {
if (isEmpty(this.value?.annotations)) {
this.$set(this.value, 'annotations', { summary: '' });
} else {
this.$set(this.value.annotations, 'summary', '');
}
} else {
this.$set(this.value.annotations, 'summary', '');
delete this.value.annotations.summary;
}
},
},
messageAnnotationChecked: {
get() {
if (has(this.value?.annotations, 'message')) {
return true;
}
return false;
},
set(value) {
if (value) {
if (isEmpty(this.value?.annotations)) {
this.$set(this.value, 'annotations', { message: '' });
} else {
this.$set(this.value.annotations, 'message', '');
}
} else {
this.$set(this.value.annotations, 'message', '');
delete this.value.annotations.message;
}
},
},
descriptionAnnotationChecked: {
get() {
if (has(this.value?.annotations, 'description')) {
return true;
}
return false;
},
set(value) {
if (value) {
if (isEmpty(this.value?.annotations)) {
this.$set(this.value, 'annotations', { description: '' });
} else {
this.$set(this.value.annotations, 'description', '');
}
} else {
this.$set(this.value.annotations, 'description', '');
delete this.value.annotations.description;
}
},
},
runbookAnnotationChecked: {
get() {
if (has(this.value?.annotations, 'runbook_url')) {
return true;
}
return false;
},
set(value) {
if (value) {
if (isEmpty(this.value?.annotations)) {
this.$set(this.value, 'annotations', { runbook_url: '' });
} else {
this.$set(this.value.annotations, 'runbook_url', '');
}
} else {
this.$set(this.value.annotations, 'runbook_url', '');
delete this.value.annotations.runbook_url;
}
},
},
filteredAnnotations() {
const { ignoredAnnotations } = this;
const annotations = this.value?.annotations;
return pickBy(annotations, (value, key) => {
return !ignoredAnnotations.includes(key);
});
},
filteredLabels() {
const labels = this.value?.labels;
return pickBy(labels, (value, key) => {
return key !== 'severity';
});
},
showCustomSeverityInput() {
const { selectedSeverityLabel } = this;
if (selectedSeverityLabel === 'custom') {
return true;
}
return false;
},
},
watch: {
selectedSeverityLabel(value /* , oldValue */) {
const neu = value === 'custom' ? '' : value;
if (this.value?.labels) {
this.$set(this.value.labels, 'severity', neu);
} else {
this.$set(this.value, 'labels', { severity: neu });
}
},
},
created() {
this.queueUpdate = debounce(this.updateExpression, 500);
this.queueLabelUpdate = debounce(this.updateLabels, 500);
this.queueAnnotationUpdate = debounce(this.updateAnnotations, 500);
},
mounted() {
this.$nextTick(() => {
if (this.value?.labels?.severity) {
this.selectedSeverityLabel = this.value.labels.severity;
}
});
},
methods: {
updateLabels(value) {
const neu = { ...value };
if (this.selectedSeverityLabel) {
neu['severity'] = this.selectedSeverityLabel;
}
this.$set(this.value, 'labels', neu);
},
updateAnnotations(value) {
const {
ignoredAnnotations,
value: { annotations = {} },
} = this;
const neu = { ...value };
ignoredAnnotations.forEach((anno) => {
if (has(annotations, anno)) {
neu[anno] = annotations[anno];
}
});
this.$set(this.value, 'annotations', neu);
},
updateExpression(value) {
this.$set(this.value, 'expr', value);
}
},
};
</script>
<template>
<div>
<div class="row mt-25">
<div class="col span-6">
<LabeledInput
v-model="value.alert"
:label="t('prometheusRule.alertingRules.name')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<UnitInput
v-model="value.for"
:suffix="t('suffix.seconds', {count: value.for})"
:placeholder="t('prometheusRule.alertingRules.for.placeholder')"
:label="t('prometheusRule.alertingRules.for.label')"
:mode="mode"
@input="(e) => $set(value, 'for', `${e}s`)"
/>
</div>
</div>
<div class="row">
<div class="col span-12">
<LabeledInput
v-model="value.expr"
:label="t('prometheusRule.promQL.label')"
:required="true"
:mode="mode"
>
<template #field>
<CodeMirror
class="mt-20"
:value="value.expr"
:options="{
mode: null,
foldGutter: false,
readOnly: mode === 'view',
}"
@onInput="queueUpdate"
/>
</template>
</LabeledInput>
</div>
</div>
<div class="suggested-labels">
<div class="row mb-0 mt-30">
<div class="col span-12">
<h3>
<t k="prometheusRule.alertingRules.labels.label" />
</h3>
</div>
</div>
<div :class="[{ hide: isView && !severityLabelChecked }, 'row']">
<div v-if="isView && severityLabelChecked" class="col span-6 severity">
<label><t k="prometheusRule.alertingRules.labels.severity.label" /></label>
<div>{{ value.labels.severity }}</div>
</div>
<div v-else class="severity col span-6">
<Checkbox
v-model="severityLabelChecked"
:mode="mode"
:label="t('prometheusRule.alertingRules.labels.severity.label')"
/>
<LabeledSelect
v-if="severityLabelChecked"
v-model="value.labels.severity"
class="mt-10"
:mode="mode"
:label="t('prometheusRule.alertingRules.labels.severity.choices.label')"
:localized-label="false"
:options="severityOptions"
/>
</div>
</div>
<div :class="[{ hide: isView && Object.keys(filteredLabels).length === 0}, 'row', 'mt-0']">
<div class="col span-12">
<KeyValue
key="labels"
:value="filteredLabels"
:add-label="t('labels.addLabel')"
:mode="mode"
:read-allowed="false"
:value-multiline="false"
@input="queueLabelUpdate"
/>
</div>
</div>
<div class="suggested-annotations">
<div class="row mb-0 mt-30">
<div class="col span-12">
<h3>
<t k="prometheusRule.alertingRules.annotations.label" />
</h3>
</div>
</div>
<div class="row mt-0 mb-0">
<div class="col span-12">
<div :class="[{ hide: isView && !summaryAnnotationChecked }, 'row']">
<div
v-if="isView && summaryAnnotationChecked"
class="col span-6 annotation-checkbox-container"
>
<label>
<t k="prometheusRule.alertingRules.annotations.summary.label" />
</label>
<div>{{ value.annotations.summary }}</div>
</div>
<div v-else class="col span-6 annotation-checkbox-container">
<Checkbox
v-model="summaryAnnotationChecked"
:mode="mode"
:label="t('prometheusRule.alertingRules.annotations.summary.label')"
/>
<LabeledInput
v-if="summaryAnnotationChecked"
v-model="value.annotations.summary"
class="mt-5"
:label="t('prometheusRule.alertingRules.annotations.summary.input')"
type="multiline"
:mode="mode"
/>
</div>
</div>
<div :class="[{ hide: isView && !messageAnnotationChecked }, 'row']">
<div
v-if="isView && messageAnnotationChecked"
class="col span-6 annotation-checkbox-container"
>
<label><t
k="prometheusRule.alertingRules.annotations.message.label"
/></label>
<div>{{ value.annotations.message }}</div>
</div>
<div v-else class="col span-6 annotation-checkbox-container">
<Checkbox
v-model="messageAnnotationChecked"
:mode="mode"
:label="t('prometheusRule.alertingRules.annotations.message.label')"
/>
<LabeledInput
v-if="messageAnnotationChecked"
v-model="value.annotations.message"
class="mt-5"
:label="t('prometheusRule.alertingRules.annotations.message.input')"
type="multiline"
:mode="mode"
/>
</div>
</div>
<div :class="[ { hide: isView && !descriptionAnnotationChecked }, 'row']">
<div
v-if="isView && descriptionAnnotationChecked"
class="col span-6 annotation-checkbox-container"
>
<label><t
k="prometheusRule.alertingRules.annotations.description.label"
/></label>
<div>{{ value.annotations.description }}</div>
</div>
<div v-else class="col span-6 annotation-checkbox-container">
<Checkbox
v-model="descriptionAnnotationChecked"
:mode="mode"
:label="t('prometheusRule.alertingRules.annotations.description.label')"
/>
<LabeledInput
v-if="descriptionAnnotationChecked"
v-model="value.annotations.description"
class="mt-5"
:label="t('prometheusRule.alertingRules.annotations.description.input')"
type="multiline"
:mode="mode"
/>
</div>
</div>
<div :class="[{ hide: isView && !runbookAnnotationChecked }, 'row']">
<div
v-if="isView && runbookAnnotationChecked"
class="col span-6 annotation-checkbox-container"
>
<label>
<t k="prometheusRule.alertingRules.annotations.runbook.label" />
</label>
<div>{{ value.annotations.runbook_url }}</div>
</div>
<div v-else class="col span-6 annotation-checkbox-container">
<Checkbox
v-model="runbookAnnotationChecked"
:mode="mode"
:label="t('prometheusRule.alertingRules.annotations.runbook.label')"
/>
<LabeledInput
v-if="runbookAnnotationChecked"
v-model="value.annotations.runbook_url"
class="mt-5"
:label="t('prometheusRule.alertingRules.annotations.runbook.input')"
type="multiline"
:mode="mode"
/>
</div>
</div>
</div>
</div>
<div :class="[{ hide: isView && Object.keys(filteredAnnotations).length === 0}, 'row', 'mt-0']">
<KeyValue
key="annotations"
:value="filteredAnnotations"
:add-label="t('labels.addAnnotation')"
:mode="mode"
:read-allowed="false"
@input="queueAnnotationUpdate"
/>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.row {
margin: 20px 0;
}
.remove {
position: absolute;
top: 0;
right: 5px;
}
</style>