grafana/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts

507 lines
18 KiB
TypeScript
Raw Normal View History

/**
* This hook will combine data from both the Alertmanager config
* and (if available) it will also fetch the status from the Grafana Managed status endpoint
*/
import { merge, set } from 'lodash';
import { useMemo } from 'react';
import { receiversApi } from 'app/features/alerting/unified/api/receiversK8sApi';
import { useOnCallIntegration } from 'app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration';
import { ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver } from 'app/features/alerting/unified/openapi/receiversApi.gen';
import { BaseAlertmanagerArgs, Skippable } from 'app/features/alerting/unified/types/hooks';
import { cloudNotifierTypes } from 'app/features/alerting/unified/utils/cloud-alertmanager-notifier-types';
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
import {
getK8sNamespace,
isK8sEntityProvisioned,
shouldUseK8sApi,
} from 'app/features/alerting/unified/utils/k8s/utils';
import {
GrafanaManagedContactPoint,
GrafanaManagedReceiverConfig,
Receiver,
} from 'app/plugins/datasource/alertmanager/types';
import { alertmanagerApi } from '../../api/alertmanagerApi';
import { onCallApi } from '../../api/onCallApi';
import { useAsync } from '../../hooks/useAsync';
import { usePluginBridge } from '../../hooks/usePluginBridge';
import { useProduceNewAlertmanagerConfiguration } from '../../hooks/useProduceNewAlertmanagerConfig';
import { addReceiverAction, deleteReceiverAction, updateReceiverAction } from '../../reducers/alertmanager/receivers';
import { SupportedPlugin } from '../../types/pluginBridges';
import { enhanceContactPointsWithMetadata } from './utils';
const RECEIVER_STATUS_POLLING_INTERVAL = 10 * 1000; // 10 seconds
/**
* This hook will combine data from several endpoints;
* 1. the alertmanager config endpoint where the definition of the receivers are
* 2. (if available) the alertmanager receiver status endpoint, currently Grafana Managed only
* 3. (if available) additional metadata about Grafana Managed contact points
* 4. (if available) the OnCall plugin metadata
*/
const {
useGetAlertmanagerConfigurationQuery,
useGetContactPointsListQuery,
useGetContactPointsStatusQuery,
useGrafanaNotifiersQuery,
useLazyGetAlertmanagerConfigurationQuery,
} = alertmanagerApi;
const { useGrafanaOnCallIntegrationsQuery } = onCallApi;
const {
useListNamespacedReceiverQuery,
useReadNamespacedReceiverQuery,
useDeleteNamespacedReceiverMutation,
useCreateNamespacedReceiverMutation,
useReplaceNamespacedReceiverMutation,
} = receiversApi;
const defaultOptions = {
refetchOnFocus: true,
refetchOnReconnect: true,
};
/**
* Check if OnCall is installed, and fetch the list of integrations if so.
*
* Otherwise, returns no data
*/
const useOnCallIntegrations = ({ skip }: Skippable = {}) => {
const { installed, loading } = usePluginBridge(SupportedPlugin.OnCall);
const oncallIntegrationsResponse = useGrafanaOnCallIntegrationsQuery(undefined, { skip: skip || !installed });
return useMemo(() => {
if (installed) {
return oncallIntegrationsResponse;
}
return {
isLoading: loading,
data: undefined,
};
}, [installed, loading, oncallIntegrationsResponse]);
};
type K8sReceiver = ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver;
const parseK8sReceiver = (item: K8sReceiver): GrafanaManagedContactPoint => {
return {
id: item.metadata.uid || item.spec.title,
name: item.spec.title,
provisioned: isK8sEntityProvisioned(item),
grafana_managed_receiver_configs: item.spec.integrations,
metadata: item.metadata,
};
};
const useK8sContactPoints = (...[hookParams, queryOptions]: Parameters<typeof useListNamespacedReceiverQuery>) => {
return useListNamespacedReceiverQuery(hookParams, {
...queryOptions,
selectFromResult: (result) => {
const data = result.data?.items.map((item) => parseK8sReceiver(item));
return {
...result,
Alerting: Add manage permissions UI logic for Contact Points (#92885) * Add showPolicies prop * Add manage permissions component for easier reuse within alerting * Add method for checking whether to show access control within alerting * Remove accidental console.log from main * Tweak styling for contact point width and add manage permissions drawer * Improve typing for access control type response * Add basic test for manage permissions on contact points list * Only show manage permissions if grafana AM and alertingApiServer is enabled * Update i18n * Add test utils for turning features on and back off * Add access control handlers * Update tests with new util * Pass AM in and add tests * Receiver OSS resource permissions There is a complication that is not fully addressed: Viewer defaults to read:* and Editor defaults to read+write+delete:* This is different to other resource permissions where non-admin are not granted any global permissions and instead access is handled solely by resource-specific permissions that are populated on create and removed on delete. This allows them to easily remove permission to view or edit a single resource from basic roles. The reason this is tricky here is that we have multiple APIs that can create/delete receivers: config api, provisioning api, and k8s receivers api. Config api in particular is not well-equipped to determine when creates/deletes are happening and thus ensuring that the proper resource-specific permissions are created/deleted is finicky. We would also have to create a migration to populate resource-specific permissions for all current receivers. This migration would need to be reset so it can run again if the flag is disabled. * Add access control permissions * Pass in contact point ID to receivers form * Temporarily remove access control check for contact points * Include access control metadata in k8s receiver List & Get GET: Always included. LIST: Included by adding a label selector with value `grafana.com/accessControl` * Include new permissions for contact points navbar * Fix receiver creator fixed role to not give global read * Include in-use metadata in k8s receiver List & Get GET: Always included. LIST: Included by adding a label selector with value `grafana.com/inUse` * Add receiver creator permission to receiver writer * Add receiver creator permission to navbar * Always allow listing receivers, don't return 403 * Remove receiver read precondition from receiver create Otherwise, Creator role will not be able to create their first receiver * Update routes permissions * Add further support for RBAC in contact points * Update routes permissions * Update contact points header logic * Back out test feature toggle refactor Not working atm, not sure why * Tidy up imports * Update mock permissions * Revert more test changes * Update i18n * Sync inuse metadata pr * Add back canAdmin permissions after main merge * Split out check for policies navtree item * Tidy up utils and imports and fix rules in use * Fix contact point tests and act warnings * Add missing ReceiverPermissionAdmin after merge conflict * Move contact points permissions * Only show contact points filter when permissions are correct * Move to constants * Fallback to empty array and remove labelSelectors (not needed) * Allow `toAbility` to take multiple actions * Show builtin alertmanager if contact points permission * Add empty state and hide templates if missing permissions * Translations * Tidy up mock data * Fix tests and templates permission * Update message for unused contact points * Don't return 403 when user lists receivers and has access to none * Fix receiver create not adding empty uid permissions * Move SetDefaultPermissions to ReceiverPermissionService * Have SetDefaultPermissions use uid from string Fixes circular dependency * Add FakeReceiverPermissionsService and fix test wiring * Implement resource permission handling in provisioning API and renames Create: Sets to default permissions Delete: Removes permissions Update: If receiver name is modified and the new name doesn't exist, it copies the permissions from the old receiver to the newly created one. If old receiver is now empty, it removes the old permissions as well. * Split contact point permissions checks for read/modify * Generalise getting annotation values from k8s entities * Proxy RouteDeleteAlertingConfig through MultiOrgAlertmanager * Cleanup permissions on config api reset and restore * Cleanup permissions on config api POST note this is still not available with feature flag enabled * Gate the permission manager behind FF until initial migration is added * Sync changes from config api PR * Switch to named export * Revert unnecessary changes * Revert Filter auth change and implement in k8s api only * Don't allow new scoped permissions to give access without FF Prevents complications around mixed support for the scoped permissions causing oddities in the UI. * Fix integration tests to account for list permission change * Move to `permissions` file * Add additional tests for contact points * Fix redirect for viewer on edit page * Combine alerting test utils and move to new file location * Allow new permissions to access provisioning export paths with FF * Always allow exporting if its grafana flavoured * Fix logic for showing auto generated policies * Fix delete logic for contact point only referenced by a rule * Suppress warning message when renaming a contact point * Clear team and role perm cache on receiver rename Prevents temporarily broken UI permissions after rename when a user's source of elevated permissions comes from a cached team or basic role permission. * Debug log failed cache clear on CopyPermissions --------- Co-authored-by: Matt Jacobson <matthew.jacobson@grafana.com>
2024-09-28 02:56:32 +08:00
// K8S API will return 403 error for no-permissions case, so its cleaner to fallback to empty array
data: result.error ? [] : data,
currentData: data,
};
},
});
};
/**
* Fetch contact points for Grafana Alertmanager, either from the k8s API,
* or the `/notifications/receivers` endpoint
*/
const useFetchGrafanaContactPoints = ({ skip }: Skippable = {}) => {
const namespace = getK8sNamespace();
const useK8sApi = shouldUseK8sApi(GRAFANA_RULES_SOURCE_NAME);
const grafanaResponse = useGetContactPointsListQuery(undefined, {
skip: skip || useK8sApi,
selectFromResult: (result) => {
const data = result.data?.map((item) => ({
...item,
provisioned: item.grafana_managed_receiver_configs?.some((item) => item.provenance),
}));
return {
...result,
data,
currentData: data,
};
},
});
const k8sResponse = useK8sContactPoints({ namespace }, { skip: skip || !useK8sApi });
return useK8sApi ? k8sResponse : grafanaResponse;
};
type GrafanaFetchOptions = {
/**
* Should we fetch and include status information about each contact point?
*/
fetchStatuses?: boolean;
/**
* Should we fetch and include the number of notification policies that reference each contact point?
*/
fetchPolicies?: boolean;
};
/**
* Fetch list of contact points from separate endpoint (i.e. not the Alertmanager config) and combine with
* OnCall integrations and any additional metadata from list of notifiers
* (e.g. hydrate with additional names/descriptions)
*/
export const useGrafanaContactPoints = ({
fetchStatuses,
fetchPolicies,
skip,
}: GrafanaFetchOptions & Skippable = {}) => {
const potentiallySkip = { skip };
const onCallResponse = useOnCallIntegrations(potentiallySkip);
const alertNotifiers = useGrafanaNotifiersQuery(undefined, potentiallySkip);
const contactPointsListResponse = useFetchGrafanaContactPoints(potentiallySkip);
Alerting: Add manage permissions UI logic for Contact Points (#92885) * Add showPolicies prop * Add manage permissions component for easier reuse within alerting * Add method for checking whether to show access control within alerting * Remove accidental console.log from main * Tweak styling for contact point width and add manage permissions drawer * Improve typing for access control type response * Add basic test for manage permissions on contact points list * Only show manage permissions if grafana AM and alertingApiServer is enabled * Update i18n * Add test utils for turning features on and back off * Add access control handlers * Update tests with new util * Pass AM in and add tests * Receiver OSS resource permissions There is a complication that is not fully addressed: Viewer defaults to read:* and Editor defaults to read+write+delete:* This is different to other resource permissions where non-admin are not granted any global permissions and instead access is handled solely by resource-specific permissions that are populated on create and removed on delete. This allows them to easily remove permission to view or edit a single resource from basic roles. The reason this is tricky here is that we have multiple APIs that can create/delete receivers: config api, provisioning api, and k8s receivers api. Config api in particular is not well-equipped to determine when creates/deletes are happening and thus ensuring that the proper resource-specific permissions are created/deleted is finicky. We would also have to create a migration to populate resource-specific permissions for all current receivers. This migration would need to be reset so it can run again if the flag is disabled. * Add access control permissions * Pass in contact point ID to receivers form * Temporarily remove access control check for contact points * Include access control metadata in k8s receiver List & Get GET: Always included. LIST: Included by adding a label selector with value `grafana.com/accessControl` * Include new permissions for contact points navbar * Fix receiver creator fixed role to not give global read * Include in-use metadata in k8s receiver List & Get GET: Always included. LIST: Included by adding a label selector with value `grafana.com/inUse` * Add receiver creator permission to receiver writer * Add receiver creator permission to navbar * Always allow listing receivers, don't return 403 * Remove receiver read precondition from receiver create Otherwise, Creator role will not be able to create their first receiver * Update routes permissions * Add further support for RBAC in contact points * Update routes permissions * Update contact points header logic * Back out test feature toggle refactor Not working atm, not sure why * Tidy up imports * Update mock permissions * Revert more test changes * Update i18n * Sync inuse metadata pr * Add back canAdmin permissions after main merge * Split out check for policies navtree item * Tidy up utils and imports and fix rules in use * Fix contact point tests and act warnings * Add missing ReceiverPermissionAdmin after merge conflict * Move contact points permissions * Only show contact points filter when permissions are correct * Move to constants * Fallback to empty array and remove labelSelectors (not needed) * Allow `toAbility` to take multiple actions * Show builtin alertmanager if contact points permission * Add empty state and hide templates if missing permissions * Translations * Tidy up mock data * Fix tests and templates permission * Update message for unused contact points * Don't return 403 when user lists receivers and has access to none * Fix receiver create not adding empty uid permissions * Move SetDefaultPermissions to ReceiverPermissionService * Have SetDefaultPermissions use uid from string Fixes circular dependency * Add FakeReceiverPermissionsService and fix test wiring * Implement resource permission handling in provisioning API and renames Create: Sets to default permissions Delete: Removes permissions Update: If receiver name is modified and the new name doesn't exist, it copies the permissions from the old receiver to the newly created one. If old receiver is now empty, it removes the old permissions as well. * Split contact point permissions checks for read/modify * Generalise getting annotation values from k8s entities * Proxy RouteDeleteAlertingConfig through MultiOrgAlertmanager * Cleanup permissions on config api reset and restore * Cleanup permissions on config api POST note this is still not available with feature flag enabled * Gate the permission manager behind FF until initial migration is added * Sync changes from config api PR * Switch to named export * Revert unnecessary changes * Revert Filter auth change and implement in k8s api only * Don't allow new scoped permissions to give access without FF Prevents complications around mixed support for the scoped permissions causing oddities in the UI. * Fix integration tests to account for list permission change * Move to `permissions` file * Add additional tests for contact points * Fix redirect for viewer on edit page * Combine alerting test utils and move to new file location * Allow new permissions to access provisioning export paths with FF * Always allow exporting if its grafana flavoured * Fix logic for showing auto generated policies * Fix delete logic for contact point only referenced by a rule * Suppress warning message when renaming a contact point * Clear team and role perm cache on receiver rename Prevents temporarily broken UI permissions after rename when a user's source of elevated permissions comes from a cached team or basic role permission. * Debug log failed cache clear on CopyPermissions --------- Co-authored-by: Matt Jacobson <matthew.jacobson@grafana.com>
2024-09-28 02:56:32 +08:00
const contactPointsStatusResponse = useGetContactPointsStatusQuery(undefined, {
...defaultOptions,
pollingInterval: RECEIVER_STATUS_POLLING_INTERVAL,
skip: skip || !fetchStatuses,
});
const alertmanagerConfigResponse = useGetAlertmanagerConfigurationQuery(GRAFANA_RULES_SOURCE_NAME, {
skip: skip || !fetchPolicies,
});
return useMemo(() => {
const isLoading = onCallResponse.isLoading || alertNotifiers.isLoading || contactPointsListResponse.isLoading;
Alerting: Add manage permissions UI logic for Contact Points (#92885) * Add showPolicies prop * Add manage permissions component for easier reuse within alerting * Add method for checking whether to show access control within alerting * Remove accidental console.log from main * Tweak styling for contact point width and add manage permissions drawer * Improve typing for access control type response * Add basic test for manage permissions on contact points list * Only show manage permissions if grafana AM and alertingApiServer is enabled * Update i18n * Add test utils for turning features on and back off * Add access control handlers * Update tests with new util * Pass AM in and add tests * Receiver OSS resource permissions There is a complication that is not fully addressed: Viewer defaults to read:* and Editor defaults to read+write+delete:* This is different to other resource permissions where non-admin are not granted any global permissions and instead access is handled solely by resource-specific permissions that are populated on create and removed on delete. This allows them to easily remove permission to view or edit a single resource from basic roles. The reason this is tricky here is that we have multiple APIs that can create/delete receivers: config api, provisioning api, and k8s receivers api. Config api in particular is not well-equipped to determine when creates/deletes are happening and thus ensuring that the proper resource-specific permissions are created/deleted is finicky. We would also have to create a migration to populate resource-specific permissions for all current receivers. This migration would need to be reset so it can run again if the flag is disabled. * Add access control permissions * Pass in contact point ID to receivers form * Temporarily remove access control check for contact points * Include access control metadata in k8s receiver List & Get GET: Always included. LIST: Included by adding a label selector with value `grafana.com/accessControl` * Include new permissions for contact points navbar * Fix receiver creator fixed role to not give global read * Include in-use metadata in k8s receiver List & Get GET: Always included. LIST: Included by adding a label selector with value `grafana.com/inUse` * Add receiver creator permission to receiver writer * Add receiver creator permission to navbar * Always allow listing receivers, don't return 403 * Remove receiver read precondition from receiver create Otherwise, Creator role will not be able to create their first receiver * Update routes permissions * Add further support for RBAC in contact points * Update routes permissions * Update contact points header logic * Back out test feature toggle refactor Not working atm, not sure why * Tidy up imports * Update mock permissions * Revert more test changes * Update i18n * Sync inuse metadata pr * Add back canAdmin permissions after main merge * Split out check for policies navtree item * Tidy up utils and imports and fix rules in use * Fix contact point tests and act warnings * Add missing ReceiverPermissionAdmin after merge conflict * Move contact points permissions * Only show contact points filter when permissions are correct * Move to constants * Fallback to empty array and remove labelSelectors (not needed) * Allow `toAbility` to take multiple actions * Show builtin alertmanager if contact points permission * Add empty state and hide templates if missing permissions * Translations * Tidy up mock data * Fix tests and templates permission * Update message for unused contact points * Don't return 403 when user lists receivers and has access to none * Fix receiver create not adding empty uid permissions * Move SetDefaultPermissions to ReceiverPermissionService * Have SetDefaultPermissions use uid from string Fixes circular dependency * Add FakeReceiverPermissionsService and fix test wiring * Implement resource permission handling in provisioning API and renames Create: Sets to default permissions Delete: Removes permissions Update: If receiver name is modified and the new name doesn't exist, it copies the permissions from the old receiver to the newly created one. If old receiver is now empty, it removes the old permissions as well. * Split contact point permissions checks for read/modify * Generalise getting annotation values from k8s entities * Proxy RouteDeleteAlertingConfig through MultiOrgAlertmanager * Cleanup permissions on config api reset and restore * Cleanup permissions on config api POST note this is still not available with feature flag enabled * Gate the permission manager behind FF until initial migration is added * Sync changes from config api PR * Switch to named export * Revert unnecessary changes * Revert Filter auth change and implement in k8s api only * Don't allow new scoped permissions to give access without FF Prevents complications around mixed support for the scoped permissions causing oddities in the UI. * Fix integration tests to account for list permission change * Move to `permissions` file * Add additional tests for contact points * Fix redirect for viewer on edit page * Combine alerting test utils and move to new file location * Allow new permissions to access provisioning export paths with FF * Always allow exporting if its grafana flavoured * Fix logic for showing auto generated policies * Fix delete logic for contact point only referenced by a rule * Suppress warning message when renaming a contact point * Clear team and role perm cache on receiver rename Prevents temporarily broken UI permissions after rename when a user's source of elevated permissions comes from a cached team or basic role permission. * Debug log failed cache clear on CopyPermissions --------- Co-authored-by: Matt Jacobson <matthew.jacobson@grafana.com>
2024-09-28 02:56:32 +08:00
if (isLoading) {
return {
...contactPointsListResponse,
// If we're inside this block, it means that at least one of the endpoints we care about is still loading,
// but the contactPointsListResponse may have in fact finished.
// If we were to use _that_ loading state, it might be inaccurate elsewhere when consuming this hook,
// so we explicitly say "yes, this is definitely still loading"
isLoading: true,
contactPoints: [],
};
}
const enhanced = enhanceContactPointsWithMetadata({
status: contactPointsStatusResponse.data,
notifiers: alertNotifiers.data,
onCallIntegrations: onCallResponse?.data,
Alerting: Add manage permissions UI logic for Contact Points (#92885) * Add showPolicies prop * Add manage permissions component for easier reuse within alerting * Add method for checking whether to show access control within alerting * Remove accidental console.log from main * Tweak styling for contact point width and add manage permissions drawer * Improve typing for access control type response * Add basic test for manage permissions on contact points list * Only show manage permissions if grafana AM and alertingApiServer is enabled * Update i18n * Add test utils for turning features on and back off * Add access control handlers * Update tests with new util * Pass AM in and add tests * Receiver OSS resource permissions There is a complication that is not fully addressed: Viewer defaults to read:* and Editor defaults to read+write+delete:* This is different to other resource permissions where non-admin are not granted any global permissions and instead access is handled solely by resource-specific permissions that are populated on create and removed on delete. This allows them to easily remove permission to view or edit a single resource from basic roles. The reason this is tricky here is that we have multiple APIs that can create/delete receivers: config api, provisioning api, and k8s receivers api. Config api in particular is not well-equipped to determine when creates/deletes are happening and thus ensuring that the proper resource-specific permissions are created/deleted is finicky. We would also have to create a migration to populate resource-specific permissions for all current receivers. This migration would need to be reset so it can run again if the flag is disabled. * Add access control permissions * Pass in contact point ID to receivers form * Temporarily remove access control check for contact points * Include access control metadata in k8s receiver List & Get GET: Always included. LIST: Included by adding a label selector with value `grafana.com/accessControl` * Include new permissions for contact points navbar * Fix receiver creator fixed role to not give global read * Include in-use metadata in k8s receiver List & Get GET: Always included. LIST: Included by adding a label selector with value `grafana.com/inUse` * Add receiver creator permission to receiver writer * Add receiver creator permission to navbar * Always allow listing receivers, don't return 403 * Remove receiver read precondition from receiver create Otherwise, Creator role will not be able to create their first receiver * Update routes permissions * Add further support for RBAC in contact points * Update routes permissions * Update contact points header logic * Back out test feature toggle refactor Not working atm, not sure why * Tidy up imports * Update mock permissions * Revert more test changes * Update i18n * Sync inuse metadata pr * Add back canAdmin permissions after main merge * Split out check for policies navtree item * Tidy up utils and imports and fix rules in use * Fix contact point tests and act warnings * Add missing ReceiverPermissionAdmin after merge conflict * Move contact points permissions * Only show contact points filter when permissions are correct * Move to constants * Fallback to empty array and remove labelSelectors (not needed) * Allow `toAbility` to take multiple actions * Show builtin alertmanager if contact points permission * Add empty state and hide templates if missing permissions * Translations * Tidy up mock data * Fix tests and templates permission * Update message for unused contact points * Don't return 403 when user lists receivers and has access to none * Fix receiver create not adding empty uid permissions * Move SetDefaultPermissions to ReceiverPermissionService * Have SetDefaultPermissions use uid from string Fixes circular dependency * Add FakeReceiverPermissionsService and fix test wiring * Implement resource permission handling in provisioning API and renames Create: Sets to default permissions Delete: Removes permissions Update: If receiver name is modified and the new name doesn't exist, it copies the permissions from the old receiver to the newly created one. If old receiver is now empty, it removes the old permissions as well. * Split contact point permissions checks for read/modify * Generalise getting annotation values from k8s entities * Proxy RouteDeleteAlertingConfig through MultiOrgAlertmanager * Cleanup permissions on config api reset and restore * Cleanup permissions on config api POST note this is still not available with feature flag enabled * Gate the permission manager behind FF until initial migration is added * Sync changes from config api PR * Switch to named export * Revert unnecessary changes * Revert Filter auth change and implement in k8s api only * Don't allow new scoped permissions to give access without FF Prevents complications around mixed support for the scoped permissions causing oddities in the UI. * Fix integration tests to account for list permission change * Move to `permissions` file * Add additional tests for contact points * Fix redirect for viewer on edit page * Combine alerting test utils and move to new file location * Allow new permissions to access provisioning export paths with FF * Always allow exporting if its grafana flavoured * Fix logic for showing auto generated policies * Fix delete logic for contact point only referenced by a rule * Suppress warning message when renaming a contact point * Clear team and role perm cache on receiver rename Prevents temporarily broken UI permissions after rename when a user's source of elevated permissions comes from a cached team or basic role permission. * Debug log failed cache clear on CopyPermissions --------- Co-authored-by: Matt Jacobson <matthew.jacobson@grafana.com>
2024-09-28 02:56:32 +08:00
contactPoints: contactPointsListResponse.data || [],
alertmanagerConfiguration: alertmanagerConfigResponse.data,
});
return {
...contactPointsListResponse,
contactPoints: enhanced,
};
}, [
alertNotifiers,
alertmanagerConfigResponse,
contactPointsListResponse,
contactPointsStatusResponse,
onCallResponse,
]);
};
/**
* Fetch single contact point via the alertmanager config
*/
const useGetAlertmanagerContactPoint = (
{ alertmanager, name }: BaseAlertmanagerArgs & { name: string },
queryOptions?: Parameters<typeof useGetAlertmanagerConfigurationQuery>[1]
) => {
return useGetAlertmanagerConfigurationQuery(alertmanager, {
...queryOptions,
selectFromResult: (result) => {
const matchedContactPoint = result.data?.alertmanager_config.receivers?.find(
(receiver) => receiver.name === name
);
return {
...result,
data: matchedContactPoint,
currentData: matchedContactPoint,
};
},
});
};
/**
* Fetch single contact point via the k8s API, or the alertmanager config
*/
const useGetGrafanaContactPoint = (
{ name }: { name: string },
queryOptions?: Parameters<typeof useReadNamespacedReceiverQuery>[1]
) => {
const namespace = getK8sNamespace();
const useK8sApi = shouldUseK8sApi(GRAFANA_RULES_SOURCE_NAME);
const k8sResponse = useReadNamespacedReceiverQuery(
{ namespace, name },
{
...queryOptions,
selectFromResult: (result) => {
const data = result.data ? parseK8sReceiver(result.data) : undefined;
return {
...result,
data,
currentData: data,
};
},
skip: queryOptions?.skip || !useK8sApi,
}
);
const grafanaResponse = useGetAlertmanagerContactPoint(
{ alertmanager: GRAFANA_RULES_SOURCE_NAME, name },
{ skip: queryOptions?.skip || useK8sApi }
);
return useK8sApi ? k8sResponse : grafanaResponse;
};
export const useGetContactPoint = ({ alertmanager, name }: { alertmanager: string; name: string }) => {
const isGrafana = alertmanager === GRAFANA_RULES_SOURCE_NAME;
const grafanaResponse = useGetGrafanaContactPoint({ name }, { skip: !isGrafana });
const alertmanagerResponse = useGetAlertmanagerContactPoint({ alertmanager, name }, { skip: isGrafana });
return isGrafana ? grafanaResponse : alertmanagerResponse;
};
export function useContactPointsWithStatus({
alertmanager,
fetchStatuses,
fetchPolicies,
Alerting: Consume k8s API for notification policies tree (#96147) * Add basic usage of K8s API for notification policies * Add permissions checks for navtree for routes * Add and update permissions for routing tree logic * Add capability to skip calling contact points hook * Conditionally show list of mute timings depending on permissions * Conditionally link to mute timings if user can see at least one * Add work in progress k8s handlers for routing tree * Update notification policy hooks * Wire up policies to permissions better (conditionally calling APIs) * Add additional checks for whether to show grafana AM * Add permission checks to access control * Remove accidental permissions after rebase * Update types and const for k8s routes * Improve statefulness and reset routing tree in tests * Update notif policy tests to check k8s and config API * Fix type assertion * Move non-grafana test out of .each * Make failure case safer * Override tag invalidation for notification policies API * Pass in error and add new error alert component * Add basic mock server conflict check * Add test to check user can save after a conflict * Add logic to allow reloading policies if changed by another user * Fix test * Update translations in Modals * Add ViewAlertGroups ability * Tweak provisioning logic and memoize AM config response * Update snapshots for useAbilities * Update result destructure * Use enums for provenance in routingtrees * Use consistent memoisation * Fix _metadata for vanilla AM * useAsync for error / update state * move k8s api error handling to separate file * use cause for error codes * Use `supported` bools from Alertmanager abilities and clarify default policy --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2024-11-26 21:46:39 +08:00
skip,
}: GrafanaFetchOptions & BaseAlertmanagerArgs & Skippable) {
const isGrafanaAlertmanager = alertmanager === GRAFANA_RULES_SOURCE_NAME;
const grafanaResponse = useGrafanaContactPoints({
Alerting: Consume k8s API for notification policies tree (#96147) * Add basic usage of K8s API for notification policies * Add permissions checks for navtree for routes * Add and update permissions for routing tree logic * Add capability to skip calling contact points hook * Conditionally show list of mute timings depending on permissions * Conditionally link to mute timings if user can see at least one * Add work in progress k8s handlers for routing tree * Update notification policy hooks * Wire up policies to permissions better (conditionally calling APIs) * Add additional checks for whether to show grafana AM * Add permission checks to access control * Remove accidental permissions after rebase * Update types and const for k8s routes * Improve statefulness and reset routing tree in tests * Update notif policy tests to check k8s and config API * Fix type assertion * Move non-grafana test out of .each * Make failure case safer * Override tag invalidation for notification policies API * Pass in error and add new error alert component * Add basic mock server conflict check * Add test to check user can save after a conflict * Add logic to allow reloading policies if changed by another user * Fix test * Update translations in Modals * Add ViewAlertGroups ability * Tweak provisioning logic and memoize AM config response * Update snapshots for useAbilities * Update result destructure * Use enums for provenance in routingtrees * Use consistent memoisation * Fix _metadata for vanilla AM * useAsync for error / update state * move k8s api error handling to separate file * use cause for error codes * Use `supported` bools from Alertmanager abilities and clarify default policy --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2024-11-26 21:46:39 +08:00
skip: skip || !isGrafanaAlertmanager,
fetchStatuses,
fetchPolicies,
});
const alertmanagerConfigResponse = useGetAlertmanagerConfigurationQuery(alertmanager, {
...defaultOptions,
selectFromResult: (result) => ({
...result,
contactPoints: result.data
? enhanceContactPointsWithMetadata({
notifiers: cloudNotifierTypes,
contactPoints: result.data.alertmanager_config.receivers ?? [],
alertmanagerConfiguration: result.data,
})
: [],
}),
Alerting: Consume k8s API for notification policies tree (#96147) * Add basic usage of K8s API for notification policies * Add permissions checks for navtree for routes * Add and update permissions for routing tree logic * Add capability to skip calling contact points hook * Conditionally show list of mute timings depending on permissions * Conditionally link to mute timings if user can see at least one * Add work in progress k8s handlers for routing tree * Update notification policy hooks * Wire up policies to permissions better (conditionally calling APIs) * Add additional checks for whether to show grafana AM * Add permission checks to access control * Remove accidental permissions after rebase * Update types and const for k8s routes * Improve statefulness and reset routing tree in tests * Update notif policy tests to check k8s and config API * Fix type assertion * Move non-grafana test out of .each * Make failure case safer * Override tag invalidation for notification policies API * Pass in error and add new error alert component * Add basic mock server conflict check * Add test to check user can save after a conflict * Add logic to allow reloading policies if changed by another user * Fix test * Update translations in Modals * Add ViewAlertGroups ability * Tweak provisioning logic and memoize AM config response * Update snapshots for useAbilities * Update result destructure * Use enums for provenance in routingtrees * Use consistent memoisation * Fix _metadata for vanilla AM * useAsync for error / update state * move k8s api error handling to separate file * use cause for error codes * Use `supported` bools from Alertmanager abilities and clarify default policy --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2024-11-26 21:46:39 +08:00
skip: skip || isGrafanaAlertmanager,
});
return isGrafanaAlertmanager ? grafanaResponse : alertmanagerConfigResponse;
}
type DeleteContactPointArgs = { name: string; resourceVersion?: string };
export function useDeleteContactPoint({ alertmanager }: BaseAlertmanagerArgs) {
const useK8sApi = shouldUseK8sApi(alertmanager);
const [produceNewAlertmanagerConfiguration] = useProduceNewAlertmanagerConfiguration();
const [deleteReceiver] = useDeleteNamespacedReceiverMutation();
const deleteFromK8sAPI = useAsync(async ({ name, resourceVersion }: DeleteContactPointArgs) => {
const namespace = getK8sNamespace();
await deleteReceiver({
name,
namespace,
ioK8SApimachineryPkgApisMetaV1DeleteOptions: { preconditions: { resourceVersion } },
}).unwrap();
});
const deleteFromAlertmanagerConfiguration = useAsync(async ({ name }: DeleteContactPointArgs) => {
const action = deleteReceiverAction(name);
return produceNewAlertmanagerConfiguration(action);
});
return useK8sApi ? deleteFromK8sAPI : deleteFromAlertmanagerConfiguration;
}
/**
* Turns a Grafana Managed receiver config into a format that can be sent to the k8s API
*
* When updating secure settings, we need to send a value of `true` for any secure setting that we want to keep the same.
*
* Any other setting that has a value in `secureSettings` will correspond to a new value for that setting -
* so we should not tell the API that we want to preserve it. Those values will instead be sent within `settings`
*/
const mapIntegrationSettingsForK8s = (integration: GrafanaManagedReceiverConfig): GrafanaManagedReceiverConfig => {
const { secureSettings, settings, ...restOfIntegration } = integration;
const secureFields = Object.entries(secureSettings || {}).reduce((acc, [key, value]) => {
// If a secure field has no (changed) value, then we tell the backend to persist it
if (value === undefined) {
return {
...acc,
[key]: true,
};
}
return acc;
}, {});
const mappedSecureSettings = Object.entries(secureSettings || {}).reduce((acc, [key, value]) => {
// If the value is an empty string/falsy value, then we need to omit it from the payload
// so the backend knows to remove it
if (!value) {
return acc;
}
// Otherwise, we send the value of the secure field
return set(acc, key, value);
}, {});
// Merge settings properly with lodash so we don't lose any information from nested keys/secure settings
const mergedSettings = merge({}, settings, mappedSecureSettings);
return {
...restOfIntegration,
secureFields,
settings: mergedSettings,
};
};
const grafanaContactPointToK8sReceiver = (
contactPoint: GrafanaManagedContactPoint,
id?: string,
resourceVersion?: string
): K8sReceiver => {
return {
metadata: {
...(id && { name: id }),
resourceVersion,
},
spec: {
title: contactPoint.name,
integrations: (contactPoint.grafana_managed_receiver_configs || []).map(mapIntegrationSettingsForK8s),
},
};
};
type ContactPointOperationArgs = {
contactPoint: Receiver;
};
type CreateContactPointArgs = ContactPointOperationArgs;
export const useCreateContactPoint = ({ alertmanager }: BaseAlertmanagerArgs) => {
const isGrafanaAlertmanager = alertmanager === GRAFANA_RULES_SOURCE_NAME;
const useK8sApi = shouldUseK8sApi(alertmanager);
const { createOnCallIntegrations } = useOnCallIntegration();
const [createGrafanaContactPoint] = useCreateNamespacedReceiverMutation();
const [produceNewAlertmanagerConfiguration] = useProduceNewAlertmanagerConfiguration();
const updateK8sAPI = useAsync(async ({ contactPoint }: CreateContactPointArgs) => {
const contactPointWithMaybeOnCall = isGrafanaAlertmanager
? await createOnCallIntegrations(contactPoint)
: contactPoint;
const namespace = getK8sNamespace();
const contactPointToUse = grafanaContactPointToK8sReceiver(contactPointWithMaybeOnCall);
return createGrafanaContactPoint({
namespace,
comGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver: contactPointToUse,
}).unwrap();
});
const updateAlertmanagerConfiguration = useAsync(async ({ contactPoint }: CreateContactPointArgs) => {
const contactPointWithMaybeOnCall = isGrafanaAlertmanager
? await createOnCallIntegrations(contactPoint)
: contactPoint;
const action = addReceiverAction(contactPointWithMaybeOnCall);
return produceNewAlertmanagerConfiguration(action);
});
return useK8sApi ? updateK8sAPI : updateAlertmanagerConfiguration;
};
type UpdateContactPointArgsK8s = ContactPointOperationArgs & {
/** ID of existing contact point to update - used when updating via k8s API */
id: string;
resourceVersion?: string;
};
type UpdateContactPointArgsConfig = ContactPointOperationArgs & {
/** Name of the existing contact point - used for checking uniqueness of name when not using k8s API*/
originalName: string;
};
type UpdateContactpointArgs = UpdateContactPointArgsK8s | UpdateContactPointArgsConfig;
export const useUpdateContactPoint = ({ alertmanager }: BaseAlertmanagerArgs) => {
const isGrafanaAlertmanager = alertmanager === GRAFANA_RULES_SOURCE_NAME;
const useK8sApi = shouldUseK8sApi(alertmanager);
const { createOnCallIntegrations } = useOnCallIntegration();
const [replaceGrafanaContactPoint] = useReplaceNamespacedReceiverMutation();
const [produceNewAlertmanagerConfiguration] = useProduceNewAlertmanagerConfiguration();
const updateContactPoint = useAsync(async (args: UpdateContactpointArgs) => {
if ('resourceVersion' in args && useK8sApi) {
const { contactPoint, id, resourceVersion } = args;
const receiverWithPotentialOnCall = isGrafanaAlertmanager
? await createOnCallIntegrations(contactPoint)
: contactPoint;
const namespace = getK8sNamespace();
const contactPointToUse = grafanaContactPointToK8sReceiver(receiverWithPotentialOnCall, id, resourceVersion);
return replaceGrafanaContactPoint({
name: id,
namespace,
comGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver: contactPointToUse,
}).unwrap();
} else if ('originalName' in args) {
const { contactPoint, originalName } = args;
const receiverWithPotentialOnCall = isGrafanaAlertmanager
? await createOnCallIntegrations(contactPoint)
: contactPoint;
const action = updateReceiverAction({ name: originalName, receiver: receiverWithPotentialOnCall });
return produceNewAlertmanagerConfiguration(action);
}
});
return updateContactPoint;
};
export const useValidateContactPoint = ({ alertmanager }: BaseAlertmanagerArgs) => {
const useK8sApi = shouldUseK8sApi(alertmanager);
const [getConfig] = useLazyGetAlertmanagerConfigurationQuery();
// If we're using the kubernetes API, then we let the API response handle the validation instead
// as we don't expect to be able to fetch the intervals via the AM config
if (useK8sApi) {
return () => undefined;
}
return async (value: string, existingValue?: string) => {
// If we've been given an existing value, and the name has not changed,
// we can skip validation
// (as we don't want to incorrectly flag the existing name as matching itself)
if (existingValue && value === existingValue) {
return;
}
return getConfig(alertmanager)
.unwrap()
.then((config) => {
const { alertmanager_config } = config;
const duplicated = Boolean(alertmanager_config.receivers?.find((contactPoint) => contactPoint.name === value));
return duplicated ? `Contact point already exists with name "${value}"` : undefined;
});
};
};