Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion libs/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,8 @@
"Pushing": "Pushing",
"Complete": "Complete",
"Failed": "Failed",
"Canceling": "Canceling",
"Canceled": "Canceled",
"Converting": "Converting",
"Image built successfully": "Image built successfully",
"Export images": "Export images",
Expand All @@ -895,8 +897,8 @@
"OpenStack/KVM (QCOW2)": "OpenStack/KVM (QCOW2)",
"OpenShift Virtualization (QCOW2)": "OpenShift Virtualization (QCOW2)",
"Metal installer (ISO)": "Metal installer (ISO)",
"In progress": "In progress",
"Completed": "Completed",
"In progress": "In progress",
"Failed to load logs": "Failed to load logs",
"For built and failed export tasks, only the last 500 lines are available.": "For built and failed export tasks, only the last 500 lines are available.",
"Export image": "Export image",
Expand Down
2 changes: 2 additions & 0 deletions libs/types/imagebuilder/models/ImageBuildConditionReason.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ export enum ImageBuildConditionReason {
ImageBuildConditionReasonPushing = 'Pushing',
ImageBuildConditionReasonCompleted = 'Completed',
ImageBuildConditionReasonFailed = 'Failed',
ImageBuildConditionReasonCanceling = 'Canceling',
ImageBuildConditionReasonCanceled = 'Canceled',
}
2 changes: 2 additions & 0 deletions libs/types/imagebuilder/models/ImageExportConditionReason.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ export enum ImageExportConditionReason {
ImageExportConditionReasonPushing = 'Pushing',
ImageExportConditionReasonCompleted = 'Completed',
ImageExportConditionReasonFailed = 'Failed',
ImageExportConditionReasonCanceling = 'Canceling',
ImageExportConditionReasonCanceled = 'Canceled',
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '@patternfly/react-core';
import { Formik, FormikErrors } from 'formik';

import { ExportFormatType, ImageBuild } from '@flightctl/types/imagebuilder';
import { ExportFormatType, ImageBuild, ImageBuildConditionReason } from '@flightctl/types/imagebuilder';
import { RESOURCE, VERB } from '../../../types/rbac';
import { useTranslation } from '../../../hooks/useTranslation';
import { Link, ROUTE, useNavigate } from '../../../hooks/useNavigate';
Expand All @@ -34,7 +34,7 @@ import CreateImageBuildWizardFooter from './CreateImageBuildWizardFooter';
import { useFetch } from '../../../hooks/useFetch';
import { useEditImageBuild } from './useEditImageBuild';
import { OciRegistriesContextProvider, useOciRegistriesContext } from '../OciRegistriesContext';
import { hasImageBuildFailed } from '../../../utils/imageBuilds';
import { getImageBuildStatusReason } from '../../../utils/imageBuilds';

const orderedIds = [sourceImageStepId, outputImageStepId, registrationStepId, reviewStepId];

Expand Down Expand Up @@ -73,11 +73,14 @@ const CreateImageBuildWizard = () => {
const { isLoading: registriesLoading, error: registriesError } = useOciRegistriesContext();

const isEdit = !!imageBuildId;
const hasFailed = imageBuild ? hasImageBuildFailed(imageBuild) : false;
const buildReason = imageBuild ? getImageBuildStatusReason(imageBuild) : undefined;

let title: string;
if (isEdit) {
title = hasFailed ? t('Retry image build') : t('Duplicate image build');
title =
buildReason === ImageBuildConditionReason.ImageBuildConditionReasonFailed
? t('Retry image build')
: t('Duplicate image build');
} else {
title = t('Build new image');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ const getImageBuildStatusInfo = (condition: ImageBuildCondition | undefined, t:
return { level: 'success', label: t('Complete') };
case ImageBuildConditionReason.ImageBuildConditionReasonFailed:
return { level: 'danger', label: t('Failed') };
case ImageBuildConditionReason.ImageBuildConditionReasonCanceling:
return { level: 'warning', label: t('Canceling') };
case ImageBuildConditionReason.ImageBuildConditionReasonCanceled:
return { level: 'warning', label: t('Canceled') };
default:
return { level: 'unknown', label: t('Unknown') };
}
Expand All @@ -68,6 +72,10 @@ const getImageExportStatusInfo = (condition: ImageExportCondition | undefined, t
return { level: 'success', label: t('Complete') };
case ImageExportConditionReason.ImageExportConditionReasonFailed:
return { level: 'danger', label: t('Failed') };
case ImageExportConditionReason.ImageExportConditionReasonCanceling:
return { level: 'warning', label: t('Canceling') };
case ImageExportConditionReason.ImageExportConditionReasonCanceled:
return { level: 'warning', label: t('Canceled') };
default:
return { level: 'unknown', label: t('Unknown') };
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import * as React from 'react';
import { DropdownItem, DropdownList, Tab } from '@patternfly/react-core';

import { ImageBuildConditionReason } from '@flightctl/types/imagebuilder';
import { RESOURCE, VERB } from '../../../types/rbac';
import PageWithPermissions from '../../common/PageWithPermissions';
import { useTranslation } from '../../../hooks/useTranslation';
import { ROUTE, useNavigate } from '../../../hooks/useNavigate';
import { usePermissionsContext } from '../../common/PermissionsContext';
import { useAppContext } from '../../../hooks/useAppContext';
import { getImageBuildStatusReason } from '../../../utils/imageBuilds';
import DetailsPage from '../../DetailsPage/DetailsPage';
import DetailsPageActions from '../../DetailsPage/DetailsPageActions';
import DeleteImageBuildModal from '../DeleteImageBuildModal/DeleteImageBuildModal';
Expand All @@ -17,7 +19,6 @@ import ImageBuildYaml from './ImageBuildYaml';
import ImageBuildDetailsTab from './ImageBuildDetailsTab';
import ImageBuildExportsGallery from './ImageBuildExportsGallery';
import ImageBuildLogsTab from './ImageBuildLogsTab';
import { hasImageBuildFailed } from '../../../utils/imageBuilds';

const imageBuildDetailsPermissions = [
{ kind: RESOURCE.IMAGE_BUILD, verb: VERB.CREATE },
Expand All @@ -36,7 +37,7 @@ const ImageBuildDetailsPageContent = () => {
const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState<boolean>();
const { checkPermissions } = usePermissionsContext();
const [canCreate, canDelete] = checkPermissions(imageBuildDetailsPermissions);
const hasFailed = imageBuild ? hasImageBuildFailed(imageBuild) : false;
const buildReason = imageBuild ? getImageBuildStatusReason(imageBuild) : undefined;

return (
<DetailsPage
Expand All @@ -60,7 +61,9 @@ const ImageBuildDetailsPageContent = () => {
<DropdownList>
{canCreate && (
<DropdownItem onClick={() => navigate({ route: ROUTE.IMAGE_BUILD_EDIT, postfix: imageBuildId })}>
{hasFailed ? t('Retry') : t('Duplicate')}
{buildReason === ImageBuildConditionReason.ImageBuildConditionReasonFailed
? t('Retry')
: t('Duplicate')}
</DropdownItem>
)}
{canDelete && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@ import {
ToolbarItem,
} from '@patternfly/react-core';

import { ExportFormatType } from '@flightctl/types/imagebuilder';
import { ExportFormatType, ImageBuildConditionReason, ImageExportConditionReason } from '@flightctl/types/imagebuilder';
import { useTranslation } from '../../../hooks/useTranslation';
import {
hasImageBuildFailed,
isImageBuildComplete,
isImageExportFailed,
shouldHaveImageBuildLogs,
shouldHaveImageExportLogs,
getImageBuildStatusReason,
getImageExportStatusReason,
isImageBuildActiveReason,
isImageExportActiveReason,
} from '../../../utils/imageBuilds';
import { ImageBuildWithExports } from '../../../types/extraTypes';
import { LogResourceType, useImageBuildLogs } from '../useImageBuildLogs';
Expand All @@ -41,7 +40,7 @@ type LogEntity = {
id: string;
label: string;
isActive: boolean;
isFailed: boolean;
status: ImageBuildConditionReason | ImageExportConditionReason;
};

const getExportFormatText = (t: TFunction, format: ExportFormatType) => {
Expand All @@ -57,19 +56,29 @@ const getExportFormatText = (t: TFunction, format: ExportFormatType) => {
}
};

const ImageBuildAndExportLogStatus = ({ isActive, isFailed }: { isActive: boolean; isFailed: boolean }) => {
const ImageBuildAndExportLogStatus = ({
status,
}: {
status: ImageBuildConditionReason | ImageExportConditionReason;
}) => {
const { t } = useTranslation();
let label: string;
let level: StatusLevel;
if (isFailed) {
if (status === ImageBuildConditionReason.ImageBuildConditionReasonCompleted) {
level = 'success';
label = t('Completed');
} else if (status === ImageBuildConditionReason.ImageBuildConditionReasonFailed) {
level = 'danger';
label = t('Failed');
} else if (isActive) {
} else if (
status === ImageBuildConditionReason.ImageBuildConditionReasonCanceling ||
status === ImageBuildConditionReason.ImageBuildConditionReasonCanceled
) {
level = 'warning';
label = t('Canceled');
} else {
level = 'info';
label = t('In progress');
} else {
level = 'success';
label = t('Completed');
}
return <StatusDisplayContent label={label} level={level} />;
};
Expand All @@ -82,31 +91,33 @@ const ImageBuildLogsTab = ({ imageBuild }: { imageBuild: ImageBuildWithExports }
const { selectableEntities, hasExports } = React.useMemo(() => {
const entities: LogEntity[] = [];
const buildName = imageBuild.metadata.name as string;

const buildReason = getImageBuildStatusReason(imageBuild);
entities.push({
type: LogResourceType.BUILD,
id: buildName,
label: buildName,
isActive: shouldHaveImageBuildLogs(imageBuild),
isFailed: hasImageBuildFailed(imageBuild),
isActive: isImageBuildActiveReason(buildReason),
status: buildReason,
});

// ImageExports can only exist once the ImageBuild is complete
if (!isImageBuildComplete(imageBuild)) {
if (buildReason !== ImageBuildConditionReason.ImageBuildConditionReasonCompleted) {
return { selectableEntities: entities, availableExportFormats: [] as ExportFormatType[], failedExportsCount: 0 };
}

let hasExports = false;
imageBuild.imageExports.forEach((ie) => {
const format = ie?.spec.format;
if (format) {
const isFailed = isImageExportFailed(ie);
const exportReason = getImageExportStatusReason(ie);
hasExports = true;
entities.push({
type: LogResourceType.EXPORT,
id: ie.metadata.name as string,
label: getExportFormatText(t, format),
isActive: shouldHaveImageExportLogs(ie),
isFailed,
isActive: isImageExportActiveReason(exportReason),
status: exportReason,
});
}
});
Expand Down Expand Up @@ -237,7 +248,7 @@ const ImageBuildLogsTab = ({ imageBuild }: { imageBuild: ImageBuildWithExports }
</ToolbarGroup>
<ToolbarGroup align={{ default: 'alignEnd' }}>
<ToolbarItem>
<ImageBuildAndExportLogStatus isActive={selectedEntity.isActive} isFailed={selectedEntity.isFailed} />
<ImageBuildAndExportLogStatus status={selectedEntity.status} />
</ToolbarItem>
</ToolbarGroup>
</ToolbarContent>
Expand Down
13 changes: 6 additions & 7 deletions libs/ui-components/src/components/ImageBuilds/ImageBuildRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { Button, Content, Flex, FlexItem, Icon, Stack, StackItem, Title } from '
import { ActionsColumn, ExpandableRowContent, IAction, OnSelect, Tbody, Td, Tr } from '@patternfly/react-table';
import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon';

import { ImageBuild } from '@flightctl/types/imagebuilder';
import { ImageBuild, ImageBuildConditionReason } from '@flightctl/types/imagebuilder';
import { ImageBuildWithExports } from '../../types/extraTypes';
import { useTranslation } from '../../hooks/useTranslation';
import { ROUTE, useNavigate } from '../../hooks/useNavigate';
import { getImageBuildImage, hasImageBuildFailed } from '../../utils/imageBuilds';
import { getImageBuildImage, getImageBuildStatusReason } from '../../utils/imageBuilds';
import { getDateDisplay } from '../../utils/dates';
import ResourceLink from '../common/ResourceLink';
import { ImageBuildStatusDisplay } from './ImageBuildAndExportStatus';
import ImageBuildExportsGallery from './ImageBuildDetails/ImageBuildExportsGallery';
import { ImageBuildStatusDisplay } from './ImageBuildAndExportStatus';

type ImageBuildRowProps = {
imageBuild: ImageBuildWithExports;
Expand All @@ -37,6 +37,7 @@ const ImageBuildRow = ({
const [isExpanded, setIsExpanded] = React.useState(false);

const imageBuildName = imageBuild.metadata.name || '';
const buildReason = getImageBuildStatusReason(imageBuild);

const actions: IAction[] = [
{
Expand All @@ -48,7 +49,7 @@ const ImageBuildRow = ({
];

actions.push({
title: hasImageBuildFailed(imageBuild) ? t('Retry') : t('Duplicate'),
title: buildReason === ImageBuildConditionReason.ImageBuildConditionReasonFailed ? t('Retry') : t('Duplicate'),
onClick: () => {
navigate({ route: ROUTE.IMAGE_BUILD_EDIT, postfix: imageBuildName });
},
Expand All @@ -64,8 +65,6 @@ const ImageBuildRow = ({
const sourceImage = getImageBuildImage(imageBuild.spec.source);
const destinationImage = getImageBuildImage(imageBuild.spec.destination);

const hasError = hasImageBuildFailed(imageBuild);

return (
<Tbody isExpanded={isExpanded}>
<Tr isContentExpanded={isExpanded}>
Expand Down Expand Up @@ -108,7 +107,7 @@ const ImageBuildRow = ({
{t('Build information')}
</Title>
</StackItem>
{hasError && (
{buildReason === ImageBuildConditionReason.ImageBuildConditionReasonFailed && (
<Flex alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem>
<Icon status="danger">
Expand Down
23 changes: 11 additions & 12 deletions libs/ui-components/src/components/ImageBuilds/ImageExportCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,8 @@ import { CloudSecurityIcon } from '@patternfly/react-icons/dist/js/icons/cloud-s
import { ServerGroupIcon } from '@patternfly/react-icons/dist/js/icons/server-group-icon';
import { BuilderImageIcon } from '@patternfly/react-icons/dist/js/icons/builder-image-icon';

import { ExportFormatType, ImageExport } from '@flightctl/types/imagebuilder';
import {
getExportFormatDescription,
getExportFormatLabel,
isImageExportCompleted,
isImageExportFailed,
} from '../../utils/imageBuilds';
import { ExportFormatType, ImageExport, ImageExportConditionReason } from '@flightctl/types/imagebuilder';
import { getExportFormatDescription, getExportFormatLabel, getImageExportStatusReason } from '../../utils/imageBuilds';
import { getDateDisplay } from '../../utils/dates';
import { useTranslation } from '../../hooks/useTranslation';
import { ImageExportStatusDisplay } from './ImageBuildAndExportStatus';
Expand Down Expand Up @@ -115,8 +110,8 @@ export const ViewImageBuildExportCard = ({
} = useAppContext();
const routerNavigate = useRouterNavigate();
const exists = !!imageExport;
const failedExport = exists && isImageExportFailed(imageExport);
const completedExport = exists && isImageExportCompleted(imageExport);

const exportReason = exists ? getImageExportStatusReason(imageExport) : undefined;
const title = getExportFormatLabel(t, format);
const description = getExportFormatDescription(t, format);

Expand All @@ -141,7 +136,11 @@ export const ViewImageBuildExportCard = ({
<FlexItem className="fctl-imageexport-card__status">
<ImageExportStatusDisplay
imageStatus={imageExport.status}
imageReference={completedExport ? imageReference : undefined}
imageReference={
exportReason === ImageExportConditionReason.ImageExportConditionReasonCompleted
? imageReference
: undefined
}
/>
</FlexItem>
)}
Expand All @@ -159,7 +158,7 @@ export const ViewImageBuildExportCard = ({
<Stack hasGutter>
<StackItem>
<Flex>
{failedExport && (
{exportReason === ImageExportConditionReason.ImageExportConditionReasonFailed && (
<FlexItem>
<Button
variant="primary"
Expand All @@ -171,7 +170,7 @@ export const ViewImageBuildExportCard = ({
</Button>
</FlexItem>
)}
{completedExport && (
{exportReason === ImageExportConditionReason.ImageExportConditionReasonCompleted && (
<FlexItem>
<Button
variant="secondary"
Expand Down
Loading