Skip to content

Commit

Permalink
Add initial support for adding OVA providers
Browse files Browse the repository at this point in the history
Signed-off-by: yaacov <yzamir@redhat.com>
  • Loading branch information
yaacov committed Jul 12, 2023
1 parent bf5ef58 commit 26146ae
Show file tree
Hide file tree
Showing 20 changed files with 448 additions and 3 deletions.
9 changes: 9 additions & 0 deletions ci/yaml/ova-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: forklift.konveyor.io/v1beta1
kind: Provider
metadata:
name: ova-test
namespace: konveyor-forklift
spec:
secret: {}
type: ova
url: 'https://fake.coml:5000/files/ova'
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
"Please enter URL for OpenStack services REST APIs.": "Please enter URL for OpenStack services REST APIs.",
"Please enter URL for the kubernetes API server, if empty URL default to this cluster.": "Please enter URL for the kubernetes API server, if empty URL default to this cluster.",
"Please enter URL for vSphere REST APIs server.": "Please enter URL for vSphere REST APIs server.",
"Please enter URL.": "Please enter URL.",
"Pod network": "Pod network",
"Product": "Product",
"Project": "Project",
Expand Down Expand Up @@ -231,6 +232,7 @@
"Specify the API end point URL, for example, https://<identity_service>/v3 for OpenStack.": "Specify the API end point URL, for example, https://<identity_service>/v3 for OpenStack.",
"Specify the API end point URL, for example, https://<kubernetes API Endpoint>:6443 for OpenShift.": "Specify the API end point URL, for example, https://<kubernetes API Endpoint>:6443 for OpenShift.",
"Specify the API end point URL, for example, https://<vCenter_host>/sdk for vSphere.": "Specify the API end point URL, for example, https://<vCenter_host>/sdk for vSphere.",
"Specify the end point to the NFS server path serving the OVA file[s].": "Specify the end point to the NFS server path serving the OVA file[s].",
"Specify the VDDK image that you created. some functionality will not be available if the VDDK image is left empty": "Specify the VDDK image that you created. some functionality will not be available if the VDDK image is left empty",
"SSHA-1 fingerprint": "SSHA-1 fingerprint",
"Staging": "Staging",
Expand Down Expand Up @@ -270,6 +272,7 @@
"Updated": "Updated",
"URL": "URL",
"URL must start with https:// or http:// and contain valid hostname and path": "URL must start with https:// or http:// and contain valid hostname and path",
"URL of the OVA provider": "URL of the OVA provider",
"URL of the provider": "URL of the provider",
"URL of the provider, leave empty to use this providers URL": "URL of the provider, leave empty to use this providers URL",
"User Domain Name": "User Domain Name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { EditModalProps } from '../EditModal';

import { OpenshiftEditURLModal } from './OpenshiftEditURLModal';
import { OpenstackEditURLModal } from './OpenstackEditURLModal';
import { OVAEditURLModal } from './OVAEditURLModal';
import { OvirtEditURLModal } from './OvirtEditURLModal';
import { VSphereEditURLModal } from './VSphereEditURLModal';

Expand All @@ -31,6 +32,8 @@ export const EditProviderURLModal: React.FC<EditProviderURLModalProps> = (props)
return <OpenstackEditURLModal {...props} />;
case 'vsphere':
return <VSphereEditURLModal {...props} />;
case 'ova':
return <OVAEditURLModal {...props} />;
default:
return <></>;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { useForkliftTranslation } from 'src/utils/i18n';

import { ProviderModel } from '@kubev2v/types';
import { ModalVariant } from '@patternfly/react-core';

import { EditModal, ValidationHookType } from '../EditModal';

import { patchProviderURL } from './utils/patchProviderURL';
import { EditProviderURLModalProps } from './EditProviderURLModal';

