E2C: Show snapshot error status (#91214)

* wip getError fn

* Create show errors from all API requests

* suppress error toasts from all e2c endpoints

* require error severity
This commit is contained in:
Josh Hunt
2024-07-31 16:04:40 +01:00
committed by GitHub
parent bffcb9de89
commit ff774e210c
6 changed files with 186 additions and 72 deletions
@@ -18,7 +18,7 @@ function createBackendSrvBaseQuery({ baseURL }: { baseURL: string }): BaseQueryF
getBackendSrv().fetch({
...requestOptions,
url: baseURL + requestOptions.url,
showErrorAlert: requestOptions.showErrorAlert,
showErrorAlert: false,
data: requestOptions.body,
})
);
@@ -8,50 +8,39 @@ export const cloudMigrationAPI = generatedAPI.enhanceEndpoints({
endpoints: {
// Cloud-side - create token
getCloudMigrationToken(endpoint) {
suppressErrorsOnQuery(endpoint);
endpoint.providesTags = ['cloud-migration-token'];
getCloudMigrationToken: {
providesTags: ['cloud-migration-token'],
},
createCloudMigrationToken(endpoint) {
suppressErrorsOnQuery(endpoint);
endpoint.invalidatesTags = ['cloud-migration-token'];
createCloudMigrationToken: {
invalidatesTags: ['cloud-migration-token'],
},
deleteCloudMigrationToken(endpoint) {
suppressErrorsOnQuery(endpoint);
endpoint.invalidatesTags = ['cloud-migration-token'];
deleteCloudMigrationToken: {
invalidatesTags: ['cloud-migration-token'],
},
// List Cloud Configs
// On-prem session management (entering token)
getSessionList: {
providesTags: ['cloud-migration-session'] /* should this be a -list? */,
},
// Create Cloud Config
createSession(endpoint) {
suppressErrorsOnQuery(endpoint);
endpoint.invalidatesTags = ['cloud-migration-session'];
},
// Get one Cloud Config
getSession: {
providesTags: ['cloud-migration-session'],
},
// Delete one Cloud Config
createSession: {
invalidatesTags: ['cloud-migration-session'],
},
deleteSession: {
invalidatesTags: ['cloud-migration-session', 'cloud-migration-snapshot'],
},
// Snapshot management
getSnapshot: {
providesTags: ['cloud-migration-snapshot'],
},
getShapshotList: {
providesTags: ['cloud-migration-snapshot'],
},
createSnapshot(endpoint) {
suppressErrorsOnQuery(endpoint);
endpoint.invalidatesTags = ['cloud-migration-snapshot'];
getSnapshot: {
providesTags: ['cloud-migration-snapshot'],
},
createSnapshot: {
invalidatesTags: ['cloud-migration-snapshot'],
},
uploadSnapshot: {
invalidatesTags: ['cloud-migration-snapshot'],
@@ -1,10 +1,11 @@
import { skipToken } from '@reduxjs/toolkit/query/react';
import { useCallback, useEffect, useState } from 'react';
import { Alert, Box, Stack, Text } from '@grafana/ui';
import { AlertVariant, Box, Stack, Text } from '@grafana/ui';
import { Trans, t } from 'app/core/internationalization';
import {
GetSnapshotResponseDto,
SnapshotDto,
useCancelSnapshotMutation,
useCreateSnapshotMutation,
@@ -81,10 +82,12 @@ function useGetLatestSnapshot(sessionUid?: string, page = 1) {
skipPollingIfUnfocused: true,
});
const isError = listResult.isError || snapshotResult.isError;
useEffect(() => {
const shouldPoll = SHOULD_POLL_STATUSES.includes(snapshotResult.data?.status);
const shouldPoll = !isError && SHOULD_POLL_STATUSES.includes(snapshotResult.data?.status);
setShouldPoll(shouldPoll);
}, [snapshotResult?.data?.status]);
}, [snapshotResult?.data?.status, isError]);
return {
...snapshotResult,
@@ -100,7 +103,7 @@ function useGetLatestSnapshot(sessionUid?: string, page = 1) {
// isSuccess and isUninitialised should always be from snapshotResult
// as only the 'final' values from those are important
isError: listResult.isError || snapshotResult.isError,
isError,
isLoading: listResult.isLoading || snapshotResult.isLoading,
isFetching: listResult.isFetching || snapshotResult.isFetching,
};
@@ -131,11 +134,22 @@ export const Page = () => {
snapshot.isLoading ||
disconnectResult.isLoading;
const showBuildSnapshot = !snapshot.isLoading && !snapshot.data;
const showBuildSnapshot = !snapshot.isError && !snapshot.isLoading && !snapshot.data;
const showBuildingSnapshot = SNAPSHOT_BUILDING_STATUSES.includes(status);
const showUploadSnapshot = status === 'PENDING_UPLOAD' || SNAPSHOT_UPLOADING_STATUSES.includes(status);
const showUploadSnapshot =
!snapshot.isError && (status === 'PENDING_UPLOAD' || SNAPSHOT_UPLOADING_STATUSES.includes(status));
const showRebuildSnapshot = SNAPSHOT_REBUILD_STATUSES.includes(status);
const error = getError({
snapshot: snapshot.data,
getSnapshotError: snapshot.error,
getSessionError: session.error,
createSnapshotError: createSnapshotResult.error,
uploadSnapshotError: uploadSnapshotResult.error,
cancelSnapshotError: cancelSnapshotResult.error,
disconnectSnapshotError: disconnectResult.error,
});
const handleDisconnect = useCallback(async () => {
if (sessionUid) {
performDisconnect({ uid: sessionUid });
@@ -173,34 +187,7 @@ export const Page = () => {
return (
<>
<Stack direction="column" gap={4}>
{/* TODO: show errors from all mutation's in a... modal? */}
{createSnapshotResult.isError && (
<AlertWithTraceID
error={createSnapshotResult.error}
severity="error"
title={t('migrate-to-cloud.summary.run-migration-error-title', 'Error creating snapshot')}
>
<Text element="p">
<Trans i18nKey="migrate-to-cloud.summary.run-migration-error-description">
See the Grafana server logs for more details
</Trans>
</Text>
</AlertWithTraceID>
)}
{disconnectResult.isError && (
<Alert
severity="error"
title={t('migrate-to-cloud.summary.disconnect-error-title', 'There was an error disconnecting')}
>
<Trans i18nKey="migrate-to-cloud.summary.disconnect-error-description">
See the Grafana server logs for more details
</Trans>
</Alert>
)}
<Stack direction="column" gap={2}>
{session.data && (
<MigrationSummary
session={session.data}
@@ -218,6 +205,12 @@ export const Page = () => {
/>
)}
{error && (
<AlertWithTraceID severity={error.severity} title={error.title} error={error.error}>
<Text element="p">{error.body}</Text>
</AlertWithTraceID>
)}
{(showBuildSnapshot || showBuildingSnapshot) && (
<Box display="flex" justifyContent="center" paddingY={10}>
{showBuildSnapshot && (
@@ -258,3 +251,113 @@ export const Page = () => {
</>
);
};
interface GetErrorProps {
snapshot: GetSnapshotResponseDto | undefined;
getSessionError: unknown; // From getLatestSessionQuery
getSnapshotError: unknown; // From getLatestSnapshotQuery
createSnapshotError: unknown; // From createSnapshotMutation
uploadSnapshotError: unknown; // From uploadSnapshotMutation
cancelSnapshotError: unknown; // From cancelSnapshotMutation
disconnectSnapshotError: unknown; // From disconnectMutation
}
interface ErrorDescription {
title: string;
body: string;
severity: AlertVariant;
error?: unknown;
}
function getError(props: GetErrorProps): ErrorDescription | undefined {
const {
snapshot,
getSnapshotError,
getSessionError,
createSnapshotError,
uploadSnapshotError,
cancelSnapshotError,
disconnectSnapshotError,
} = props;
const seeLogs = t('migrate-to-cloud.onprem.error-see-server-logs', 'See the Grafana server logs for more details');
if (getSessionError) {
return {
severity: 'error',
title: t('migrate-to-cloud.onprem.get-session-error-title', 'Error loading migration configuration'),
body: seeLogs,
error: getSessionError,
};
}
if (getSnapshotError) {
return {
severity: 'error',
title: t('migrate-to-cloud.onprem.get-snapshot-error-title', 'Error loading snapshot'),
body: seeLogs,
error: getSnapshotError,
};
}
if (disconnectSnapshotError) {
return {
severity: 'warning',
title: t('migrate-to-cloud.onprem.disconnect-error-title', 'Error disconnecting'),
body: seeLogs,
error: disconnectSnapshotError,
};
}
if (createSnapshotError) {
return {
severity: 'warning',
title: t('migrate-to-cloud.onprem.create-snapshot-error-title', 'Error creating snapshot'),
body: seeLogs,
error: createSnapshotError,
};
}
if (uploadSnapshotError) {
return {
severity: 'warning',
title: t('migrate-to-cloud.onprem.upload-snapshot-error-title', 'Error uploading snapshot'),
body: seeLogs,
error: uploadSnapshotError,
};
}
if (cancelSnapshotError) {
return {
severity: 'warning',
title: t('migrate-to-cloud.onprem.cancel-snapshot-error-title', 'Error cancelling creating snapshot'),
body: seeLogs,
error: cancelSnapshotError,
};
}
if (snapshot?.status === 'ERROR') {
return {
severity: 'warning',
title: t('migrate-to-cloud.onprem.snapshot-error-status-title', 'Error migrating resources'),
body: t(
'migrate-to-cloud.onprem.snapshot-error-status-body',
'There was an error creating the snapshot or starting the migration process. See the Grafana server logs for more details'
),
};
}
const errorCount = snapshot?.stats?.statuses?.['ERROR'] ?? 0;
if (snapshot?.status === 'FINISHED' && errorCount > 0) {
return {
severity: 'warning',
title: t('migrate-to-cloud.onprem.some-resources-errored-title', 'Resource migration complete'),
body: t(
'migrate-to-cloud.onprem.some-resources-errored-body',
'The migration has completed, but some items could not be migrated to the cloud stack. Check the failed resources for more details'
),
};
}
return undefined;
}
@@ -15,9 +15,13 @@ export function AlertWithTraceID(props: AlertWithTraceIDProps) {
<Stack direction="column" gap={1}>
{children}
{/* Deliberately don't want to translate 'Trace ID' */}
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
{traceID && <Text element="p">Trace ID: {traceID}</Text>}
{traceID && (
/* Deliberately don't want to translate 'Trace ID' */
/* eslint-disable-next-line @grafana/no-untranslated-strings */
<Text element="p" color="secondary" variant="bodySmall">
Trace ID: {traceID}
</Text>
)}
</Stack>
</Alert>
);
+13 -4
View File
@@ -1111,6 +1111,19 @@
"modal-title": "Migration token created",
"status": "Current status: <2></2>"
},
"onprem": {
"cancel-snapshot-error-title": "Error cancelling creating snapshot",
"create-snapshot-error-title": "Error creating snapshot",
"disconnect-error-title": "Error disconnecting",
"error-see-server-logs": "See the Grafana server logs for more details",
"get-session-error-title": "Error loading migration configuration",
"get-snapshot-error-title": "Error loading snapshot",
"snapshot-error-status-body": "There was an error creating the snapshot or starting the migration process. See the Grafana server logs for more details",
"snapshot-error-status-title": "Error migrating resources",
"some-resources-errored-body": "The migration has completed, but some items could not be migrated to the cloud stack. Check the failed resources for more details",
"some-resources-errored-title": "Resource migration complete",
"upload-snapshot-error-title": "Error uploading snapshot"
},
"pdc": {
"body": "Exposing your data sources to the internet can raise security concerns. Private data source connect (PDC) allows Grafana Cloud to access your existing data sources over a secure network tunnel.",
"link-title": "Learn about PDC",
@@ -1154,13 +1167,9 @@
"summary": {
"cancel-snapshot": "Cancel snapshot",
"disconnect": "Disconnect",
"disconnect-error-description": "See the Grafana server logs for more details",
"disconnect-error-title": "There was an error disconnecting",
"errored-resource-count": "Errors",
"page-loading": "Loading...",
"rebuild-snapshot": "Rebuild snapshot",
"run-migration-error-description": "See the Grafana server logs for more details",
"run-migration-error-title": "Error creating snapshot",
"snapshot-date": "Snapshot timestamp",
"snapshot-not-created": "Not yet created",
"start-migration": "Build snapshot",
+13 -4
View File
@@ -1111,6 +1111,19 @@
"modal-title": "Mįģřäŧįőʼn ŧőĸęʼn čřęäŧęđ",
"status": "Cūřřęʼnŧ şŧäŧūş: <2></2>"
},
"onprem": {
"cancel-snapshot-error-title": "Ēřřőř čäʼnčęľľįʼnģ čřęäŧįʼnģ şʼnäpşĥőŧ",
"create-snapshot-error-title": "Ēřřőř čřęäŧįʼnģ şʼnäpşĥőŧ",
"disconnect-error-title": "Ēřřőř đįşčőʼnʼnęčŧįʼnģ",
"error-see-server-logs": "Ŝęę ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş",
"get-session-error-title": "Ēřřőř ľőäđįʼnģ mįģřäŧįőʼn čőʼnƒįģūřäŧįőʼn",
"get-snapshot-error-title": "Ēřřőř ľőäđįʼnģ şʼnäpşĥőŧ",
"snapshot-error-status-body": "Ŧĥęřę ŵäş äʼn ęřřőř čřęäŧįʼnģ ŧĥę şʼnäpşĥőŧ őř şŧäřŧįʼnģ ŧĥę mįģřäŧįőʼn přőčęşş. Ŝęę ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş",
"snapshot-error-status-title": "Ēřřőř mįģřäŧįʼnģ řęşőūřčęş",
"some-resources-errored-body": "Ŧĥę mįģřäŧįőʼn ĥäş čőmpľęŧęđ, þūŧ şőmę įŧęmş čőūľđ ʼnőŧ þę mįģřäŧęđ ŧő ŧĥę čľőūđ şŧäčĸ. Cĥęčĸ ŧĥę ƒäįľęđ řęşőūřčęş ƒőř mőřę đęŧäįľş",
"some-resources-errored-title": "Ŗęşőūřčę mįģřäŧįőʼn čőmpľęŧę",
"upload-snapshot-error-title": "Ēřřőř ūpľőäđįʼnģ şʼnäpşĥőŧ"
},
"pdc": {
"body": "Ēχpőşįʼnģ yőūř đäŧä şőūřčęş ŧő ŧĥę įʼnŧęřʼnęŧ čäʼn řäįşę şęčūřįŧy čőʼnčęřʼnş. Přįväŧę đäŧä şőūřčę čőʼnʼnęčŧ (PĐC) äľľőŵş Ğřäƒäʼnä Cľőūđ ŧő äččęşş yőūř ęχįşŧįʼnģ đäŧä şőūřčęş ővęř ä şęčūřę ʼnęŧŵőřĸ ŧūʼnʼnęľ.",
"link-title": "Ŀęäřʼn äþőūŧ PĐC",
@@ -1154,13 +1167,9 @@
"summary": {
"cancel-snapshot": "Cäʼnčęľ şʼnäpşĥőŧ",
"disconnect": "Đįşčőʼnʼnęčŧ",
"disconnect-error-description": "Ŝęę ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş",
"disconnect-error-title": "Ŧĥęřę ŵäş äʼn ęřřőř đįşčőʼnʼnęčŧįʼnģ",
"errored-resource-count": "Ēřřőřş",
"page-loading": "Ŀőäđįʼnģ...",
"rebuild-snapshot": "Ŗęþūįľđ şʼnäpşĥőŧ",
"run-migration-error-description": "Ŝęę ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş",
"run-migration-error-title": "Ēřřőř čřęäŧįʼnģ şʼnäpşĥőŧ",
"snapshot-date": "Ŝʼnäpşĥőŧ ŧįmęşŧämp",
"snapshot-not-created": "Ńőŧ yęŧ čřęäŧęđ",
"start-migration": "ßūįľđ şʼnäpşĥőŧ",