Skip to content

Commit 0bf45a8

Browse files
authored
Merge pull request #1123 from sgratch/add-rhv-ui-link
🐾 Provide the RHV UI URL as part of the RHV provider details page
2 parents d9fdf5a + 8562ca0 commit 0bf45a8

File tree

9 files changed

+171
-9
lines changed

9 files changed

+171
-9
lines changed

packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json

+4
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
"Edit Provider": "Edit Provider",
137137
"Edit Provider Credentials": "Edit Provider Credentials",
138138
"Edit provider credentials.\n Use this link to edit the providers credentials instead of editing the secret directly.": "Edit provider credentials.\n Use this link to edit the providers credentials instead of editing the secret directly.",
139+
"Edit provider web UI link": "Edit provider web UI link",
139140
"Edit Snapshot polling interval (seconds)": "Edit Snapshot polling interval (seconds)",
140141
"Edit StorageMap": "Edit StorageMap",
141142
"Edit target namespace": "Edit target namespace",
@@ -198,6 +199,7 @@
198199
"Issuer": "Issuer",
199200
"Kubernetes name of the new migration Plan resource": "Kubernetes name of the new migration Plan resource",
200201
"Label": "Label",
202+
"Link for the Red Hat Virtualization Manager (RHVM) landing page. For example, https://rhv-host-example.com/ovirt-engine.": "Link for the Red Hat Virtualization Manager (RHVM) landing page. For example, https://rhv-host-example.com/ovirt-engine.",
201203
"List of objects depended by this object. If ALL objects in the list have been deleted,\n this object will be garbage collected. If this object is managed by a controller,\n then an entry in this list will point to this controller, with the controller field set to true.\n There cannot be more than one managing controller.": "List of objects depended by this object. If ALL objects in the list have been deleted,\n this object will be garbage collected. If this object is managed by a controller,\n then an entry in this list will point to this controller, with the controller field set to true.\n There cannot be more than one managing controller.",
202204
"Loading": "Loading",
203205
"Loading...": "Loading...",
@@ -348,12 +350,14 @@
348350
"Provider details": "Provider details",
349351
"Provider inventory": "Provider inventory",
350352
"Provider resource name": "Provider resource name",
353+
"Provider web UI link": "Provider web UI link",
351354
"Provider YAML": "Provider YAML",
352355
"Providers": "Providers",
353356
"Providers default": "Providers default",
354357
"Providers for virtualization": "Providers for virtualization",
355358
"Ready": "Ready",
356359
"Reason": "Reason",
360+
"Red Hat Virtualization Manager UI": "Red Hat Virtualization Manager UI",
357361
"Region": "Region",
358362
"Regions": "Regions",
359363
"Remove cutover": "Remove cutover",

packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderUI/EditProviderUIModal.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { K8sModel } from '@openshift-console/dynamic-plugin-sdk/lib/api/core-api
55

66
import { EditModalProps } from '../EditModal';
77

