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
6 changes: 5 additions & 1 deletion public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
"FluxList": {
"tableNameHeader": "Name",
"tableStatusHeader": "Status",
"tableCreatedHeader": "Created"
"tableCreatedHeader": "Created",
"tableVersionHeader": "Revision",
"noFluxError": "Please install flux to view this component",
"gitOpsTitle": "GitOps",
"kustomizationsTitle": "Kustomizations"
},
"ProvidersList": {
"tableNameHeader": "Name",
Expand Down
153 changes: 128 additions & 25 deletions src/components/ControlPlane/FluxList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import {
AnalyticalTableColumnDefinition,
Title,
} from '@ui5/webcomponents-react';
import ReactTimeAgo from 'react-time-ago';
import IllustratedError from '../Shared/IllustratedError.tsx';
import useResource from '../../lib/api/useApiResource';
import { FluxGitRepo } from '../../lib/api/types/flux/listGitRepo';
import { FluxRequest } from '../../lib/api/types/flux/listGitRepo';
import { FluxKustomization } from '../../lib/api/types/flux/listKustomization';
import { useTranslation } from 'react-i18next';
import { timeAgo } from '../../utils/i18n/timeAgo.ts';
import { ResourceStatusCell } from '../Shared/ResourceStatusCell.tsx';

export default function FluxList() {
const {
data: repoData,
data: gitReposData,
error: repoErr,
isLoading: repoIsLoading,
} = useResource(FluxGitRepo); //404 if component not enabled
} = useResource(FluxRequest); //404 if component not enabled
const {
data: kustmizationData,
error: kustomizationErr,
Expand All @@ -24,43 +25,145 @@ export default function FluxList() {

const { t } = useTranslation();

if (repoErr) {
return <IllustratedError error={repoErr} />;
interface CellData<T> {
cell: {
value: T | null; // null for grouping rows
row: {
original?: FluxRow; // missing for grouping rows
};
};
}
if (kustomizationErr) {
return <IllustratedError error={kustomizationErr} />;

type FluxRow = {
name: string;
created: string;
isReady: boolean;
statusUpdateTime?: string;
};

if (repoErr || kustomizationErr) {
return (
<IllustratedError
error={repoErr || kustomizationErr}
title={t('FluxList.noFluxError')}
/>
);
}

const columns: AnalyticalTableColumnDefinition[] = [
const gitReposColumns: AnalyticalTableColumnDefinition[] = [
{
Header: t('FluxList.tableNameHeader'),
accessor: 'metadata.name',
accessor: 'name',
},
{
Header: t('FluxList.tableStatusHeader'),
accessor: 'status.usages',
accessor: 'status',
Cell: (cellData: CellData<FluxRow['isReady']>) =>
cellData.cell.row.original?.isReady != null ? (
<ResourceStatusCell
value={cellData.cell.row.original?.isReady}
transitionTime={
cellData.cell.row.original?.statusUpdateTime
? cellData.cell.row.original?.statusUpdateTime
: ''
}
/>
) : null,
},
{
Header: t('FluxList.tableVersionHeader'),
accessor: 'revision',
},
{
Header: t('FluxList.tableCreatedHeader'),
accessor: 'metadata.creationTimestamp',
Cell: (props: any) => <ReactTimeAgo date={new Date(props.cell.value)} />,
accessor: 'created',
},
];

const kustomizationsColumns: AnalyticalTableColumnDefinition[] = [
{
Header: t('FluxList.tableNameHeader'),
accessor: 'name',
},
{
Header: t('FluxList.tableStatusHeader'),
accessor: 'status',
Cell: (cellData: CellData<FluxRow['isReady']>) =>
cellData.cell.row.original?.isReady != null ? (
<ResourceStatusCell
value={cellData.cell.row.original?.isReady}
transitionTime={
cellData.cell.row.original?.statusUpdateTime
? cellData.cell.row.original?.statusUpdateTime
: ''
}
/>
) : null,
},
{
Header: t('FluxList.tableCreatedHeader'),
accessor: 'created',
},
];

const gitReposRows: FluxRow[] =
gitReposData?.items?.map((item) => {
return {
name: item.metadata.name,
isReady:
item.status.conditions.find((x) => x.type === 'Ready')?.status ===
'True',
statusUpdateTime: item.status.conditions.find((x) => x.type === 'Ready')
?.lastTransitionTime,
revision: shortenCommitHash(item.status.artifact.revision),
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
};
}) ?? [];

const kustomizationsRows: FluxRow[] =
kustmizationData?.items?.map((item) => {
return {
name: item.metadata.name,
isReady:
item.status.conditions.find((x) => x.type === 'Ready')?.status ===
'True',
statusUpdateTime: item.status.conditions.find((x) => x.type === 'Ready')
?.lastTransitionTime,
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
};
}) ?? [];

return (
<>
<Title level="H4">Git Repos</Title>
<ConfiguredAnalyticstable
columns={columns}
isLoading={repoIsLoading}
data={repoData ?? []}
/>
<Title level="H4">Kustomizations</Title>
<ConfiguredAnalyticstable
columns={columns}
isLoading={kustomizationIsLoading}
data={kustmizationData ?? []}
/>
{' '}
<div className="crossplane-table-element">
<Title level="H4">{t('FluxList.gitOpsTitle')}</Title>
<ConfiguredAnalyticstable
columns={gitReposColumns}
isLoading={repoIsLoading}
data={gitReposRows}
/>
</div>
<div className="crossplane-table-element">
<Title level="H4">{t('FluxList.kustomizationsTitle')}</Title>
<ConfiguredAnalyticstable
columns={kustomizationsColumns}
isLoading={kustomizationIsLoading}
data={kustomizationsRows}
/>
</div>
</>
);
}

function shortenCommitHash(commitHash: string): string {
//example hash: master@sha1:b3396adb98a6a0f5eeedd1a600beaf5e954a1f28
const match = commitHash.match(/^([a-zA-Z0-9-_]+)@sha1:([a-f0-9]{40})/);

if (match && match[2]) {
return `${match[1]}@${match[2].slice(0, 7)}`;
}

//example output : master@b3396ad
return commitHash;
}
30 changes: 28 additions & 2 deletions src/lib/api/types/flux/listGitRepo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import { Resource } from '../resource';

export const FluxGitRepo: Resource<any> = {
export type GitReposResponse = {
items: [
{
spec: {
package: string;
};
kind: string;
metadata: {
name: string;
creationTimestamp: string;
};
status: {
artifact: {
revision: string;
};
conditions: [
{
status: string;
type: string;
lastTransitionTime: string;
},
];
};
},
];
};

export const FluxRequest: Resource<GitReposResponse> = {
path: '/apis/source.toolkit.fluxcd.io/v1/gitrepositories',
jq: '[.items[]]',
};
30 changes: 28 additions & 2 deletions src/lib/api/types/flux/listKustomization.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import { Resource } from '../resource';

export const FluxKustomization: Resource<any> = {
export type KustomizationsResponse = {
items: [
{
spec: {
package: string;
};
kind: string;
metadata: {
name: string;
creationTimestamp: string;
};
status: {
artifact: {
revision: string;
};
conditions: [
{
status: string;
type: string;
lastTransitionTime: string;
},
];
};
},
];
};

export const FluxKustomization: Resource<KustomizationsResponse> = {
path: '/apis/kustomize.toolkit.fluxcd.io/v1/kustomizations',
jq: '[.items[]]',
};
Loading