From 4609b3745ec21558ec6e5b6da71cba96d80b078b Mon Sep 17 00:00:00 2001 From: Cara Wang Date: Fri, 17 Jan 2025 18:26:32 +0800 Subject: [PATCH] chore(KFLUXUI-256): support component.spec.containerImage as latestImage (#67) --- .../__data__/WorkflowComponentsData.ts | 3 +- .../tabs/ComponentDetails.tsx | 3 +- .../tabs/ComponentLatestBuild.tsx | 3 +- .../__tests__/ComponentDetailsTab.spec.tsx | 2 +- .../ComponentsListView/ComponentsListRow.tsx | 8 +++-- .../CustomizedPipeline/CustomizePipelines.tsx | 18 +++++----- .../__tests__/CustomizePipeline.spec.tsx | 7 ++-- .../SnapshotComponentsListRow.spec.tsx | 2 +- src/types/component.ts | 1 + src/utils/__tests__/component-utils.spec.ts | 34 +++++++++++++++++++ src/utils/component-utils.ts | 13 +++++++ 11 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/components/ApplicationDetails/__data__/WorkflowComponentsData.ts b/src/components/ApplicationDetails/__data__/WorkflowComponentsData.ts index e3357db..e9b5db1 100644 --- a/src/components/ApplicationDetails/__data__/WorkflowComponentsData.ts +++ b/src/components/ApplicationDetails/__data__/WorkflowComponentsData.ts @@ -106,7 +106,7 @@ export const mockComponentsData = [ spec: { application: 'test-dev-samples', componentName: 'test-dotnet60', - containerImage: 'quay.io/redhat-appstudio/user-workload:test-ns-test-dotnet60', + containerImage: 'quay.io/redhat-appstudio/user-workload:test-ns-test-dotnet61', replicas: 1, resources: { requests: { @@ -140,6 +140,7 @@ export const mockComponentsData = [ type: 'Created', }, ], + lastPromotedImage: 'quay.io/redhat-appstudio/user-workload:test-ns-test-dotnet60', containerImage: 'quay.io/redhat-appstudio/user-workload:test-ns-test-dotnet60', devfile: 'commands:\n- apply:\n component: dockerfile-build\n id: build-image\ncomponents:\n- image:\n dockerfile:\n rootRequired: false\n uri: https://raw.githubusercontent.com/test-user-1/devfile-sample-dotnet60-basic/main/docker/Dockerfile\n imageName: ""\n name: dockerfile-build\n- attributes:\n deployment/container-port: 8081\n deployment/cpuRequest: 10m\n deployment/memoryRequest: 100Mi\n deployment/replicas: 1\n deployment/storageRequest: "0"\n kubernetes:\n inlined: placeholder\n name: kubernetes\nmetadata:\n description: Basic Devfile for a Dockerfile Component\n name: dockerfile-component\nschemaVersion: 2.2.0\n', diff --git a/src/components/Components/ComponentDetails/tabs/ComponentDetails.tsx b/src/components/Components/ComponentDetails/tabs/ComponentDetails.tsx index 46b4c96..312f5a6 100644 --- a/src/components/Components/ComponentDetails/tabs/ComponentDetails.tsx +++ b/src/components/Components/ComponentDetails/tabs/ComponentDetails.tsx @@ -11,6 +11,7 @@ import yamlParser from 'js-yaml'; import { useLatestPushBuildPipelineRunForComponent } from '../../../../hooks/usePipelineRuns'; import ExternalLink from '../../../../shared/components/links/ExternalLink'; import { ComponentKind } from '../../../../types'; +import { getLastestImage } from '../../../../utils/component-utils'; import { getPipelineRunStatusResults } from '../../../../utils/pipeline-utils'; import GitRepoLink from '../../../GitLink/GitRepoLink'; import { useWorkspaceInfo } from '../../../Workspace/useWorkspaceInfo'; @@ -35,7 +36,7 @@ const ComponentDetails: React.FC> ? getPipelineRunStatusResults(latestPushBuildPLR) : null; const latestImageURL = results?.find((result) => result.name === RESULT_NAME); - const componentImageURL = latestImageURL?.value ?? component.spec.containerImage; + const componentImageURL = latestImageURL?.value ?? getLastestImage(component); const runTime = React.useMemo(() => { try { diff --git a/src/components/Components/ComponentDetails/tabs/ComponentLatestBuild.tsx b/src/components/Components/ComponentDetails/tabs/ComponentLatestBuild.tsx index 555e577..6495f34 100644 --- a/src/components/Components/ComponentDetails/tabs/ComponentLatestBuild.tsx +++ b/src/components/Components/ComponentDetails/tabs/ComponentLatestBuild.tsx @@ -19,6 +19,7 @@ import ErrorEmptyState from '../../../../shared/components/empty-state/ErrorEmpt import { Timestamp } from '../../../../shared/components/timestamp/Timestamp'; import { ComponentKind } from '../../../../types'; import { getCommitsFromPLRs } from '../../../../utils/commits-utils'; +import { getLastestImage } from '../../../../utils/component-utils'; import CommitLabel from '../../../Commits/commit-label/CommitLabel'; import { useBuildLogViewerModal } from '../../../LogViewer/BuildLogViewer'; import ScanDescriptionListGroup from '../../../PipelineRun/PipelineRunDetailsView/tabs/ScanDescriptionListGroup'; @@ -43,7 +44,7 @@ const ComponentLatestBuild: React.FC { it('should renderWithQueryClientAndRouter Component container image URL when latest build url not found', () => { useComponentMock.mockReturnValue([ - { ...mockComponent, spec: { containerImage: 'test-url', ...mockComponent.spec } }, + { ...mockComponent, status: { lastPromotedImage: 'test-url', ...mockComponent.status } }, true, ]); renderWithQueryClientAndRouter(); diff --git a/src/components/Components/ComponentsListView/ComponentsListRow.tsx b/src/components/Components/ComponentsListView/ComponentsListRow.tsx index 13f98ad..647720a 100644 --- a/src/components/Components/ComponentsListView/ComponentsListRow.tsx +++ b/src/components/Components/ComponentsListView/ComponentsListRow.tsx @@ -7,6 +7,7 @@ import ActionMenu from '../../../shared/components/action-menu/ActionMenu'; import ExternalLink from '../../../shared/components/links/ExternalLink'; import { ComponentKind, PipelineRunKind } from '../../../types'; import { getCommitsFromPLRs } from '../../../utils/commits-utils'; +import { getLastestImage } from '../../../utils/component-utils'; import CommitLabel from '../../Commits/commit-label/CommitLabel'; import { ComponentRelationStatusIcon } from '../../ComponentRelation/details-page/ComponentRelationStatusIcon'; import GitRepoLink from '../../GitLink/GitRepoLink'; @@ -33,6 +34,7 @@ const ComponentsListRow: React.FC< const name = component.metadata.name; const actions = useComponentActions(component, name); const buildLogsModal = useBuildLogViewerModal(component); + const latestImage = getLastestImage(component); const commit = React.useMemo( () => @@ -66,14 +68,14 @@ const ComponentsListRow: React.FC< /> )} - {component.spec.containerImage && ( + {latestImage && ( tag */ style={{ userSelect: 'auto' }} - href={getContainerImageLink(component.spec.containerImage)} - text={component.spec.containerImage} + href={getContainerImageLink(latestImage)} + text={latestImage} /> )} diff --git a/src/components/CustomizedPipeline/CustomizePipelines.tsx b/src/components/CustomizedPipeline/CustomizePipelines.tsx index df708fc..10bd1f3 100644 --- a/src/components/CustomizedPipeline/CustomizePipelines.tsx +++ b/src/components/CustomizedPipeline/CustomizePipelines.tsx @@ -29,7 +29,12 @@ import { ComponentModel } from '../../models'; import ExternalLink from '../../shared/components/links/ExternalLink'; import { ComponentKind } from '../../types'; import { useTrackEvent, TrackEvents } from '../../utils/analytics'; -import { enablePAC, disablePAC, useComponentBuildStatus } from '../../utils/component-utils'; +import { + enablePAC, + disablePAC, + useComponentBuildStatus, + getLastestImage, +} from '../../utils/component-utils'; import { useAccessReviewForModel } from '../../utils/rbac'; import AnalyticsButton from '../AnalyticsButton/AnalyticsButton'; import { ButtonWithAccessTooltip } from '../ButtonWithAccessTooltip'; @@ -111,6 +116,7 @@ const Row: React.FC< const buildStatus = useComponentBuildStatus(component); const pacError = buildStatus?.pac?.['error-message']; const prURL = buildStatus?.pac?.['merge-url']; + const latestImage = getLastestImage(component); React.useEffect(() => { onStateChange(pacState); @@ -140,16 +146,12 @@ const Row: React.FC< /> )} - {component.spec.containerImage && ( + {latestImage && (
Image:{' '} } + href={latestImage.startsWith('http') ? latestImage : `https://${latestImage}`} + text={} />
)} diff --git a/src/components/CustomizedPipeline/__tests__/CustomizePipeline.spec.tsx b/src/components/CustomizedPipeline/__tests__/CustomizePipeline.spec.tsx index 05a7fc5..66850a3 100644 --- a/src/components/CustomizedPipeline/__tests__/CustomizePipeline.spec.tsx +++ b/src/components/CustomizedPipeline/__tests__/CustomizePipeline.spec.tsx @@ -239,11 +239,14 @@ describe('CustomizePipeline', () => { ({ const rowData: SnapshotComponentTableData = { metadata: { uid: mockComponentsData[1].metadata.uid, name: mockComponentsData[1].metadata.name }, name: mockComponentsData[1].metadata.name, - containerImage: mockComponentsData[1].spec.containerImage, + containerImage: mockComponentsData[1].status.lastPromotedImage, application: 'test-app', source: { git: { url: mockComponentsData[1].spec.source.git.url, revision: 'main' } }, }; diff --git a/src/types/component.ts b/src/types/component.ts index 85e4f87..5c71eb9 100644 --- a/src/types/component.ts +++ b/src/types/component.ts @@ -50,6 +50,7 @@ export type ComponentSpecs = { export type ComponentKind = K8sResourceCommon & { spec: ComponentSpecs; status?: { + lastPromotedImage?: string; containerImage?: string; conditions?: ResourceStatusCondition[]; devfile?: string; diff --git a/src/utils/__tests__/component-utils.spec.ts b/src/utils/__tests__/component-utils.spec.ts index 249e2be..4250fc7 100644 --- a/src/utils/__tests__/component-utils.spec.ts +++ b/src/utils/__tests__/component-utils.spec.ts @@ -10,6 +10,7 @@ import { startNewBuild, BUILD_REQUEST_ANNOTATION, BuildRequest, + getLastestImage, } from '../component-utils'; import { createK8sUtilMock } from '../test-utils'; @@ -160,4 +161,37 @@ describe('component-utils', () => { message: 'done', }); }); + + it('should return status.lastPromotedImage when lastPromotedImage is available', () => { + const mockComponent = { + status: { + lastPromotedImage: 'test-url', + }, + } as unknown as ComponentKind; + + expect(getLastestImage(mockComponent)).toEqual('test-url'); + }); + + it('should return spec.containerImage when lastPromotedImage is unavailable', () => { + const mockComponent = { + spec: { + containerImage: 'test-url', + }, + } as unknown as ComponentKind; + + expect(getLastestImage(mockComponent)).toEqual('test-url'); + }); + + it('should return status.lastPromotedImage when lastPromotedImage and containerImage are both available', () => { + const mockComponent = { + spec: { + containerImage: 'test-url', + }, + status: { + lastPromotedImage: 'test-url-promoted', + }, + } as unknown as ComponentKind; + + expect(getLastestImage(mockComponent)).toEqual('test-url-promoted'); + }); }); diff --git a/src/utils/component-utils.ts b/src/utils/component-utils.ts index ebdb8ad..1fdabdb 100644 --- a/src/utils/component-utils.ts +++ b/src/utils/component-utils.ts @@ -45,6 +45,19 @@ export type ComponentBuildStatus = { message?: string; }; +/** + * + * @param component + * @returns name of latest container image + * + * The latest container image fieled is likely changed from time to time. + * So it is valueable to track it as utils to avoid bringing multiple changes + * accross several files for furture possible changes. + * + */ +export const getLastestImage = (component: ComponentKind) => + component.status?.lastPromotedImage || component.spec?.containerImage; + /** * If whole pac section is missing, PaC state is considered disabled * Missing pac section shows that PaC was never requested on this component before,