Skip to content

Commit

Permalink
Fetching pipelinerun either from k8s or from tekton results
Browse files Browse the repository at this point in the history
  • Loading branch information
lprabhu committed Nov 13, 2023
1 parent 84d49e7 commit dd015e3
Show file tree
Hide file tree
Showing 14 changed files with 912 additions and 230 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export type MatchLabels = {
export type Selector = {
matchLabels?: MatchLabels;
matchExpressions?: MatchExpression[];
filterByName?: string;
};

type K8sVerb =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,8 @@
"TektonConfigs": "TektonConfigs",
"TektonHub": "TektonHub",
"TektonHubs": "TektonHubs",
"TektonResult": "TektonResult",
"TektonResults": "TektonResults",
"Pipeline {{status}}": "Pipeline {{status}}",
"Pipeline status is {{status}}. View logs.": "Pipeline status is {{status}}. View logs.",
"Pipeline not started. Start pipeline.": "Pipeline not started. Start pipeline.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { useTranslation } from 'react-i18next';
import { DetailsPage, DetailsPageProps } from '@console/internal/components/factory';
import { KebabAction, navFactory, viewYamlComponent } from '@console/internal/components/utils';
import * as SignedPipelinerunIcon from '../../images/signed-badge.svg';
import { useK8sWatchResource } from '@console/dynamic-plugin-sdk/src/utils/k8s/hooks/useK8sWatchResource';
import { FirehoseResult, K8sResourceKind, referenceForModel } from '@console/internal/module/k8s';
import { PipelineRunModel } from '../../models';
import { PipelineRunKind } from '../../types';
import { usePipelineTechPreviewBadge } from '../../utils/hooks';
import { getPipelineRunKebabActions } from '../../utils/pipeline-actions';
Expand All @@ -16,14 +19,17 @@ import { PipelineRunDetails } from './detail-page-tabs/PipelineRunDetails';
import { PipelineRunLogsWithActiveTask } from './detail-page-tabs/PipelineRunLogs';
import TaskRuns from './detail-page-tabs/TaskRuns';
import PipelineRunEvents from './events/PipelineRunEvents';
import { useTRPipelineRuns } from './hooks/useTektonResults';
import PipelineRunParametersForm from './PipelineRunParametersForm';
import { useMenuActionsWithUserAnnotation } from './triggered-by';

const PipelineRunDetailsPage: React.FC<DetailsPageProps> = (props) => {
const { kindObj, match } = props;
const { kindObj, match, namespace, name } = props;
const { t } = useTranslation();
const operatorVersion = usePipelineOperatorVersion(props.namespace);
const [taskRuns] = useTaskRuns(props.namespace);
const [data, setData] = React.useState<FirehoseResult<K8sResourceKind> | PipelineRunKind>();
const [loaded, setLoaded] = React.useState(false);
const operatorVersion = usePipelineOperatorVersion(namespace);
const [taskRuns] = useTaskRuns(namespace);
const menuActions: KebabAction[] = useMenuActionsWithUserAnnotation(
getPipelineRunKebabActions(operatorVersion, taskRuns, true),
);
Expand All @@ -40,9 +46,30 @@ const PipelineRunDetailsPage: React.FC<DetailsPageProps> = (props) => {
) : (
obj?.metadata?.name
);

const [pipelineRun, pipelineRunLoaded] = useK8sWatchResource<FirehoseResult<K8sResourceKind>>({
kind: referenceForModel(PipelineRunModel),
namespace,
name,
isList: false,
});
const [TRPlrs, TRPlrsLoaded] = useTRPipelineRuns(namespace, {
selector: { filterByName: name },
});

React.useEffect(() => {
setData(pipelineRun || TRPlrs[0]);
setLoaded(pipelineRunLoaded || TRPlrsLoaded);
}, [TRPlrs, pipelineRun, pipelineRunLoaded, TRPlrsLoaded]);

return (
<DetailsPage
{...props}
obj={{
data,
loaded,
loadError: undefined,
}}
badge={badge}
menuActions={menuActions}
getResourceStatus={pipelineRunStatus}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { SemVer } from 'semver';
import { useK8sWatchResource } from '@console/dynamic-plugin-sdk/src/utils/k8s/hooks/useK8sWatchResource';
import { DetailsPage } from '@console/internal/components/factory';
import { referenceForModel } from '@console/internal/module/k8s';
import { PipelineRunModel } from '../../../models';
Expand All @@ -19,6 +20,10 @@ type PipelineRunDetailsPageProps = React.ComponentProps<typeof PipelineRunDetail
const i18nNS = 'public';
const i18nPipelineNS = 'pipelines-plugin';

jest.mock('@console/dynamic-plugin-sdk/src/utils/k8s/hooks/useK8sWatchResource', () => ({
useK8sWatchResource: jest.fn(),
}));

describe('PipelineRunDetailsPage:', () => {
let pipelineRunDetailsPageProps: PipelineRunDetailsPageProps;
let wrapper: ShallowWrapper<PipelineRunDetailsPageProps>;
Expand All @@ -38,6 +43,7 @@ describe('PipelineRunDetailsPage:', () => {
menuActions.mockReturnValue([getPipelineRunKebabActions(new SemVer('1.9.0'), [], true)]);
breadCrumbs.mockReturnValue([{ label: 'PipelineRuns' }, { label: 'PipelineRuns Details' }]);
taskRuns.mockReturnValue([]);
(useK8sWatchResource as jest.Mock).mockReturnValue([[], true]);
wrapper = shallow(<PipelineRunDetailsPage {...pipelineRunDetailsPageProps} />);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { TFunction } from 'i18next';
import * as _ from 'lodash';
import { withTranslation } from 'react-i18next';
import { RouteComponentProps } from 'react-router';
import { Link } from 'react-router-dom';
import { Firehose, resourcePathFromModel } from '@console/internal/components/utils';
import { PipelineRunModel } from '../../../models';
import { PipelineRunKind, TaskRunKind } from '../../../types';
import { pipelineRunFilterReducer } from '../../../utils/pipeline-filter-reducer';
import { WatchK8sResource } from '@console/dynamic-plugin-sdk';
import { ComputedStatus, PipelineRunKind, PipelineTask, TaskRunKind } from '../../../types';
import { pipelineRunStatus } from '../../../utils/pipeline-filter-reducer';
import { taskRunStatus } from '../../../utils/pipeline-utils';
import { TektonResourceLabel } from '../../pipelines/const';
import { ColoredStatusIcon } from '../../pipelines/detail-page-tabs/pipeline-details/StatusIcon';
import { useTaskRuns } from '../../taskruns/useTaskRuns';
import { useTRTaskRuns } from '../hooks/useTektonResults';
import { ErrorDetailsWithStaticLog } from '../logs/log-snippet-types';
import { getDownloadAllLogsCallback } from '../logs/logs-utils';
import LogsWrapperComponent from '../logs/LogsWrapperComponent';
Expand All @@ -38,8 +38,11 @@ class PipelineRunLogsWithTranslation extends React.Component<
}

componentDidMount() {
const { activeTask, taskRuns } = this.props;
const sortedTaskRuns = this.getSortedTaskRun(taskRuns);
const { activeTask, taskRuns, obj } = this.props;
const sortedTaskRuns = this.getSortedTaskRun(taskRuns, [
...(obj?.status?.pipelineSpec?.tasks || []),
...(obj?.status?.pipelineSpec?.finally || []),
]);
const activeItem = this.getActiveTaskRun(sortedTaskRuns, activeTask);
this.setState({ activeItem });
}
Expand All @@ -48,18 +51,25 @@ class PipelineRunLogsWithTranslation extends React.Component<
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.obj !== nextProps.obj || this.props.taskRuns !== nextProps.taskRuns) {
const { activeTask, taskRuns } = this.props;
const sortedTaskRuns = this.getSortedTaskRun(taskRuns);
const sortedTaskRuns = this.getSortedTaskRun(taskRuns, [
...(this.props?.obj?.status?.pipelineSpec?.tasks || []),
...(this.props?.obj?.status?.pipelineSpec?.finally || []),
]);
const activeItem = this.getActiveTaskRun(sortedTaskRuns, activeTask);
this.state.navUntouched && this.setState({ activeItem });
}
}

getActiveTaskRun = (taskRuns: string[], activeTask: string): string =>
activeTask
? taskRuns.find((taskRun) => taskRun.includes(activeTask))
: taskRuns[taskRuns.length - 1];
getActiveTaskRun = (taskRuns: TaskRunKind[], activeTask: string): string => {
const activeTaskRun = activeTask
? taskRuns.find((taskRun) => taskRun.metadata.name.includes(activeTask))
: taskRuns.find((taskRun) => taskRunStatus(taskRun) === ComputedStatus.Failed) ||
taskRuns[taskRuns.length - 1];

getSortedTaskRun = (tRuns: TaskRunKind[]): string[] => {
return activeTaskRun?.metadata.name;
};

getSortedTaskRun = (tRuns: TaskRunKind[], tasks: PipelineTask[]): TaskRunKind[] => {
const taskRuns = tRuns?.sort((a, b) => {
if (_.get(a, ['status', 'completionTime'], false)) {
return b.status?.completionTime &&
Expand All @@ -72,7 +82,15 @@ class PipelineRunLogsWithTranslation extends React.Component<
? 1
: -1;
});
return taskRuns?.map((tr) => tr?.metadata?.name) || [];

const pipelineTaskNames = tasks?.map((t) => t?.name);
return (
taskRuns?.sort(
(c, d) =>
pipelineTaskNames?.indexOf(c?.metadata?.labels?.[TektonResourceLabel.pipelineTask]) -
pipelineTaskNames?.indexOf(d?.metadata?.labels?.[TektonResourceLabel.pipelineTask]),
) || []
);
};

onNavSelect = (item) => {
Expand All @@ -85,76 +103,62 @@ class PipelineRunLogsWithTranslation extends React.Component<
render() {
const { obj, t, taskRuns: tRuns } = this.props;
const { activeItem } = this.state;
const taskRuns = this.getSortedTaskRun(tRuns);
const taskRunFromYaml = tRuns?.reduce((acc, value) => {
acc[value?.metadata?.name] = value;
return acc;
}, {});
const logDetails = getPLRLogSnippet(obj, tRuns) as ErrorDetailsWithStaticLog;
const taskRunNames = this.getSortedTaskRun(tRuns, [
...(obj?.status?.pipelineSpec?.tasks || []),
...(obj?.status?.pipelineSpec?.finally || []),
])?.map((tRun) => tRun.metadata.name);

const taskCount = taskRuns.length;
const logDetails = getPLRLogSnippet(obj, tRuns) as ErrorDetailsWithStaticLog;
const pipelineStatus = pipelineRunStatus(obj);
const taskCount = taskRunNames.length;
const downloadAllCallback =
taskCount > 1
? getDownloadAllLogsCallback(
taskRuns,
taskRunFromYaml,
taskRunNames,
tRuns,
obj.metadata?.namespace,
obj.metadata?.name,
)
: undefined;
const podName = taskRunFromYaml?.[activeItem]?.status?.podName;
const taskName =
taskRunFromYaml?.[activeItem]?.metadata?.labels?.[TektonResourceLabel.pipelineTask] || '-';
const resources = taskCount > 0 &&
podName && [
{
name: podName,
kind: 'Pod',
namespace: obj.metadata.namespace,
prop: `obj`,
isList: false,
},
];
const path = `${resourcePathFromModel(
PipelineRunModel,
obj.metadata.name,
obj.metadata.namespace,
)}/logs/`;
const activeTaskRun = tRuns.find((taskRun) => taskRun.metadata.name === activeItem);
const podName = activeTaskRun?.status?.podName;
const taskName = activeTaskRun?.metadata?.labels?.[TektonResourceLabel.pipelineTask] || '-';
const pipelineRunFinished = pipelineStatus !== ComputedStatus.Running;
const resources: WatchK8sResource = taskCount > 0 &&
podName && {
name: podName,
kind: 'Pod',
namespace: obj.metadata.namespace,
isList: false,
};
const waitingForPods = !!(activeItem && !resources);

const selectedItemRef = (item: HTMLSpanElement) => {
if (item?.scrollIntoView) {
item.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
};
return (
<div className="odc-pipeline-run-logs">
<div className="odc-pipeline-run-logs__tasklist" data-test-id="logs-tasklist">
{taskCount > 0 ? (
<Nav onSelect={this.onNavSelect} theme="light">
<NavList className="odc-pipeline-run-logs__nav">
{taskRuns.map((task) => {
{taskRunNames.map((taskRunName) => {
const taskRun = tRuns.find((tRun) => tRun.metadata.name === taskRunName);
return (
<NavItem
key={task}
itemId={task}
isActive={activeItem === task}
key={taskRunName}
itemId={taskRunName}
isActive={activeItem === taskRunName}
className="odc-pipeline-run-logs__navitem"
>
<Link
to={
path +
taskRunFromYaml?.[task]?.metadata?.labels?.[
TektonResourceLabel.pipelineTask
] || '-'
}
>
<ColoredStatusIcon
status={pipelineRunFilterReducer(
obj?.status?.taskRuns
? _.get(obj, ['status', 'taskRuns', task])
: tRuns?.find((tr) => tr?.metadata?.name === task),
)}
/>
<span ref={activeItem === taskRunName ? selectedItemRef : undefined}>
<ColoredStatusIcon status={taskRunStatus(taskRun)} />
<span className="odc-pipeline-run-logs__namespan">
{taskRunFromYaml[task]?.metadata?.labels?.[
TektonResourceLabel.pipelineTask
] || '-'}
{taskRun?.metadata?.labels?.[TektonResourceLabel.pipelineTask] || '-'}
</span>
</Link>
</span>
</NavItem>
);
})}
Expand All @@ -168,21 +172,21 @@ class PipelineRunLogsWithTranslation extends React.Component<
</div>
<div className="odc-pipeline-run-logs__container">
{activeItem && resources ? (
<Firehose key={activeItem} resources={resources}>
<LogsWrapperComponent
taskName={taskName}
downloadAllLabel={t('pipelines-plugin~Download all task logs')}
onDownloadAll={downloadAllCallback}
/>
</Firehose>
<LogsWrapperComponent
resource={resources}
taskName={taskName}
downloadAllLabel={t('pipelines-plugin~Download all task logs')}
onDownloadAll={downloadAllCallback}
taskRun={activeTaskRun}
/>
) : (
<div className="odc-pipeline-run-logs__log">
<div className="odc-pipeline-run-logs__logtext">
{_.get(
obj,
['status', 'conditions', 0, 'message'],
t('pipelines-plugin~No logs found'),
)}
<div className="odc-pipeline-run-logs__logtext" data-test-id="task-logs-error">
{waitingForPods && !pipelineRunFinished && `Waiting for ${taskName} task to start `}
{!resources &&
pipelineRunFinished &&
!obj.status &&
t('pipelines-plugin~No logs found')}
{logDetails && (
<div className="odc-pipeline-run-logs__logsnippet">
{logDetails.staticMessage}
Expand All @@ -208,11 +212,22 @@ export const PipelineRunLogsWithActiveTask: React.FC<PipelineRunLogsWithActiveTa
obj,
params,
}) => {
const [data, setData] = React.useState<TaskRunKind[]>();
const [loaded, setLoaded] = React.useState(false);
const activeTask = _.get(params, 'match.params.name');
const [taskRuns, taskRunsLoaded] = useTaskRuns(obj?.metadata?.namespace, obj?.metadata?.name);
return (
taskRunsLoaded && <PipelineRunLogs obj={obj} activeTask={activeTask} taskRuns={taskRuns} />
);
const [resultTaskRuns, resultTaskRunsLoaded] = useTRTaskRuns(obj?.metadata?.namespace, {
selector: {
matchLabels: {
[TektonResourceLabel.pipelinerun]: obj?.metadata?.name,
},
},
});
React.useEffect(() => {
setData(taskRuns || resultTaskRuns);
setLoaded(taskRunsLoaded || resultTaskRunsLoaded);
}, [taskRuns, resultTaskRuns, taskRunsLoaded, resultTaskRunsLoaded]);
return loaded && <PipelineRunLogs obj={obj} activeTask={activeTask} taskRuns={data} />;
};

export default PipelineRunLogs;

0 comments on commit dd015e3

Please sign in to comment.