AlertmanagerConfig.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { css } from '@emotion/css';
  2. import React, { useEffect, useState, useMemo } from 'react';
  3. import { useDispatch } from 'react-redux';
  4. import { GrafanaTheme2 } from '@grafana/data';
  5. import { Alert, Button, ConfirmModal, TextArea, HorizontalGroup, Field, Form, useStyles2 } from '@grafana/ui';
  6. import { useAlertManagerSourceName } from '../../hooks/useAlertManagerSourceName';
  7. import { useAlertManagersByPermission } from '../../hooks/useAlertManagerSources';
  8. import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector';
  9. import {
  10. deleteAlertManagerConfigAction,
  11. fetchAlertManagerConfigAction,
  12. updateAlertManagerConfigAction,
  13. } from '../../state/actions';
  14. import { GRAFANA_RULES_SOURCE_NAME, isVanillaPrometheusAlertManagerDataSource } from '../../utils/datasource';
  15. import { initialAsyncRequestState } from '../../utils/redux';
  16. import { AlertManagerPicker } from '../AlertManagerPicker';
  17. interface FormValues {
  18. configJSON: string;
  19. }
  20. export default function AlertmanagerConfig(): JSX.Element {
  21. const dispatch = useDispatch();
  22. const alertManagers = useAlertManagersByPermission('notification');
  23. const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
  24. const [showConfirmDeleteAMConfig, setShowConfirmDeleteAMConfig] = useState(false);
  25. const { loading: isDeleting } = useUnifiedAlertingSelector((state) => state.deleteAMConfig);
  26. const { loading: isSaving } = useUnifiedAlertingSelector((state) => state.saveAMConfig);
  27. const readOnly = alertManagerSourceName ? isVanillaPrometheusAlertManagerDataSource(alertManagerSourceName) : false;
  28. const styles = useStyles2(getStyles);
  29. const configRequests = useUnifiedAlertingSelector((state) => state.amConfigs);
  30. const {
  31. result: config,
  32. loading: isLoadingConfig,
  33. error: loadingError,
  34. } = (alertManagerSourceName && configRequests[alertManagerSourceName]) || initialAsyncRequestState;
  35. useEffect(() => {
  36. if (alertManagerSourceName) {
  37. dispatch(fetchAlertManagerConfigAction(alertManagerSourceName));
  38. }
  39. }, [alertManagerSourceName, dispatch]);
  40. const resetConfig = () => {
  41. if (alertManagerSourceName) {
  42. dispatch(deleteAlertManagerConfigAction(alertManagerSourceName));
  43. }
  44. setShowConfirmDeleteAMConfig(false);
  45. };
  46. const defaultValues = useMemo(
  47. (): FormValues => ({
  48. configJSON: config ? JSON.stringify(config, null, 2) : '',
  49. }),
  50. [config]
  51. );
  52. const loading = isDeleting || isLoadingConfig || isSaving;
  53. const onSubmit = (values: FormValues) => {
  54. if (alertManagerSourceName && config) {
  55. dispatch(
  56. updateAlertManagerConfigAction({
  57. newConfig: JSON.parse(values.configJSON),
  58. oldConfig: config,
  59. alertManagerSourceName,
  60. successMessage: 'Alertmanager configuration updated.',
  61. refetch: true,
  62. })
  63. );
  64. }
  65. };
  66. return (
  67. <div className={styles.container}>
  68. <AlertManagerPicker
  69. current={alertManagerSourceName}
  70. onChange={setAlertManagerSourceName}
  71. dataSources={alertManagers}
  72. />
  73. {loadingError && !loading && (
  74. <Alert severity="error" title="Error loading Alertmanager configuration">
  75. {loadingError.message || 'Unknown error.'}
  76. </Alert>
  77. )}
  78. {isDeleting && alertManagerSourceName !== GRAFANA_RULES_SOURCE_NAME && (
  79. <Alert severity="info" title="Resetting Alertmanager configuration">
  80. It might take a while...
  81. </Alert>
  82. )}
  83. {alertManagerSourceName && config && (
  84. <Form defaultValues={defaultValues} onSubmit={onSubmit} key={defaultValues.configJSON}>
  85. {({ register, errors }) => (
  86. <>
  87. {!readOnly && (
  88. <Field
  89. disabled={loading}
  90. label="Configuration"
  91. invalid={!!errors.configJSON}
  92. error={errors.configJSON?.message}
  93. >
  94. <TextArea
  95. {...register('configJSON', {
  96. required: { value: true, message: 'Required.' },
  97. validate: (v) => {
  98. try {
  99. JSON.parse(v);
  100. return true;
  101. } catch (e) {
  102. return e.message;
  103. }
  104. },
  105. })}
  106. id="configuration"
  107. rows={25}
  108. />
  109. </Field>
  110. )}
  111. {readOnly && (
  112. <Field label="Configuration">
  113. <pre data-testid="readonly-config">{defaultValues.configJSON}</pre>
  114. </Field>
  115. )}
  116. {!readOnly && (
  117. <HorizontalGroup>
  118. <Button type="submit" variant="primary" disabled={loading}>
  119. Save
  120. </Button>
  121. <Button
  122. type="button"
  123. disabled={loading}
  124. variant="destructive"
  125. onClick={() => setShowConfirmDeleteAMConfig(true)}
  126. >
  127. Reset configuration
  128. </Button>
  129. </HorizontalGroup>
  130. )}
  131. {!!showConfirmDeleteAMConfig && (
  132. <ConfirmModal
  133. isOpen={true}
  134. title="Reset Alertmanager configuration"
  135. body={`Are you sure you want to reset configuration ${
  136. alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME
  137. ? 'for the Grafana Alertmanager'
  138. : `for "${alertManagerSourceName}"`
  139. }? Contact points and notification policies will be reset to their defaults.`}
  140. confirmText="Yes, reset configuration"
  141. onConfirm={resetConfig}
  142. onDismiss={() => setShowConfirmDeleteAMConfig(false)}
  143. />
  144. )}
  145. </>
  146. )}
  147. </Form>
  148. )}
  149. </div>
  150. );
  151. }
  152. const getStyles = (theme: GrafanaTheme2) => ({
  153. container: css`
  154. margin-bottom: ${theme.spacing(4)};
  155. `,
  156. });