Skip to content

Commit

Permalink
add Start time and Duration columnn in Admin perspectivr builds list …
Browse files Browse the repository at this point in the history
…page and show duration on details page whie build is running
  • Loading branch information
vikram-raj committed Jun 23, 2023
1 parent 338ce5b commit 652ec23
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 101 deletions.
59 changes: 31 additions & 28 deletions frontend/packages/shipwright-plugin/src/actions/useBuildActions.ts
Expand Up @@ -16,8 +16,9 @@ const useBuildActions = (build: Build) => {
const history = useHistory();
const [kindObj, inFlight] = useK8sModel(referenceFor(build));

const actions = React.useMemo<Action[]>(() => {
const start: Action = {
const actionsMenu = React.useMemo<Action[]>(() => {
const actions: Action[] = [];
actions.push({
id: 'shipwright-build-start',
label: t('shipwright-plugin~Start'),
cta: () => {
Expand All @@ -35,34 +36,36 @@ const useBuildActions = (build: Build) => {
resource: BuildRunModel.plural,
namespace: build.metadata?.namespace,
},
};
const startLastRun: Action = {
id: 'shipwright-build-start-last-run',
label: t('shipwright-plugin~Start last run'),
disabled: !build.latestBuild,
cta: () => {
rerunBuildRun(build.latestBuild)
.then((newBuildRun) => {
history.push(resourceObjPath(newBuildRun, referenceFor(newBuildRun)));
})
.catch((err) => {
const error = err.message;
errorModal({ error });
});
},
accessReview: {
verb: 'create',
group: BuildRunModel.apiGroup,
resource: BuildRunModel.plural,
namespace: build.metadata?.namespace,
},
};
return build.latestBuild
? [start, startLastRun, ...getCommonResourceActions(kindObj, build)]
: [start, ...getCommonResourceActions(kindObj, build)];
});

if (build.latestBuild) {
actions.push({
id: 'shipwright-build-start-last-run',
label: t('shipwright-plugin~Start last run'),
disabled: !build.latestBuild,
cta: () => {
rerunBuildRun(build.latestBuild)
.then((newBuildRun) => {
history.push(resourceObjPath(newBuildRun, referenceFor(newBuildRun)));
})
.catch((err) => {
const error = err.message;
errorModal({ error });
});
},
accessReview: {
verb: 'create',
group: BuildRunModel.apiGroup,
resource: BuildRunModel.plural,
namespace: build.metadata?.namespace,
},
});
}
actions.push(...getCommonResourceActions(kindObj, build));
return actions;
}, [t, build, kindObj, history]);

return [actions, !inFlight, undefined];
return [actionsMenu, !inFlight, undefined];
};

export default useBuildActions;
Expand Up @@ -99,8 +99,8 @@ export const BuildRow: React.FC<RowFunctionArgs<Build>> = ({ obj: build }) => {
{build.latestBuild ? (
<ResourceLink
kind={buildRunKindReference}
name={build?.latestBuild?.metadata?.name}
namespace={build?.latestBuild?.metadata?.namespace}
name={build.latestBuild.metadata?.name}
namespace={build.latestBuild.metadata?.namespace}
/>
) : (
'-'
Expand Down Expand Up @@ -147,7 +147,7 @@ export const BuildTable: React.FC<BuildTableProps> = (props) => {
isList: true,
});

const customData = React.useMemo<CustomData>(
const data = React.useMemo<CustomData>(
() => ({
buildRuns: {
latestByBuildName: buildRuns.reduce<Record<string, BuildRun>>((acc, buildRun) => {
Expand All @@ -168,9 +168,7 @@ export const BuildTable: React.FC<BuildTableProps> = (props) => {
);
const buildResource = props.data.map((sBuild) => {
sBuild.latestBuild =
customData.buildRuns.latestByBuildName[
`${sBuild.metadata.name}-${sBuild.metadata.namespace}`
];
data.buildRuns.latestByBuildName[`${sBuild.metadata.name}-${sBuild.metadata.namespace}`];
return sBuild;
});

Expand Down
Expand Up @@ -45,14 +45,10 @@ const BuildRunSection: React.FC<BuildRunSectionProps> = ({ buildRun }) => {
<Timestamp timestamp={buildRun.status?.completionTime} />
</DetailsItem>

{buildRun.status?.startTime && buildRun.status?.completionTime ? (
<>
<dt>{t('shipwright-plugin~Duration')}</dt>
<dd>
<BuildRunDuration buildRun={buildRun} />
</dd>
</>
) : null}
<>
<dt>{t('shipwright-plugin~Duration')}</dt>
<dd>{buildRun.status?.startTime ? <BuildRunDuration buildRun={buildRun} /> : '-'}</dd>
</>
</dl>
);
};
Expand Down
53 changes: 8 additions & 45 deletions frontend/public/components/build-config.tsx
Expand Up @@ -38,7 +38,7 @@ import { BuildConfigModel, BuildModel } from '../models';
import Helmet from 'react-helmet';
import { useK8sWatchResource } from './utils/k8s-watch-hook';
import { Status } from '@console/shared';
import i18next from 'i18next';
import { displayDurationInWords } from './utils/build-utils';

const BuildConfigsReference: K8sResourceKindReference = 'BuildConfig';
const BuildsReference: K8sResourceKindReference = 'Build';
Expand All @@ -65,8 +65,6 @@ const startBuildAction: KebabAction = (kind, buildConfig) => ({
},
});

const isLastbuildPresent = (build) => (build ? true : false);

const startLastBuildAction: KebabAction = (kind, buildConfig: BuildConfig) => {
return {
// t('public~Start last run')
Expand All @@ -80,7 +78,7 @@ const startLastBuildAction: KebabAction = (kind, buildConfig: BuildConfig) => {
const error = err.message;
errorModal({ error });
}),
hidden: !isLastbuildPresent(buildConfig.latestBuild),
hidden: !!buildConfig.latestBuild,
accessReview: {
group: kind.apiGroup,
resource: kind.plural,
Expand Down Expand Up @@ -153,38 +151,6 @@ const tableColumnClasses = [
Kebab.columnClass,
];

const displayDurationInWords = (start: string, stop: string): string => {
if (!start) {
return '-';
}
const startTime = new Date(start).getTime();
const stopTime = stop ? new Date(stop).getTime() : new Date().getTime();
let duration = (stopTime - startTime) / 1000;
const time = [];
let durationInWords = '';
while (duration >= 60) {
time.push(duration % 60);
duration = Math.floor(duration / 60);
}
time.push(duration);
if (time[2]) {
durationInWords += `${time[2]} ${
time[2] > 1 ? i18next.t('public~hours') : i18next.t('public~hour')
} `;
}
if (time[1]) {
durationInWords += `${time[1]} ${
time[1] > 1 ? i18next.t('public~minutes') : i18next.t('public~minute')
} `;
}
if (time[0]) {
durationInWords += `${time[0]} ${
time[0] > 1 ? i18next.t('public~seconds') : i18next.t('public~second')
} `;
}
return durationInWords.trim();
};

const BuildConfigsTableRow: React.FC<RowFunctionArgs<BuildConfig>> = ({ obj }) => {
const latestBuild = obj?.latestBuild;

Expand Down Expand Up @@ -243,11 +209,8 @@ const isBuildNewerThen = (newBuild: K8sResourceKind, prevBuild: K8sResourceKind
const buildStrategy = (buildConfig: K8sResourceKind): BuildStrategyType =>
buildConfig.spec.strategy.type;

const buildStatus = (buildConfig: BuildConfig) => {
if (buildConfig.latestBuild) {
return buildConfig.latestBuild?.status?.phase;
}
return 'Unknown';
const getBuildStatus = (buildConfig: BuildConfig) => {
return buildConfig?.latestBuild?.status?.phase || 'Unknown';
};

export const BuildConfigsList: React.SFC<BuildConfigsListProps> = (props) => {
Expand Down Expand Up @@ -305,7 +268,7 @@ export const BuildConfigsList: React.SFC<BuildConfigsListProps> = (props) => {
namespace: props.namespace,
isList: true,
});
const customData = React.useMemo<CustomData>(
const data = React.useMemo<CustomData>(
() => ({
builds: {
latestByBuildName: builds.reduce<Record<string, K8sResourceKind>>((acc, build) => {
Expand All @@ -327,7 +290,7 @@ export const BuildConfigsList: React.SFC<BuildConfigsListProps> = (props) => {

const buildResource = props.data.map((buildConfig) => {
buildConfig.latestBuild =
customData.builds.latestByBuildName[
data.builds.latestByBuildName[
`${buildConfig.metadata.name}-${buildConfig.metadata.namespace}`
];
return buildConfig;
Expand Down Expand Up @@ -383,9 +346,9 @@ export const BuildConfigsPage: React.FC<BuildConfigsPageProps> = (props) => {
items: allStrategies,
},
{
filterGroupName: t('public~BuildRun status'),
filterGroupName: t('public~Build status'),
type: 'build-run-status',
reducer: buildStatus,
reducer: getBuildStatus,
items: statusFilters,
filter: (filterValue, build: BuildConfig): boolean => {
const status = build?.latestBuild?.status?.phase ?? 'Unknown';
Expand Down
24 changes: 18 additions & 6 deletions frontend/public/components/build.tsx
Expand Up @@ -24,7 +24,7 @@ import {
k8sPatch,
K8sKind,
} from '../module/k8s';
import { cloneBuild, formatBuildDuration, getBuildNumber } from '../module/k8s/builds';
import { cloneBuild, getBuildNumber } from '../module/k8s/builds';
import { DetailsPage, ListPage, Table, TableData, RowFunctionArgs } from './factory';
import { errorModal, confirmModal } from './modals';
import {
Expand Down Expand Up @@ -54,6 +54,7 @@ import { Area } from './graphs';
import { BuildConfigModel, BuildModel } from '../models';
import { timeFormatter, timeFormatterWithSeconds } from './utils/datetime';
import Dashboard from '@console/shared/src/components/dashboard/Dashboard';
import { displayDurationInWords } from './utils/build-utils';

const BuildsReference: K8sResourceKindReference = 'Build';

Expand Down Expand Up @@ -253,7 +254,6 @@ export const PipelineBuildStrategyAlert: React.FC<BuildsDetailsProps> = () => {
export const BuildsDetails: React.SFC<BuildsDetailsProps> = ({ obj: build }) => {
const { logSnippet, message, startTimestamp, completionTimestamp } = build.status;
const triggeredBy = _.map(build.spec.triggeredBy, 'message').join(', ');
const duration = formatBuildDuration(build);
const hasPipeline = build.spec.strategy.type === BuildStrategyType.JenkinsPipeline;
const { t } = useTranslation();
const BUILDCONFIG_TO_BUILD_REFERENCE_LABEL = 'openshift.io/build-config.name';
Expand Down Expand Up @@ -316,7 +316,10 @@ export const BuildsDetails: React.SFC<BuildsDetailsProps> = ({ obj: build }) =>
<Timestamp timestamp={completionTimestamp} />
</DetailsItem>
<DetailsItem label={t('public~Duration')} obj={build} path="status.duration">
{duration}
{displayDurationInWords(
build?.status?.startTimestamp,
build?.status?.completionTimestamp,
)}
</DetailsItem>
<DetailsItem label={t('public~Message')} obj={build} path="status.message" hideEmpty>
{message}
Expand Down Expand Up @@ -442,7 +445,10 @@ const BuildsTableRow: React.FC<RowFunctionArgs<K8sResourceKind>> = ({ obj }) =>
<Status status={obj.status?.phase} />
</TableData>
<TableData className={tableColumnClasses[3]}>
<Timestamp timestamp={obj.metadata.creationTimestamp} />
<Timestamp timestamp={obj.status?.startTimestamp} />
</TableData>
<TableData className={tableColumnClasses[3]}>
{displayDurationInWords(obj.status?.startTimestamp, obj.status?.completionTimestamp)}
</TableData>
<TableData className={tableColumnClasses[4]}>
<ResourceKebab actions={menuActions} kind={BuildsReference} resource={obj} />
Expand Down Expand Up @@ -475,8 +481,14 @@ export const BuildsList: React.SFC = (props) => {
props: { className: tableColumnClasses[2] },
},
{
title: t('public~Created'),
sortField: 'metadata.creationTimestamp',
title: t('public~Start time'),
sortField: 'status.startTimestamp',
transforms: [sortable],
props: { className: tableColumnClasses[3] },
},
{
title: t('public~Duration'),
sortField: 'status.duration',
transforms: [sortable],
props: { className: tableColumnClasses[3] },
},
Expand Down
33 changes: 33 additions & 0 deletions frontend/public/components/utils/build-utils.ts
@@ -0,0 +1,33 @@
import i18next from 'i18next';

export const displayDurationInWords = (start: string, stop: string): string => {
if (!start) {
return '-';
}
const startTime = new Date(start).getTime();
const stopTime = stop ? new Date(stop).getTime() : new Date().getTime();
let duration = Math.round((stopTime - startTime) / 1000);
const time = [];
let durationInWords = '';
while (duration >= 60) {
time.push(duration % 60);
duration = Math.floor(duration / 60);
}
time.push(duration);
if (time[2]) {
durationInWords += `${time[2]} ${
time[2] > 1 ? i18next.t('public~hours') : i18next.t('public~hour')
} `;
}
if (time[1]) {
durationInWords += `${time[1]} ${
time[1] > 1 ? i18next.t('public~minutes') : i18next.t('public~minute')
} `;
}
if (time[0]) {
durationInWords += `${time[0]} ${
time[0] > 1 ? i18next.t('public~seconds') : i18next.t('public~second')
} `;
}
return durationInWords.trim();
};
16 changes: 8 additions & 8 deletions frontend/public/locales/en/public.json
Expand Up @@ -59,12 +59,6 @@
"Start build": "Start build",
"Start last run": "Start last run",
"BuildConfig details": "BuildConfig details",
"hours": "hours",
"hour": "hour",
"minutes": "minutes",
"minute": "minute",
"seconds": "seconds",
"second": "second",
"Last run": "Last run",
"Last run status": "Last run status",
"Last run time": "Last run time",
Expand All @@ -84,7 +78,7 @@
"Cancelled": "Cancelled",
"Unknown": "Unknown",
"Build strategy": "Build strategy",
"BuildRun status": "BuildRun status",
"Build status": "Build status",
"Pipeline build logs are available through Jenkins (linked below)": "Pipeline build logs are available through Jenkins (linked below)",
"A link to the Jenkins pipeline build logs will appear below when the build starts": "A link to the Jenkins pipeline build logs will appear below when the build starts",
"See Jenkins log": "See Jenkins log",
Expand Down Expand Up @@ -114,7 +108,6 @@
"Message": "Message",
"Log snippet": "Log snippet",
"The environment variable editor does not support build strategy: {{ type }}": "The environment variable editor does not support build strategy: {{ type }}",
"Created": "Created",
"Builds": "Builds",
"Icon": "Icon",
"Values for certificate and key should both be either excluded or provided.": "Values for certificate and key should both be either excluded or provided.",
Expand Down Expand Up @@ -311,6 +304,7 @@
"Hide values": "Hide values",
"Reveal values": "Reveal values",
"Size": "Size",
"Created": "Created",
"ConfigMaps": "ConfigMaps",
"ConfigMap details": "ConfigMap details",
"Binary data": "Binary data",
Expand Down Expand Up @@ -1708,6 +1702,12 @@
"Output to": "Output to",
"Push secret": "Push secret",
"Run policy": "Run policy",
"hours": "hours",
"hour": "hour",
"minutes": "minutes",
"minute": "minute",
"seconds": "seconds",
"second": "second",
"Copied": "Copied",
"Copy to clipboard": "Copy to clipboard",
"Just now": "Just now",
Expand Down

0 comments on commit 652ec23

Please sign in to comment.