diff --git a/web/locales/en/plugin__monitoring-plugin.json b/web/locales/en/plugin__monitoring-plugin.json index f665d086..2f7a72a1 100644 --- a/web/locales/en/plugin__monitoring-plugin.json +++ b/web/locales/en/plugin__monitoring-plugin.json @@ -83,6 +83,10 @@ "1d": "1d", "2d": "2d", "1w": "1w", + "Comment is required.": "Comment is required.", + "Alertmanager URL not set": "Alertmanager URL not set", + "Error saving Silence": "Error saving Silence", + "Forbidden: Missing permissions for silences": "Forbidden: Missing permissions for silences", "Silences temporarily mute alerts based on a set of label selectors that you define. Notifications will not be sent for alerts that match all the listed values or regular expressions.": "Silences temporarily mute alerts based on a set of label selectors that you define. Notifications will not be sent for alerts that match all the listed values or regular expressions.", "Duration": "Duration", "Silence alert from...": "Silence alert from...", @@ -135,9 +139,6 @@ "404: Not Found": "404: Not Found", "{{labels}} content is not available in the catalog at this time due to loading failures.": "{{labels}} content is not available in the catalog at this time due to loading failures.", "No datapoints found.": "No datapoints found.", - "Namespaces": "Namespaces", - "Project": "Project", - "Projects": "Projects", "Create new option \"{{option}}\"": "Create new option \"{{option}}\"", "Filter options": "Filter options", "Clear input value": "Clear input value", @@ -177,6 +178,8 @@ "No results match the filter criteria.": "No results match the filter criteria.", "Clear filters": "Clear filters", "Select project...": "Select project...", + "Projects": "Projects", + "Project": "Project", "Dashboard": "Dashboard", "Refresh off": "Refresh off", "{{count}} second_one": "{{count}} second", diff --git a/web/src/components/alerting/SilenceForm.tsx b/web/src/components/alerting/SilenceForm.tsx index 50c78275..46915709 100644 --- a/web/src/components/alerting/SilenceForm.tsx +++ b/web/src/components/alerting/SilenceForm.tsx @@ -1,7 +1,6 @@ import { consoleFetchJSON, DocumentTitle, - NamespaceBar, useActiveNamespace, } from '@openshift-console/dynamic-plugin-sdk'; import { @@ -51,7 +50,7 @@ import { ExternalLink } from '../console/utils/link'; import { useBoolean } from '../hooks/useBoolean'; import { getSilenceAlertUrl, usePerspective } from '../hooks/usePerspective'; import { DataTestIDs } from '../data-test'; -import { getAlertmanagerSilencesUrl } from '../utils'; +import { ALL_NAMESPACES_KEY, getAlertmanagerSilencesUrl } from '../utils'; import { useAlerts } from '../../hooks/useAlerts'; import { useMonitoring } from '../../hooks/useMonitoring'; @@ -136,6 +135,7 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace const [namespace] = useActiveNamespace(); const { prometheus } = useMonitoring(); const navigate = useNavigate(); + const isPageNamespaceLocked = isNamespaced && namespace !== ALL_NAMESPACES_KEY; const durations = useMemo(() => { return { @@ -187,7 +187,7 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace // Since the namespace matcher MUST be the same as the namespace the request is being // made in, we remove the namespace value here and re-add it before sending the request const [matchers, setMatchers] = useState>( - (isNamespaced + (isPageNamespaceLocked ? (defaults.matchers as Matcher[])?.filter((matcher) => matcher.name !== 'namespace') : defaults.matchers) ?? [{ isRegex: false, isEqual: true, name: '', value: '' }], ); @@ -211,7 +211,7 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace }; const setMatcherField = (i: number, field: string, v: string | boolean): void => { - const newMatchers = _.clone(matchers); + const newMatchers = _.cloneDeep(matchers); _.set(newMatchers, [i, field], v); setMatchers(newMatchers); }; @@ -221,11 +221,6 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace }; const removeMatcher = (i: number): void => { - // If we require the namespace don't allow removing it - if (isNamespaced && i === 0) { - return; - } - const newMatchers = _.clone(matchers); newMatchers.splice(i, 1); @@ -242,17 +237,17 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace // Don't allow comments to only contain whitespace if (_.trim(comment) === '') { - setError('Comment is required.'); + setError(t('Comment is required.')); return; } const url = getAlertmanagerSilencesUrl({ prometheus, namespace, - useTenancyPath: isNamespaced, + useTenancyPath: isPageNamespaceLocked, }); if (!url) { - setError('Alertmanager URL not set'); + setError(t('Alertmanager URL not set')); return; } @@ -269,7 +264,7 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace createdBy, endsAt: saveEndsAt.toISOString(), id: defaults.id, - matchers: isNamespaced + matchers: isPageNamespaceLocked ? matchers.concat({ name: 'namespace', value: namespace, @@ -282,7 +277,11 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace consoleFetchJSON .post( - getAlertmanagerSilencesUrl({ prometheus, namespace, useTenancyPath: isNamespaced }), + getAlertmanagerSilencesUrl({ + prometheus, + namespace, + useTenancyPath: isPageNamespaceLocked, + }), body, ) .then(({ silenceID }) => { @@ -291,10 +290,13 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace navigate(getSilenceAlertUrl(perspective, silenceID)); }) .catch((err) => { - const errorMessage = + let errorMessage = typeof _.get(err, 'json') === 'string' ? _.get(err, 'json') - : err.message || 'Error saving Silence'; + : err.message || t('Error saving Silence'); + if (errorMessage === 'Forbidden') { + errorMessage = t('Forbidden: Missing permissions for silences'); + } setError(errorMessage); setInProgress(false); }); @@ -309,7 +311,6 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace return ( <> {title} - {isNamespaced && } {title} @@ -425,7 +426,7 @@ const SilenceForm_: FC = ({ defaults, Info, title, isNamespace - {isNamespaced && ( + {isPageNamespaceLocked && (