From ff774e210c65b4f5b6ef2fccf15de368e1faf82e Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Wed, 31 Jul 2024 16:04:40 +0100 Subject: [PATCH] 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 --- .../features/migrate-to-cloud/api/baseAPI.ts | 2 +- .../features/migrate-to-cloud/api/index.ts | 41 ++--- .../features/migrate-to-cloud/onprem/Page.tsx | 171 ++++++++++++++---- .../shared/AlertWithTraceID.tsx | 10 +- public/locales/en-US/grafana.json | 17 +- public/locales/pseudo-LOCALE/grafana.json | 17 +- 6 files changed, 186 insertions(+), 72 deletions(-) diff --git a/public/app/features/migrate-to-cloud/api/baseAPI.ts b/public/app/features/migrate-to-cloud/api/baseAPI.ts index 7e1b154581a..6a140906631 100644 --- a/public/app/features/migrate-to-cloud/api/baseAPI.ts +++ b/public/app/features/migrate-to-cloud/api/baseAPI.ts @@ -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, }) ); diff --git a/public/app/features/migrate-to-cloud/api/index.ts b/public/app/features/migrate-to-cloud/api/index.ts index 75f33c8e728..f4bfb599d69 100644 --- a/public/app/features/migrate-to-cloud/api/index.ts +++ b/public/app/features/migrate-to-cloud/api/index.ts @@ -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'], diff --git a/public/app/features/migrate-to-cloud/onprem/Page.tsx b/public/app/features/migrate-to-cloud/onprem/Page.tsx index fd233695c95..4da9ea67aa7 100644 --- a/public/app/features/migrate-to-cloud/onprem/Page.tsx +++ b/public/app/features/migrate-to-cloud/onprem/Page.tsx @@ -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 ( <> - - {/* TODO: show errors from all mutation's in a... modal? */} - - {createSnapshotResult.isError && ( - - - - See the Grafana server logs for more details - - - - )} - - {disconnectResult.isError && ( - - - See the Grafana server logs for more details - - - )} - + {session.data && ( { /> )} + {error && ( + + {error.body} + + )} + {(showBuildSnapshot || showBuildingSnapshot) && ( {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; +} diff --git a/public/app/features/migrate-to-cloud/shared/AlertWithTraceID.tsx b/public/app/features/migrate-to-cloud/shared/AlertWithTraceID.tsx index 97e7745aab4..d3daec019f4 100644 --- a/public/app/features/migrate-to-cloud/shared/AlertWithTraceID.tsx +++ b/public/app/features/migrate-to-cloud/shared/AlertWithTraceID.tsx @@ -15,9 +15,13 @@ export function AlertWithTraceID(props: AlertWithTraceIDProps) { {children} - {/* Deliberately don't want to translate 'Trace ID' */} - {/* eslint-disable-next-line @grafana/no-untranslated-strings */} - {traceID && Trace ID: {traceID}} + {traceID && ( + /* Deliberately don't want to translate 'Trace ID' */ + /* eslint-disable-next-line @grafana/no-untranslated-strings */ + + Trace ID: {traceID} + + )} ); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index d70711c3331..10df4dd789a 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -1111,6 +1111,19 @@ "modal-title": "Migration token created", "status": "Current status: <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", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 5107a01162f..c067310672a 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -1111,6 +1111,19 @@ "modal-title": "Mįģřäŧįőʼn ŧőĸęʼn čřęäŧęđ", "status": "Cūřřęʼnŧ şŧäŧūş: <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şĥőŧ",