export const OVAEditURLModal: React.FC<EditProviderURLModalProps> = (props) => {
const { t } = useForkliftTranslation();

const urlValidationHook: ValidationHookType = (value) => {

Check warning on line 15 in packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderURL/OVAEditURLModal.tsx

View workflow job for this annotation

GitHub Actions / Run linter and tests

'value' is defined but never used
const isValidURL = true;

return isValidURL
? {
validationHelpText: undefined,
validated: 'success',
}
: {
validationHelpText: t(
'URL must start with https:// or http:// and contain valid hostname and path',
),
validated: 'error',
};
};

return (
<EditModal
{...props}
jsonPath={'spec.url'}
title={props?.title || t('Edit URL')}
label={props?.label || t('URL')}
model={ProviderModel}
variant={ModalVariant.large}
body={t('Specify the end point to the NFS server path serving the OVA file[s].')}
helperText={t('Please enter URL.')}
onConfirmHook={patchProviderURL}
validationHook={urlValidationHook}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export * from './EditProviderURLModal';
export * from './OpenshiftEditURLModal';
export * from './OpenstackEditURLModal';
export * from './OVAEditURLModal';
export * from './OvirtEditURLModal';
export * from './VSphereEditURLModal';
// @endindex
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ interface SelectableGalleryProps {
export const SelectableGallery: FC<SelectableGalleryProps> = ({
items,
onChange,
sortFunction = ([, a], [, b]) => a.title.localeCompare(b.title),
sortFunction,
selectedID,
}) => {
// State to manage the selected card's id
Expand All @@ -52,7 +52,9 @@ export const SelectableGallery: FC<SelectableGalleryProps> = ({
};

// Convert the items object to an array and sort it
const sortedItems = Object.entries(items).sort(sortFunction);
const sortedItems = sortFunction
? Object.entries(items).sort(sortFunction)
: Object.entries(items);

return (
<Gallery hasGutter className="forklift-selectable-gallery">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @index(['./*.tsx', './*.ts', /__/g], f => `export * from '${f.path}';`)
export * from './openshiftProviderValidator';
export * from './openstackProviderValidator';
export * from './ovaProviderValidator';
export * from './ovirtProviderValidator';
export * from './providerValidator';
export * from './vsphereProviderValidator';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { V1beta1Provider } from '@kubev2v/types';

import { validateK8sName } from '../common';

export function ovaProviderValidator(provider: V1beta1Provider) {
const name = provider?.metadata?.name;

if (!validateK8sName(name)) {
return new Error('invalided kubernetes resource name');
}

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { V1beta1Provider } from '@kubev2v/types';

import { openshiftProviderValidator } from './openshiftProviderValidator';
import { openstackProviderValidator } from './openstackProviderValidator';
import { ovaProviderValidator } from './ovaProviderValidator';
import { ovirtProviderValidator } from './ovirtProviderValidator';
import { vsphereProviderValidator } from './vsphereProviderValidator';

Expand All @@ -21,6 +22,9 @@ export function providerValidator(provider: V1beta1Provider) {
case 'vsphere':
validationError = vsphereProviderValidator(provider);
break;
case 'ova':
validationError = ovaProviderValidator(provider);
break;
default:
validationError = new Error('bad provider type');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export function secretValidator(type: string, secret: V1Secret) {
case 'vsphere':
validationError = vsphereSecretValidator(secret);
break;
case 'ova':
validationError = null;
break;
default:
validationError = new Error('bad provider type');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {

import { OpenshiftProviderFormCreate } from './OpenshiftProviderCreateForm';
import { OpenstackProviderCreateForm } from './OpenstackProviderCreateForm';
import { OVAProviderCreateForm } from './OVAProviderCreateForm';
import { OvirtProviderCreateForm } from './OvirtProviderCreateForm';
import { ProvidersCreateFormProps } from './ProviderCreateForm';
import { VSphereProviderCreateForm } from './VSphereProviderCreateForm';
Expand Down Expand Up @@ -48,6 +49,12 @@ export const EditProvider: React.FC<ProvidersCreateFormProps> = ({
<VSphereCredentialsEdit secret={newSecret} onChange={onNewSecretChange} />
</>
);
case 'ova':
return (
<>
<OVAProviderCreateForm provider={newProvider} onChange={onNewProviderChange} />
</>
);
default:
return <></>;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { useCallback, useReducer } from 'react';
import { Validation } from 'src/modules/Providers/utils';
import { useForkliftTranslation } from 'src/utils/i18n';

import { V1beta1Provider } from '@kubev2v/types';
import { Form, FormGroup, TextInput } from '@patternfly/react-core';

export interface OVAProviderCreateFormProps {
provider: V1beta1Provider;
onChange: (newValue: V1beta1Provider) => void;
}

export const OVAProviderCreateForm: React.FC<OVAProviderCreateFormProps> = ({
provider,
onChange,
}) => {
const { t } = useForkliftTranslation();

const url = provider?.spec?.url || '';

const initialState = {
validation: {
url: 'default' as Validation,
},
};

const reducer = (state, action) => {
switch (action.type) {
case 'SET_FIELD_VALIDATED':
return {
...state,
validation: {
...state.validation,
[action.payload.field]: action.payload.validationState,
},
};
default:
return state;
}
};

const [state, dispatch] = useReducer(reducer, initialState);

const handleChange = useCallback(
(id, value) => {
const trimmedValue = value.trim();

if (id === 'url') {
const validationState = 'success';
dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: id, validationState } });

onChange({ ...provider, spec: { ...provider.spec, url: trimmedValue } });
}
},
[provider],
);

return (
<Form isWidthLimited className="forklift-section-provider-edit">
<FormGroup
label={t('URL')}
fieldId="url"
helperText={t('URL of the OVA provider')}
validated={state.validation.url}
helperTextInvalid={t('Error: URL must be valid.')}
>
<TextInput
type="text"
id="url"
name="url"
value={url}
validated={state.validation.url}
onChange={(value) => handleChange('url', value)}
/>
</FormGroup>
</Form>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// @index(['./*', /style/g], f => `export * from '${f.path}';`)
export * from './EditProvider';
export * from './OpenshiftProviderCreateForm';
export * from './OpenstackProviderCreateForm';
export * from './OVAProviderCreateForm';
export * from './OvirtProviderCreateForm';
export * from './providerCardItems';
export * from './ProviderCreateForm';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SelectableGalleryItem } from 'src/modules/Providers/utils/components/Ga

import openshiftImg from '../images/openshift.svg';
import openstackImg from '../images/openstack.svg';
import ovaImg from '../images/ova.svg';
import redhatImg from '../images/redhat.svg';
import vmImg from '../images/vm.svg';

Expand Down Expand Up @@ -42,6 +43,15 @@ const redhatLogo = (
/>
);

const ovaLogo = (
<img
className="forklift--create-provider-edit-card-title-logo"
src={ovaImg}
alt="PatternFly logo"
width="27px"
/>
);

export const providerCardItems: Record<string, SelectableGalleryItem> = {
openshift: {
title: 'OpenShift Virtualization',
Expand All @@ -63,4 +73,9 @@ export const providerCardItems: Record<string, SelectableGalleryItem> = {
logo: vmLogo,
content: "vSphere is VMware's cloud computing virtualization platform.",
},
ova: {
title: 'Open Virtual Appliance (OVA)',
logo: ovaLogo,
content: 'OVA file is a virtual appliance used by virtualization applications.',
},
};
Loading

0 comments on commit 26146ae

Please sign in to comment.