From 3c77866cc4db5904ec329f8f7600d0d19e590036 Mon Sep 17 00:00:00 2001 From: Noble Mittal Date: Wed, 28 Aug 2024 19:42:38 +0530 Subject: [PATCH 1/3] VTAdmin(web): Add workflow start/stop actions Signed-off-by: Noble Mittal --- web/vtadmin/src/api/http.ts | 22 ++++++ .../src/components/routes/Workflows.tsx | 20 +++-- .../routes/workflows/WorkflowAction.tsx | 77 +++++++++++++++++++ .../routes/workflows/WorkflowActions.tsx | 68 ++++++++++++++++ web/vtadmin/src/hooks/api.ts | 26 +++++++ 5 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 web/vtadmin/src/components/routes/workflows/WorkflowAction.tsx create mode 100644 web/vtadmin/src/components/routes/workflows/WorkflowActions.tsx diff --git a/web/vtadmin/src/api/http.ts b/web/vtadmin/src/api/http.ts index 64d75b23a42..030455c652d 100644 --- a/web/vtadmin/src/api/http.ts +++ b/web/vtadmin/src/api/http.ts @@ -448,6 +448,28 @@ export const fetchWorkflowStatus = async (params: { clusterID: string; keyspace: return vtctldata.WorkflowStatusResponse.create(result); }; +export interface WorkflowActionParams { + clusterID: string; + keyspace: string; + name: string; +} + +export const startWorkflow = async ({ clusterID, keyspace, name }: WorkflowActionParams) => { + const { result } = await vtfetch(`/api/workflow/${clusterID}/${keyspace}/${name}/start`); + const err = vtctldata.WorkflowUpdateResponse.verify(result); + if (err) throw Error(err); + + return vtctldata.WorkflowUpdateResponse.create(result); +}; + +export const stopWorkflow = async ({ clusterID, keyspace, name }: WorkflowActionParams) => { + const { result } = await vtfetch(`/api/workflow/${clusterID}/${keyspace}/${name}/stop`); + const err = vtctldata.WorkflowUpdateResponse.verify(result); + if (err) throw Error(err); + + return vtctldata.WorkflowUpdateResponse.create(result); +}; + export const fetchVTExplain = async ({ cluster, keyspace, sql }: R) => { // As an easy enhancement for later, we can also validate the request parameters on the front-end // instead of defaulting to '', to save a round trip. diff --git a/web/vtadmin/src/components/routes/Workflows.tsx b/web/vtadmin/src/components/routes/Workflows.tsx index 32ddfcfb825..477d8bcb582 100644 --- a/web/vtadmin/src/components/routes/Workflows.tsx +++ b/web/vtadmin/src/components/routes/Workflows.tsx @@ -35,9 +35,13 @@ import { Tooltip } from '../tooltip/Tooltip'; import { KeyspaceLink } from '../links/KeyspaceLink'; import { QueryLoadingPlaceholder } from '../placeholders/QueryLoadingPlaceholder'; import { UseQueryResult } from 'react-query'; +import { ReadOnlyGate } from '../ReadOnlyGate'; +import WorkflowActions from './workflows/WorkflowActions'; export const ThrottleThresholdSeconds = 60; +const COLUMNS = ['Workflow', 'Source', 'Target', 'Streams', 'Last Updated', 'Actions']; + export const Workflows = () => { useDocumentTitle('Workflows'); const workflowsQuery = useWorkflows(); @@ -180,6 +184,16 @@ export const Workflows = () => {
{formatDateTime(row.timeUpdated)}
{formatRelativeTime(row.timeUpdated)}
+ + + + + + ); }); @@ -198,11 +212,7 @@ export const Workflows = () => { value={filter || ''} /> - + diff --git a/web/vtadmin/src/components/routes/workflows/WorkflowAction.tsx b/web/vtadmin/src/components/routes/workflows/WorkflowAction.tsx new file mode 100644 index 00000000000..707ae6a3f7b --- /dev/null +++ b/web/vtadmin/src/components/routes/workflows/WorkflowAction.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { Icon, Icons } from '../../Icon'; +import Dialog from '../../dialog/Dialog'; +import { UseMutationResult } from 'react-query'; + +interface WorkflowActionProps { + isOpen: boolean; + mutation: UseMutationResult; + title: string; + confirmText: string; + successText: string; + errorText: string; + loadingText: string; + description?: string; + body?: JSX.Element; + successBody?: JSX.Element; + closeDialog: () => void; +} + +const WorkflowAction: React.FC = ({ + isOpen, + closeDialog, + mutation, + title, + confirmText, + description, + successText, + successBody, + loadingText, + errorText, + body, +}) => { + const onCloseDialog = () => { + setTimeout(mutation.reset, 500); + closeDialog(); + }; + + const hasRun = mutation.data || mutation.error; + return ( + +
+ {!hasRun && body} + {mutation.data && !mutation.error && ( +
+ + + +
{successText}
+ {successBody} +
+ )} + {mutation.error && ( +
+ + + +
{errorText}
+
+ )} +
+
+ ); +}; + +export default WorkflowAction; diff --git a/web/vtadmin/src/components/routes/workflows/WorkflowActions.tsx b/web/vtadmin/src/components/routes/workflows/WorkflowActions.tsx new file mode 100644 index 00000000000..7edb632327b --- /dev/null +++ b/web/vtadmin/src/components/routes/workflows/WorkflowActions.tsx @@ -0,0 +1,68 @@ +import React, { useState } from 'react'; +import Dropdown from '../../dropdown/Dropdown'; +import MenuItem from '../../dropdown/MenuItem'; +import { Icons } from '../../Icon'; +import WorkflowAction from './WorkflowAction'; +import { useStartWorkflow, useStopWorkflow } from '../../../hooks/api'; + +interface WorkflowActionsProps { + keyspace: string; + clusterID: string; + name: string; +} + +const WorkflowActions: React.FC = ({ keyspace, clusterID, name }) => { + const [currentDialog, setCurrentDialog] = useState(''); + const closeDialog = () => setCurrentDialog(''); + + const startWorkflowMutation = useStartWorkflow({ keyspace, clusterID, name }); + + const stopWorkflowMutation = useStopWorkflow({ keyspace, clusterID, name }); + + return ( +
+ + setCurrentDialog('Start Workflow')}>Start Workflow + setCurrentDialog('Stop Workflow')}>Stop Workflow + + + {startWorkflowMutation.data && startWorkflowMutation.data.summary && ( +
{startWorkflowMutation.data.summary}
+ )} +
+ } + /> + + {stopWorkflowMutation.data && stopWorkflowMutation.data.summary && ( +
{stopWorkflowMutation.data.summary}
+ )} + + } + /> + + ); +}; + +export default WorkflowActions; diff --git a/web/vtadmin/src/hooks/api.ts b/web/vtadmin/src/hooks/api.ts index 536293702c1..f3772ed4ab0 100644 --- a/web/vtadmin/src/hooks/api.ts +++ b/web/vtadmin/src/hooks/api.ts @@ -79,6 +79,8 @@ import { GetFullStatusParams, validateVersionShard, ValidateVersionShardParams, + startWorkflow, + stopWorkflow, } from '../api/http'; import { vtadmin as pb, vtctldata } from '../proto/vtadmin'; import { formatAlias } from '../util/tablets'; @@ -460,6 +462,30 @@ export const useWorkflowStatus = ( return useQuery(['workflow_status', params], () => fetchWorkflowStatus(params)); }; +/** + * useStartWorkflow is a mutate hook that starts a workflow. + */ +export const useStartWorkflow = ( + params: Parameters[0], + options?: UseMutationOptions>, Error> +) => { + return useMutation>, Error>(() => { + return startWorkflow(params); + }, options); +}; + +/** + * useStopWorkflow is a mutate hook that stops a workflow. + */ +export const useStopWorkflow = ( + params: Parameters[0], + options?: UseMutationOptions>, Error> +) => { + return useMutation>, Error>(() => { + return stopWorkflow(params); + }, options); +}; + /** * useReloadSchema is a mutate hook that reloads schemas in one or more * keyspaces, shards, or tablets in the cluster, depending on the request parameters. From 536f0c4f8351e5b8539364b036fb40a2d105ef9a Mon Sep 17 00:00:00 2001 From: Noble Mittal Date: Thu, 29 Aug 2024 13:03:22 +0530 Subject: [PATCH 2/3] VTAdmin(web): Refetch workflows after performing workflow actions Signed-off-by: Noble Mittal --- .../src/components/routes/Workflows.tsx | 9 ++++++- .../routes/workflows/WorkflowAction.tsx | 14 ++++++++++- .../routes/workflows/WorkflowActions.tsx | 25 +++++++++++++------ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/web/vtadmin/src/components/routes/Workflows.tsx b/web/vtadmin/src/components/routes/Workflows.tsx index 477d8bcb582..558d82ab8e8 100644 --- a/web/vtadmin/src/components/routes/Workflows.tsx +++ b/web/vtadmin/src/components/routes/Workflows.tsx @@ -37,10 +37,12 @@ import { QueryLoadingPlaceholder } from '../placeholders/QueryLoadingPlaceholder import { UseQueryResult } from 'react-query'; import { ReadOnlyGate } from '../ReadOnlyGate'; import WorkflowActions from './workflows/WorkflowActions'; +import { isReadOnlyMode } from '../../util/env'; export const ThrottleThresholdSeconds = 60; const COLUMNS = ['Workflow', 'Source', 'Target', 'Streams', 'Last Updated', 'Actions']; +const READ_ONLY_COLUMNS = ['Workflow', 'Source', 'Target', 'Streams', 'Last Updated']; export const Workflows = () => { useDocumentTitle('Workflows'); @@ -188,6 +190,7 @@ export const Workflows = () => { { value={filter || ''} /> - + diff --git a/web/vtadmin/src/components/routes/workflows/WorkflowAction.tsx b/web/vtadmin/src/components/routes/workflows/WorkflowAction.tsx index 707ae6a3f7b..8ff08801dff 100644 --- a/web/vtadmin/src/components/routes/workflows/WorkflowAction.tsx +++ b/web/vtadmin/src/components/routes/workflows/WorkflowAction.tsx @@ -14,6 +14,7 @@ interface WorkflowActionProps { description?: string; body?: JSX.Element; successBody?: JSX.Element; + refetchWorkflows: Function; closeDialog: () => void; } @@ -28,6 +29,7 @@ const WorkflowAction: React.FC = ({ successBody, loadingText, errorText, + refetchWorkflows, body, }) => { const onCloseDialog = () => { @@ -36,12 +38,22 @@ const WorkflowAction: React.FC = ({ }; const hasRun = mutation.data || mutation.error; + const onConfirm = () => { + mutation.mutate( + {}, + { + onSuccess: () => { + refetchWorkflows(); + }, + } + ); + }; return ( = ({ keyspace, clusterID, name }) => { +const WorkflowActions: React.FC = ({ refetchWorkflows, keyspace, clusterID, name }) => { const [currentDialog, setCurrentDialog] = useState(''); const closeDialog = () => setCurrentDialog(''); @@ -27,14 +28,14 @@ const WorkflowActions: React.FC = ({ keyspace, clusterID, {startWorkflowMutation.data && startWorkflowMutation.data.summary && ( @@ -42,17 +43,22 @@ const WorkflowActions: React.FC = ({ keyspace, clusterID, )} } + body={ +
+ Starts the {name} workflow. +
+ } /> {stopWorkflowMutation.data && stopWorkflowMutation.data.summary && ( @@ -60,6 +66,11 @@ const WorkflowActions: React.FC = ({ keyspace, clusterID, )} } + body={ +
+ Stops the {name} workflow. +
+ } /> ); From 41cf462f2dcce215da6b55d7e6ae874b5d09cf0c Mon Sep 17 00:00:00 2001 From: Noble Mittal Date: Sat, 7 Sep 2024 09:25:47 +0530 Subject: [PATCH 3/3] Fix the workflow action description Signed-off-by: Noble Mittal --- .../src/components/routes/workflows/WorkflowActions.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/vtadmin/src/components/routes/workflows/WorkflowActions.tsx b/web/vtadmin/src/components/routes/workflows/WorkflowActions.tsx index dc702c11661..f437fb9e785 100644 --- a/web/vtadmin/src/components/routes/workflows/WorkflowActions.tsx +++ b/web/vtadmin/src/components/routes/workflows/WorkflowActions.tsx @@ -45,7 +45,7 @@ const WorkflowActions: React.FC = ({ refetchWorkflows, key } body={
- Starts the {name} workflow. + Start the {name} workflow.
} /> @@ -68,7 +68,7 @@ const WorkflowActions: React.FC = ({ refetchWorkflows, key } body={
- Stops the {name} workflow. + Stop the {name} workflow.
} />