8+
import { OvirtEditUIModal } from './OvirtEditUIModal';
9+
810
export type EditProviderUIModalProps = Modify<
911
EditModalProps,
1012
{
@@ -20,6 +22,7 @@ export type EditProviderUIModalProps = Modify<
2022
export const EditProviderUIModal: React.FC<EditProviderUIModalProps> = (props) => {
2123
switch (props.resource?.spec?.type) {
2224
case 'ovirt':
25+
return <OvirtEditUIModal {...props} />;
2326
case 'openshift':
2427
case 'openstack':
2528
case 'vsphere':
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import { ForkliftTrans, useForkliftTranslation } from 'src/utils/i18n';
3+
4+
import { ProviderModel } from '@kubev2v/types';
5+
import { ModalVariant } from '@patternfly/react-core';
6+
7+
import { validateOvirtUILink } from '../../utils';
8+
import { EditModal } from '../EditModal';
9+
10+
import { patchProviderUI } from './utils/patchProviderUI';
11+
import { EditProviderUIModalProps } from './EditProviderUIModal';
12+
13+
export const OvirtEditUIModal: React.FC<EditProviderUIModalProps> = (props) => {
14+
const { t } = useForkliftTranslation();
15+
16+
const ModalBody = (
17+
<ForkliftTrans>
18+
<p>Link for the Red Hat Virtualization Manager (RHVM) landing page.</p>
19+
<br />
20+
<p>Use this link to access the RHVM user interface for RHV VMs management.</p>
21+
<br />
22+
<p>
23+
The format of the provided link for the Red Hat Virtualization Manager (RHVM) landing page
24+
should include a scheme, a domain name, path, and optionally a port. Usually the path will
25+
end with /ovirt-engine, for example:{' '}
26+
<strong>https://rhv-host-example.com/ovirt-engine</strong>.
27+
</p>
28+
<br />
29+
<p>If no link value is specify then a default auto calculated or an empty value is set.</p>
30+
</ForkliftTrans>
31+
);
32+
33+
return (
34+
<EditModal
35+
{...props}
36+
jsonPath={['metadata', 'annotations', 'forklift.konveyor.io/providerUI']}
37+
title={props?.title || t('Edit provider web UI link')}
38+
label={props?.label || t('Provider web UI link')}
39+
model={ProviderModel}
40+
variant={ModalVariant.large}
41+
body={ModalBody}
42+
helperText={t(
43+
'Link for the Red Hat Virtualization Manager (RHVM) landing page. For example, https://rhv-host-example.com/ovirt-engine.',
44+
)}
45+
onConfirmHook={patchProviderUI}
46+
validationHook={validateOvirtUILink}
47+
/>
48+
);
49+
};
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
// @index('./*.tsx', f => `export * from '${f.path}';`)
22
export * from './EditProviderUIModal';
3+
export * from './OvirtEditUIModal';
34
// @endindex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { k8sPatch } from '@openshift-console/dynamic-plugin-sdk';
2+
3+
import { OnConfirmHookType } from '../../EditModal';
4+
5+
/**
6+
* Handles the confirmation action for editing a resource annotations.
7+
* Adds or updates the 'forklift.konveyor.io/providerUI' annotation in the resource's metadata.
8+
*
9+
* @param {Object} options - Options for the confirmation action.
10+
* @param {Object} options.resource - The resource to be modified.
11+
* @param {Object} options.model - The model associated with the resource.
12+
* @param {any} options.newValue - The new value for the 'forklift.konveyor.io/providerUI' annotation.
13+
* @returns {Promise<Object>} - The modified resource.
14+
*/
15+
export const patchProviderUI: OnConfirmHookType = async ({ resource, model, newValue: value }) => {
16+
const currentAnnotations = resource?.metadata?.annotations;
17+
const newAnnotations = {
18+
...currentAnnotations,
19+
'forklift.konveyor.io/providerUI': value || undefined,
20+
};
21+
22+
const op = resource?.metadata?.annotations ? 'replace' : 'add';
23+
24+
const obj = await k8sPatch({
25+
model: model,
26+
resource: resource,
27+
data: [
28+
{
29+
op,
30+
path: '/metadata/annotations',
31+
value: newAnnotations,
32+
},
33+
],
34+
});
35+
36+
return obj;
37+
};

packages/forklift-console-plugin/src/modules/Providers/utils/validators/provider/ovirt/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
export * from './ovirtProviderValidator';
33
export * from './ovirtSecretFieldValidator';
44
export * from './ovirtSecretValidator';
5+
export * from './validateOvirtUILink';
56
export * from './validateOvirtURL';
67
// @endindex
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { validateURL, ValidationMsg } from '../../common';
2+
3+
export const validateOvirtUILink = (uiLink: string | number): ValidationMsg => {
4+
// For a newly opened form where the field is not set yet, set the validation type to default.
5+
if (uiLink === undefined) {
6+
return {
7+
type: 'default',
8+
msg: 'The link of the Red Hat Virtualization Manager (RHVM) landing page. For example, https://rhv-host-example.com/ovirt-engine.',
9+
};
10+
}
11+
12+
// Sanity check
13+
if (typeof uiLink !== 'string') {
14+
return {
15+
type: 'error',
16+
msg: 'The link for the Red Hat Virtualization Manager (RHVM) landing page is not a string',
17+
};
18+
}
19+
20+
const trimmedUrl: string = uiLink.trim();
21+
const isValidURL = validateURL(trimmedUrl);
22+
23+
if (trimmedUrl === '') {
24+
return {
25+
type: 'warning',
26+
msg: 'The link for the Red Hat Virtualization Manager (RHVM) landing page is empty. A default or an empty value will be used.',
27+
};
28+
}
29+
30+
if (!isValidURL) {
31+
return {
32+
type: 'error',
33+
msg: 'The link for the Red Hat Virtualization Manager (RHVM) landing page is invalid. It should include the schema and path, for example: https://rhv-host-example.com/ovirt-engine.',
34+
};
35+
}
36+
37+
if (!trimmedUrl.endsWith('ovirt-engine') && !trimmedUrl.endsWith('ovirt-engine/'))
38+
return {
39+
msg: 'The link for the Red Hat Virtualization Manager (RHVM) landing page does not end with a /ovirt-engine path, for example: https://rhv-host-example.com/ovirt-engine.',
40+
type: 'warning',
41+
};
42+
43+
return {
44+
type: 'success',
45+
msg: 'The link for the Red Hat Virtualization Manager (RHVM) landing page. For example, https://rhv-host-example.com/ovirt-engine.',
46+
};
47+
};

packages/forklift-console-plugin/src/modules/Providers/views/details/components/DetailsSection/OvirtDetailsSection.tsx

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import { useForkliftTranslation } from 'src/utils/i18n';
33

4+
import { V1beta1Provider } from '@kubev2v/types';
45
import { DescriptionList } from '@patternfly/react-core';
56

67
import { DetailsItem } from '../../../../utils';
@@ -21,6 +22,21 @@ export const OvirtDetailsSection: React.FC<DetailsSectionProps> = ({ data }) =>
2122

2223
const { provider, permissions } = data;
2324

25+
/**
26+
* A function for auto calculating the RHV UI link.
27+
* It extracts the provider's RHV UI link from the RHV URL by searching for the URL's path of
28+
* '/ovirt-engine/api[/]' and cutting out the /api[/] path section.
29+
* If RHV URL is invalid then an empty string is returned
30+
*/
31+
const getProviderUiContent = (provider: V1beta1Provider): string => {
32+
const uiLinkRegexp = new RegExp('(?<=ovirt-engine)\\/api(\\/)*$', 'g');
33+
const regexpResult = uiLinkRegexp.exec(provider?.spec?.url);
34+
35+
return provider?.spec?.url && regexpResult
36+
? provider?.spec?.url.slice(0, uiLinkRegexp.lastIndex - regexpResult[0].length)
37+
: '';
38+
};
39+
2440
return (
2541
<DescriptionList
2642
columnModifier={{
@@ -31,7 +47,12 @@ export const OvirtDetailsSection: React.FC<DetailsSectionProps> = ({ data }) =>
3147

3248
<DetailsItem title={''} content={''} />
3349

34-
<NameAndUiLinkDetailsItem resource={provider} />
50+
<NameAndUiLinkDetailsItem
51+
resource={provider}
52+
canPatch={permissions.canPatch}
53+
webUILinkText={t(`Red Hat Virtualization Manager UI`)}
54+
webUILinkCalcVal={getProviderUiContent(provider)}
55+
/>
3556

3657
<NamespaceDetailsItem resource={provider} />
3758

packages/forklift-console-plugin/src/modules/Providers/views/details/components/DetailsSection/components/NameAndUiLinkDetailsItem.tsx

+7-8
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import { ProviderDetailsItemProps } from './ProviderDetailsItem';
1212

1313
/**
1414
* @typedef {Object} NameDetailsItemProps - extends ProviderDetailsItemProps
15-
16-
* @property {string} [webUILinkText - A text to be displayed as a content.
17-
* @property {function} [calcWebUILink] - A method for auto calculating the provider's UI path.
18-
**/
15+
*
16+
* @property {string} [webUILinkText - A label text to be displayed as a content.
17+
* @property {string} [webUILinkCalcVal] - provider's auto calculated UI link (uses as a default if no value is set by the user).
18+
*/
1919

2020
export interface NameAndUiLinkDetailsItemProps extends ProviderDetailsItemProps {
2121
webUILinkText?: string;
22-
calcWebUILink?: (provider: V1beta1Provider) => string;
22+
webUILinkCalcVal?: string;
2323
}
2424

2525
const getProviderUIAnnotation = (provider: V1beta1Provider): string =>
@@ -40,7 +40,7 @@ export const NameAndUiLinkDetailsItem: React.FC<NameAndUiLinkDetailsItemProps> =
4040
helpContent,
4141
canPatch,
4242
webUILinkText,
43-
calcWebUILink,
43+
webUILinkCalcVal,
4444
}) => {
4545
const { t } = useForkliftTranslation();
4646
const { showModal } = useModal();
@@ -65,8 +65,7 @@ export const NameAndUiLinkDetailsItem: React.FC<NameAndUiLinkDetailsItemProps> =
6565
const nameContent = provider?.metadata?.name;
6666

6767
// Read the annotation for getting the web UI link and only if empty, try to auto calculate the provider web UI path
68-
const webUILink =
69-
getProviderUIAnnotation(provider) ?? (calcWebUILink ? calcWebUILink(provider) : null);
68+
const webUILink = getProviderUIAnnotation(provider) ?? webUILinkCalcVal;
7069

7170
const webUILinkContent = (
7271
<ExternalLink text={webUILinkText} href={webUILink} isInline={true}>

0 commit comments

Comments
 (0)