Skip to content

Commit

Permalink
Merge pull request #1007 from yaacov/refactor-simple-hook-tab
Browse files Browse the repository at this point in the history
🧼 Refactor simple plan hook form
  • Loading branch information
yaacov authored Mar 21, 2024
2 parents 46997d1 + fbf5da6 commit 2527a71
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@
"Migrate": "Migrate",
"Migrate without validating a CA certificate": "Migrate without validating a CA certificate",
"Migration": "Migration",
"Migration hook": "Migration hook",
"Migration networks maps are used to map network interfaces between source and target workloads.": "Migration networks maps are used to map network interfaces between source and target workloads.",
"Migration plan state information and progress": "Migration plan state information and progress",
"Migration plans are used to plan migration or virtualization workloads from source providers to target providers.": "Migration plans are used to plan migration or virtualization workloads from source providers to target providers.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,12 @@
margin-bottom: var(--pf-global--spacer--xs);
margin-top: var(--pf-global--spacer--md);
}

.forklift-page-plan-details-vm-status__section-actions {
padding-bottom: 0;
}

.forklift-page-plan-details-vm-status__actions {
margin-bottom: var(--pf-global--spacer--md);
margin-top: var(--pf-global--spacer--xs);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,13 @@ import React, { useEffect, useReducer } from 'react';
import { Base64 } from 'js-base64';
import SectionHeading from 'src/components/headers/SectionHeading';
import { deepCopy } from 'src/modules/Plans/utils';
import { AlertMessageForModals } from 'src/modules/Providers/modals';
import { useForkliftTranslation } from 'src/utils/i18n';

import {
HookModelGroupVersionKind,
PlanModelGroupVersionKind,
V1beta1Hook,
V1beta1Plan,
V1beta1PlanSpecVmsHooks,
} from '@kubev2v/types';
import { CodeEditor, useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';
import { V1beta1Hook, V1beta1Plan } from '@kubev2v/types';
import { CodeEditor } from '@openshift-console/dynamic-plugin-sdk';
import {
Button,
Divider,
Flex,
FlexItem,
Form,
Expand All @@ -28,8 +22,9 @@ import {

import { Suspend } from '../../components';

import { usePlanHooks } from './hooks';
import { formReducer, FormState } from './state';
import { createHook, deleteHook, updateHook } from './utils';
import { onUpdatePlanHooks } from './utils';

const preHookTemplate = (plan: V1beta1Plan) => ({
spec: { image: 'quay.io/konveyor/hook-runner', playbook: '' },
Expand Down Expand Up @@ -62,25 +57,10 @@ const initialState = (
export const SimpleHooks: React.FC<{ name: string; namespace: string }> = ({ name, namespace }) => {
const { t } = useForkliftTranslation();

const [plan, loaded, loadError] = useK8sWatchResource<V1beta1Plan>({
groupVersionKind: PlanModelGroupVersionKind,
namespaced: true,
const [plan, preHookResource, postHookResource, loaded, loadError] = usePlanHooks(
name,
namespace,
});
const hooks: V1beta1PlanSpecVmsHooks[] = plan?.spec?.vms?.[0]?.hooks || [];
const postHook = hooks.find((h) => h.step === 'PostHook');
const preHook = hooks.find((h) => h.step === 'PreHook');

const [hookRecourses] = useK8sWatchResource<V1beta1Hook[]>({
groupVersionKind: HookModelGroupVersionKind,
namespaced: true,
isList: true,
namespace: plan?.metadata?.namespace,
});

const postHookResource = hookRecourses.find((h) => h.metadata.name === postHook?.hook?.name);
const preHookResource = hookRecourses.find((h) => h.metadata.name === preHook?.hook?.name);
);

const [state, dispatch] = useReducer(
formReducer,
Expand All @@ -97,86 +77,52 @@ export const SimpleHooks: React.FC<{ name: string; namespace: string }> = ({ nam

// Handle user clicking "save"
async function onUpdate() {
dispatch({ type: 'SET_LOADING', payload: true });

let newPlan = deepCopy(plan);

try {
if (state.preHookSet) {
if (preHookResource) {
// Patch new hook
await updateHook(state.preHook);
} else {
// Create hook
newPlan = await createHook(newPlan, state.preHook, 'PreHook');
}
} else {
if (preHookResource) {
// Delete hook
newPlan = await deleteHook(newPlan, preHookResource, 'PreHook');
}
}

if (state.postHookSet) {
if (postHookResource) {
// Patch new hook
await updateHook(state.postHook);
} else {
// Create hook
await createHook(newPlan, state.postHook, 'PostHook');
}
} else {
if (postHookResource) {
// Delete hook
await deleteHook(newPlan, postHookResource, 'PostHook');
}
}

dispatch({ type: 'SET_LOADING', payload: false });
} catch (err) {
dispatch({
type: 'SET_ALERT_MESSAGE',
payload: (
<AlertMessageForModals title={t('Error')} message={err.message || err.toString()} />
),
});

dispatch({ type: 'SET_LOADING', payload: false });
}
onUpdatePlanHooks({ plan, preHookResource, postHookResource, dispatch, state });
}

const HooksTabAction = (
<Flex className="forklift-page-plan-details-vm-status__actions">
<FlexItem>
<Button
variant="primary"
onClick={onUpdate}
isDisabled={!state.hasChanges}
isLoading={state.isLoading}
>
{t('Update hooks')}
</Button>
</FlexItem>

<FlexItem>
<Button
variant="secondary"
isDisabled={!state.hasChanges}
onClick={() =>
dispatch({
type: 'INIT',
payload: initialState(plan, preHookResource, postHookResource),
})
}
>
{t('Cancel')}
</Button>
</FlexItem>
</Flex>
);

return (
<Suspend obj={plan} loaded={loaded} loadError={loadError}>
<PageSection variant="light">
<Flex>
<FlexItem>
<Button
variant="primary"
onClick={onUpdate}
isDisabled={!state.hasChanges}
isLoading={state.isLoading}
>
{t('Update hooks')}
</Button>
</FlexItem>

<FlexItem>
<Button
variant="secondary"
isDisabled={!state.hasChanges}
onClick={() =>
dispatch({
type: 'INIT',
payload: initialState(plan, preHookResource, postHookResource),
})
}
>
{t('Cancel')}
</Button>
</FlexItem>
</Flex>
{state.alertMessage}
{state.alertMessage && <PageSection variant="light">{state.alertMessage}</PageSection>}

<PageSection
variant="light"
className="forklift-page-plan-details-vm-status__section-actions"
>
<SectionHeading text={t('Migration hook')} />
{HooksTabAction}
<Divider />
</PageSection>

<PageSection variant="light">
<SectionHeading text={t('Pre migration hook')} />
<Form>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @index(['./*', /style/g], f => `export * from '${f.path}';`)
export * from './usePlanHooks';
// @endindex
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
HookModelGroupVersionKind,
PlanModelGroupVersionKind,
V1beta1Hook,
V1beta1Plan,
V1beta1PlanSpecVmsHooks,
} from '@kubev2v/types';
import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';

export const usePlanHooks = (name: string, namespace: string) => {
const [plan, loaded, loadError] = useK8sWatchResource<V1beta1Plan>({
groupVersionKind: PlanModelGroupVersionKind,
namespaced: true,
name,
namespace,
});
const hooks: V1beta1PlanSpecVmsHooks[] = plan?.spec?.vms?.[0]?.hooks || [];
const postHook = hooks.find((h) => h.step === 'PostHook');
const preHook = hooks.find((h) => h.step === 'PreHook');

const [hookRecourses] = useK8sWatchResource<V1beta1Hook[]>({
groupVersionKind: HookModelGroupVersionKind,
namespaced: true,
isList: true,
namespace: plan?.metadata?.namespace,
});

const postHookResource = hookRecourses.find((h) => h.metadata.name === postHook?.hook?.name);
const preHookResource = hookRecourses.find((h) => h.metadata.name === preHook?.hook?.name);

return [plan, preHookResource, postHookResource, loaded, loadError];
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @index(['./*', /style/g], f => `export * from '${f.path}';`)
export * from './createHook';
export * from './deleteHook';
export * from './onUpdatePlanHooks';
export * from './updateHook';
// @endindex
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import { deepCopy } from 'src/modules/Plans/utils';
import { AlertMessageForModals } from 'src/modules/Providers/modals';

import { V1beta1Hook, V1beta1Plan } from '@kubev2v/types';

import { FormAction, FormState } from '../state';

import { createHook } from './createHook';
import { deleteHook } from './deleteHook';
import { updateHook } from './updateHook';

type onUpdatePlanHooksProps = {
plan: V1beta1Plan;
preHookResource: V1beta1Hook;
postHookResource: V1beta1Hook;
dispatch: (value: FormAction) => void;
state: FormState;
};

// Handle user clicking "save"
export async function onUpdatePlanHooks(props: onUpdatePlanHooksProps) {
const { plan, preHookResource, postHookResource, dispatch, state } = props;

dispatch({ type: 'SET_LOADING', payload: true });

let newPlan = deepCopy(plan);

try {
if (state.preHookSet) {
if (preHookResource) {
// Patch new hook
await updateHook(state.preHook);
} else {
// Create hook
newPlan = await createHook(newPlan, state.preHook, 'PreHook');
}
} else {
if (preHookResource) {
// Delete hook
newPlan = await deleteHook(newPlan, preHookResource, 'PreHook');
}
}

if (state.postHookSet) {
if (postHookResource) {
// Patch new hook
await updateHook(state.postHook);
} else {
// Create hook
await createHook(newPlan, state.postHook, 'PostHook');
}
} else {
if (postHookResource) {
// Delete hook
await deleteHook(newPlan, postHookResource, 'PostHook');
}
}

dispatch({ type: 'SET_LOADING', payload: false });
} catch (err) {
dispatch({
type: 'SET_ALERT_MESSAGE',
payload: <AlertMessageForModals title={'Error'} message={err.message || err.toString()} />,
});

dispatch({ type: 'SET_LOADING', payload: false });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export const MigrationVirtualMachinesRowExtended: React.FC<RowProps<VMData>> = (
const conditions = props.resourceData.statusVM?.conditions;
const pods = props.resourceData.pods;
const jobs = props.resourceData.jobs;
const success = conditions?.find((c) => c.type === 'Succeeded' && c.status === 'True');
const vmCreated = pipeline.find(
(p) => p?.name === 'VirtualMachineCreation' && p?.phase === 'Completed',
);

const getStatusLabel = (status: string) => {
switch (status) {
Expand All @@ -37,7 +39,7 @@ export const MigrationVirtualMachinesRowExtended: React.FC<RowProps<VMData>> = (

return (
<PageSection>
{success && (
{vmCreated && (
<>
<SectionHeading
text={'Virtual machine'}
Expand Down

0 comments on commit 2527a71

Please sign in to comment.