import React, { Suspense, useState } from 'react';
import {
Alert,
Button,
ClipboardCopy,
List,
ListItem,
Modal,
ModalVariant,
Popover,
PopoverPosition,
Skeleton,
} from '@patternfly/react-core';
import {
ListComponent,
OrderType,
} from '@patternfly/react-core/dist/esm/components/List/List';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome';
import { useLoadModule, useScalprum } from '@scalprum/react-core';
import { useNavigate } from 'react-router-dom';
import {
FILE_SYSTEM_CUSTOMIZATION_URL,
MODAL_ANCHOR,
SEARCH_INPUT,
} from '../../constants';
import {
selectSelectedBlueprintId,
selectBlueprintSearchInput,
} from '../../store/BlueprintSlice';
import { useAppSelector } from '../../store/hooks';
import {
BlueprintItem,
useGetBlueprintsQuery,
ComposesResponseItem,
ComposeStatus,
ImageTypes,
useGetComposeStatusQuery,
} from '../../store/imageBuilderApi';
import {
isAwsUploadRequestOptions,
isAwss3UploadStatus,
isGcpUploadRequestOptions,
isOciUploadStatus,
} from '../../store/typeGuards';
import { resolveRelPath } from '../../Utilities/path';
import useProvisioningPermissions from '../../Utilities/useProvisioningPermissions';
type CloudInstancePropTypes = {
compose: ComposesResponseItem;
};
export const CloudInstance = ({ compose }: CloudInstancePropTypes) => {
const { initialized: chromeInitialized } = useChrome();
const scalprum = useScalprum();
const hasProvisioning = chromeInitialized && scalprum.config?.provisioning;
const { data, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id,
});
if (!isSuccess) {
return ;
}
if (hasProvisioning) {
return ;
} else {
return ;
}
};
const DisabledProvisioningLink = () => {
return (
);
};
type ProvisioningLinkPropTypes = {
compose: ComposesResponseItem;
composeStatus: ComposeStatus | undefined;
};
const ProvisioningLink = ({
compose,
composeStatus,
}: ProvisioningLinkPropTypes) => {
const [wizardOpen, setWizardOpen] = useState(false);
const [exposedScalprumModule, error] = useLoadModule(
{
scope: 'provisioning',
module: './ProvisioningWizard',
},
{}
);
const { permissions, isLoading: isLoadingPermission } =
useProvisioningPermissions();
// Recomputing this value on every render made the modal crash. Using a state
// helps avoiding this situation as the value is only set the first time.
const [appendTo] = useState(
document.querySelector(MODAL_ANCHOR) as HTMLElement
);
const selectedBlueprintId = useAppSelector(selectSelectedBlueprintId);
const blueprintSearchInput =
useAppSelector(selectBlueprintSearchInput) || SEARCH_INPUT;
const { selectedBlueprintVersion } = useGetBlueprintsQuery(
{ search: blueprintSearchInput },
{
selectFromResult: ({ data }) => ({
selectedBlueprintVersion: data?.data?.find(
(blueprint: BlueprintItem) => blueprint.id === selectedBlueprintId
)?.version,
}),
}
);
if (
error ||
!exposedScalprumModule ||
composeStatus.image_status.status !== 'success'
) {
return ;
} else {
const ProvisioningWizard = exposedScalprumModule?.default;
const provider = getImageProvider(compose);
const options = compose.request.image_requests[0].upload_request.options;
let sourceIds = undefined;
let accountIds = undefined;
if (isGcpUploadRequestOptions(options)) {
accountIds = options.share_with_accounts;
}
if (isAwsUploadRequestOptions(options)) {
accountIds = options.share_with_accounts;
sourceIds = options.share_with_sources;
}
const btn = (
);
const buttonWithTooltip = (
A newer version is available}
bodyContent={
This image can be launched, but it is not the latest version.
}
>
{btn}
);
return (
<>
{selectedBlueprintVersion &&
compose.blueprint_version !== selectedBlueprintVersion
? buttonWithTooltip
: btn}
{wizardOpen && (
setWizardOpen(false)}
image={{
name: compose.image_name || compose.id,
id: compose.id,
architecture: compose.request.image_requests[0].architecture,
provider: provider,
sourceIDs: sourceIds,
accountIDs: accountIds,
uploadOptions:
compose.request.image_requests[0].upload_request.options,
uploadStatus: composeStatus.image_status.upload_status,
}}
/>
)}
>
);
}
};
const getImageProvider = (compose: ComposesResponseItem) => {
const imageType = compose.request.image_requests[0].image_type;
switch (imageType) {
case 'aws':
return 'aws';
case 'ami':
return 'aws';
case 'azure':
return 'azure';
case 'gcp':
return 'gcp';
default:
//TODO check with Provisioning: what if imageType is not 'aws', 'ami', or 'azure'?
return 'aws';
}
};
type OciInstancePropTypes = {
compose: ComposesResponseItem;
isExpired: boolean;
};
export const OciInstance = ({ compose, isExpired }: OciInstancePropTypes) => {
const navigate = useNavigate();
const { data, isSuccess, isFetching, isError } = useGetComposeStatusQuery({
composeId: compose.id,
});
if (!isSuccess) {
return ;
}
const options = data.image_status.upload_status?.options;
if (options && !isOciUploadStatus(options)) {
throw TypeError(
`Error: options must be of type OciUploadStatus, not ${typeof options}.`
);
}
if (isExpired) {
return (
);
} else {
return (
Launch an OCI image}
minWidth="30rem"
bodyContent={
<>
To run the image copy the link below and follow the steps below:
Go to "Compute" in Oracle Cloud and choose "
Custom Images".
Click on "Import image", choose "Import from an
object storage URL".
Choose "Import from an object storage URL" and paste
the URL in the "Object Storage URL" field. The image
type has to be set to QCOW2 and the launch mode should be
paravirtualized.
{isSuccess && (
{options?.url || ''}
)}
{isFetching && }
{isError && (
)}
}
iconPosition="right"
// TO DO update the link after documentation is up
href={FILE_SYSTEM_CUSTOMIZATION_URL}
className="pf-v5-u-pl-0"
>
Read more about launching OCI images
>
}
>
);
}
};
type AwsS3InstancePropTypes = {
compose: ComposesResponseItem;
isExpired: boolean;
};
export const AwsS3Instance = ({
compose,
isExpired,
}: AwsS3InstancePropTypes) => {
const { data: composeStatus, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id,
});
if (!isSuccess) {
return ;
}
const fileExtensions: { [key in ImageTypes]: string } = {
aws: '',
azure: '',
'edge-commit': '',
'edge-installer': '',
gcp: '',
'guest-image': '.qcow2',
'image-installer': '.iso',
vsphere: '.vmdk',
'vsphere-ova': '.ova',
wsl: '.tar.gz',
ami: '',
'rhel-edge-commit': '',
'rhel-edge-installer': '',
vhd: '',
oci: '',
};
const status = composeStatus.image_status.status;
const options = composeStatus.image_status.upload_status?.options;
if (options && !isAwss3UploadStatus(options)) {
throw TypeError(
`Error: options must be of type Awss3UploadStatus, not ${typeof options}.`
);
}
if (status !== 'success') {
return (
);
} else {
return (
);
}
};