|
|
|
@ -40,18 +40,16 @@ import { |
|
|
|
|
deleteAlertManagerConfig, |
|
|
|
|
expireSilence, |
|
|
|
|
fetchAlertGroups, |
|
|
|
|
fetchAlertManagerConfig, |
|
|
|
|
fetchAlerts, |
|
|
|
|
fetchExternalAlertmanagerConfig, |
|
|
|
|
fetchExternalAlertmanagers, |
|
|
|
|
fetchSilences, |
|
|
|
|
fetchStatus, |
|
|
|
|
testReceivers, |
|
|
|
|
updateAlertManagerConfig, |
|
|
|
|
} from '../api/alertmanager'; |
|
|
|
|
import { alertmanagerApi } from '../api/alertmanagerApi'; |
|
|
|
|
import { fetchAnnotations } from '../api/annotations'; |
|
|
|
|
import { discoverFeatures } from '../api/buildInfo'; |
|
|
|
|
import { featureDiscoveryApi } from '../api/featureDiscoveryApi'; |
|
|
|
|
import { fetchNotifiers } from '../api/grafana'; |
|
|
|
|
import { FetchPromRulesFilter, fetchRules } from '../api/prometheus'; |
|
|
|
|
import { |
|
|
|
@ -68,17 +66,14 @@ import { |
|
|
|
|
getRulesDataSource, |
|
|
|
|
getRulesSourceName, |
|
|
|
|
GRAFANA_RULES_SOURCE_NAME, |
|
|
|
|
isVanillaPrometheusAlertManagerDataSource, |
|
|
|
|
} from '../utils/datasource'; |
|
|
|
|
import { makeAMLink, retryWhile } from '../utils/misc'; |
|
|
|
|
import { AsyncRequestMapSlice, messageFromError, withAppEvents, withSerializedError } from '../utils/redux'; |
|
|
|
|
import { makeAMLink } from '../utils/misc'; |
|
|
|
|
import { AsyncRequestMapSlice, withAppEvents, withSerializedError } from '../utils/redux'; |
|
|
|
|
import * as ruleId from '../utils/rule-id'; |
|
|
|
|
import { getRulerClient } from '../utils/rulerClient'; |
|
|
|
|
import { getAlertInfo, isRulerNotSupportedResponse } from '../utils/rules'; |
|
|
|
|
import { safeParseDurationstr } from '../utils/time'; |
|
|
|
|
|
|
|
|
|
const FETCH_CONFIG_RETRY_TIMEOUT = 30 * 1000; |
|
|
|
|
|
|
|
|
|
function getDataSourceConfig(getState: () => unknown, rulesSourceName: string) { |
|
|
|
|
const dataSources = (getState() as StoreState).unifiedAlerting.dataSources; |
|
|
|
|
const dsConfig = dataSources[rulesSourceName]?.result; |
|
|
|
@ -131,76 +126,6 @@ export const fetchPromRulesAction = createAsyncThunk( |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
export const fetchAlertManagerConfigAction = createAsyncThunk( |
|
|
|
|
'unifiedalerting/fetchAmConfig', |
|
|
|
|
(alertManagerSourceName: string, thunkAPI): Promise<AlertManagerCortexConfig> => |
|
|
|
|
withSerializedError( |
|
|
|
|
(async () => { |
|
|
|
|
// for vanilla prometheus, there is no config endpoint. Only fetch config from status
|
|
|
|
|
if (isVanillaPrometheusAlertManagerDataSource(alertManagerSourceName)) { |
|
|
|
|
return fetchStatus(alertManagerSourceName).then((status) => ({ |
|
|
|
|
alertmanager_config: status.config, |
|
|
|
|
template_files: {}, |
|
|
|
|
})); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const { data: amFeatures } = await thunkAPI.dispatch( |
|
|
|
|
featureDiscoveryApi.endpoints.discoverAmFeatures.initiate({ |
|
|
|
|
amSourceName: alertManagerSourceName, |
|
|
|
|
}) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const lazyConfigInitSupported = amFeatures?.lazyConfigInit ?? false; |
|
|
|
|
const fetchAMconfigWithLogging = withPerformanceLogging( |
|
|
|
|
fetchAlertManagerConfig, |
|
|
|
|
`[${alertManagerSourceName}] Alertmanager config loaded`, |
|
|
|
|
{ |
|
|
|
|
dataSourceName: alertManagerSourceName, |
|
|
|
|
thunk: 'unifiedalerting/fetchAmConfig', |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
return retryWhile( |
|
|
|
|
() => fetchAMconfigWithLogging(alertManagerSourceName), |
|
|
|
|
// if config has been recently deleted, it takes a while for cortex start returning the default one.
|
|
|
|
|
// retry for a short while instead of failing
|
|
|
|
|
(e) => !!messageFromError(e)?.includes('alertmanager storage object not found') && !lazyConfigInitSupported, |
|
|
|
|
FETCH_CONFIG_RETRY_TIMEOUT |
|
|
|
|
) |
|
|
|
|
.then((result) => { |
|
|
|
|
// if user config is empty for cortex alertmanager, try to get config from status endpoint
|
|
|
|
|
if ( |
|
|
|
|
isEmpty(result.alertmanager_config) && |
|
|
|
|
isEmpty(result.template_files) && |
|
|
|
|
alertManagerSourceName !== GRAFANA_RULES_SOURCE_NAME |
|
|
|
|
) { |
|
|
|
|
return fetchStatus(alertManagerSourceName).then((status) => ({ |
|
|
|
|
alertmanager_config: status.config, |
|
|
|
|
template_files: {}, |
|
|
|
|
template_file_provenances: result.template_file_provenances, |
|
|
|
|
last_applied: result.last_applied, |
|
|
|
|
id: result.id, |
|
|
|
|
})); |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
}) |
|
|
|
|
.catch((e) => { |
|
|
|
|
// When mimir doesn't have fallback AM url configured the default response will be as above
|
|
|
|
|
// However it's fine, and it's possible to create AM configuration
|
|
|
|
|
if (lazyConfigInitSupported && messageFromError(e)?.includes('alertmanager storage object not found')) { |
|
|
|
|
return Promise.resolve<AlertManagerCortexConfig>({ |
|
|
|
|
alertmanager_config: {}, |
|
|
|
|
template_files: {}, |
|
|
|
|
template_file_provenances: {}, |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
throw e; |
|
|
|
|
}); |
|
|
|
|
})() |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
export const fetchExternalAlertmanagersAction = createAsyncThunk( |
|
|
|
|
'unifiedAlerting/fetchExternalAlertmanagers', |
|
|
|
|
(): Promise<ExternalAlertmanagersResponse> => { |
|
|
|
@ -585,8 +510,9 @@ export const updateAlertManagerConfigAction = createAsyncThunk<void, UpdateAlert |
|
|
|
|
withAppEvents( |
|
|
|
|
withSerializedError( |
|
|
|
|
(async () => { |
|
|
|
|
// TODO there must be a better way here than to dispatch another fetch as this causes re-rendering :(
|
|
|
|
|
const latestConfig = await thunkAPI.dispatch(fetchAlertManagerConfigAction(alertManagerSourceName)).unwrap(); |
|
|
|
|
const latestConfig = await thunkAPI |
|
|
|
|
.dispatch(alertmanagerApi.endpoints.getAlertmanagerConfiguration.initiate(alertManagerSourceName)) |
|
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
const isLatestConfigEmpty = isEmpty(latestConfig.alertmanager_config) && isEmpty(latestConfig.template_files); |
|
|
|
|
const oldLastConfigsDiffer = JSON.stringify(latestConfig) !== JSON.stringify(oldConfig); |
|
|
|
@ -598,7 +524,7 @@ export const updateAlertManagerConfigAction = createAsyncThunk<void, UpdateAlert |
|
|
|
|
} |
|
|
|
|
await updateAlertManagerConfig(alertManagerSourceName, addDefaultsToAlertmanagerConfig(newConfig)); |
|
|
|
|
if (refetch) { |
|
|
|
|
await thunkAPI.dispatch(fetchAlertManagerConfigAction(alertManagerSourceName)); |
|
|
|
|
thunkAPI.dispatch(alertmanagerApi.util.invalidateTags(['AlertmanagerConfiguration'])); |
|
|
|
|
} |
|
|
|
|
if (redirectPath) { |
|
|
|
|
const options = new URLSearchParams(redirectSearch ?? ''); |
|
|
|
@ -654,8 +580,11 @@ export const createOrUpdateSilenceAction = createAsyncThunk<void, UpdateSilenceA |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
export const deleteReceiverAction = (receiverName: string, alertManagerSourceName: string): ThunkResult<void> => { |
|
|
|
|
return (dispatch, getState) => { |
|
|
|
|
const config = getState().unifiedAlerting.amConfigs?.[alertManagerSourceName]?.result; |
|
|
|
|
return async (dispatch) => { |
|
|
|
|
const config = await dispatch( |
|
|
|
|
alertmanagerApi.endpoints.getAlertmanagerConfiguration.initiate(alertManagerSourceName) |
|
|
|
|
).unwrap(); |
|
|
|
|
|
|
|
|
|
if (!config) { |
|
|
|
|
throw new Error(`Config for ${alertManagerSourceName} not found`); |
|
|
|
|
} |
|
|
|
@ -682,8 +611,11 @@ export const deleteReceiverAction = (receiverName: string, alertManagerSourceNam |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
export const deleteTemplateAction = (templateName: string, alertManagerSourceName: string): ThunkResult<void> => { |
|
|
|
|
return (dispatch, getState) => { |
|
|
|
|
const config = getState().unifiedAlerting.amConfigs?.[alertManagerSourceName]?.result; |
|
|
|
|
return async (dispatch) => { |
|
|
|
|
const config = await dispatch( |
|
|
|
|
alertmanagerApi.endpoints.getAlertmanagerConfiguration.initiate(alertManagerSourceName) |
|
|
|
|
).unwrap(); |
|
|
|
|
|
|
|
|
|
if (!config) { |
|
|
|
|
throw new Error(`Config for ${alertManagerSourceName} not found`); |
|
|
|
|
} |
|
|
|
@ -739,7 +671,7 @@ export const deleteAlertManagerConfigAction = createAsyncThunk( |
|
|
|
|
withSerializedError( |
|
|
|
|
(async () => { |
|
|
|
|
await deleteAlertManagerConfig(alertManagerSourceName); |
|
|
|
|
await thunkAPI.dispatch(fetchAlertManagerConfigAction(alertManagerSourceName)); |
|
|
|
|
await thunkAPI.dispatch(alertmanagerApi.util.invalidateTags(['AlertmanagerConfiguration'])); |
|
|
|
|
})() |
|
|
|
|
), |
|
|
|
|
{ |
|
|
|
@ -751,8 +683,10 @@ export const deleteAlertManagerConfigAction = createAsyncThunk( |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
export const deleteMuteTimingAction = (alertManagerSourceName: string, muteTimingName: string): ThunkResult<void> => { |
|
|
|
|
return async (dispatch, getState) => { |
|
|
|
|
const config = getState().unifiedAlerting.amConfigs[alertManagerSourceName].result; |
|
|
|
|
return async (dispatch) => { |
|
|
|
|
const config = await dispatch( |
|
|
|
|
alertmanagerApi.endpoints.getAlertmanagerConfiguration.initiate(alertManagerSourceName) |
|
|
|
|
).unwrap(); |
|
|
|
|
|
|
|
|
|
const muteIntervals = |
|
|
|
|
config?.alertmanager_config?.mute_time_intervals?.filter(({ name }) => name !== muteTimingName) ?? []; |
|
|
|
|