Skip to content

Commit

Permalink
Support Embedded Task/Pipeline/Pipeline Resources
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewballantyne committed Apr 12, 2021
1 parent c3db063 commit 97d95e8
Show file tree
Hide file tree
Showing 21 changed files with 299 additions and 180 deletions.
Expand Up @@ -16,6 +16,7 @@
"Type": "Type",
"Created": "Created",
"Status": "Status",
"Pipeline Resources": "Pipeline Resources",
"Pipeline Run details": "Pipeline Run details",
"No task runs found": "No task runs found",
"Download all task logs": "Download all task logs",
Expand Down Expand Up @@ -44,7 +45,6 @@
"Pipeline Resource": "Pipeline Resource",
"Condition": "Condition",
"Pipeline Runs": "Pipeline Runs",
"Pipeline Resources": "Pipeline Resources",
"Conditions": "Conditions",
"Event Listener details": "Event Listener details",
"Triggers": "Triggers",
Expand All @@ -71,6 +71,9 @@
"No workspaces are associated with this pipeline.": "No workspaces are associated with this pipeline.",
"{{triggerBindingLabel}} details": "{{triggerBindingLabel}} details",
"Trigger Template details": "Trigger Template details",
"Embedded Task": "Embedded Task",
"Embedded Pipeline Resource": "Embedded Pipeline Resource",
"Embedded Pipeline": "Embedded Pipeline",
"Last run": "Last run",
"Last run status": "Last run status",
"Last run time": "Last run time",
Expand Down
@@ -1,30 +1,31 @@
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { ResourceLink } from '@console/internal/components/utils';
import { referenceForModel } from '@console/internal/module/k8s';
import { Status } from '@console/shared';
import { PipelineRunKind, PipelineRunReferenceResource } from '../../../types';
import { PipelineRunKind } from '../../../types';
import { pipelineRunFilterReducer } from '../../../utils/pipeline-filter-reducer';
import { pipelineRefExists } from '../../../utils/pipeline-augment';
import { PipelineModel, PipelineResourceModel } from '../../../models';
import ResourceLinkList from '../../pipelines/resource-overview/ResourceLinkList';
import DynamicResourceLinkList from '../../pipelines/resource-overview/DynamicResourceLinkList';
import {
convertBackingPipelineToPipelineResourceRefProps,
getPipelineResourceLinks,
} from '../../pipelines/detail-page-tabs';
import WorkspaceResourceLinkList from '../../shared/workspaces/WorkspaceResourceLinkList';
import RunDetailsErrorLog from '../logs/RunDetailsErrorLog';
import TriggeredBySection from './TriggeredBySection';
import { getPLRLogSnippet } from '../logs/pipelineRunLogSnippet';
import WorkspaceResourceLinkList from '../../shared/workspaces/WorkspaceResourceLinkList';
import PipelineResourceRef from '../../shared/common/PipelineResourceRef';

export type PipelineRunCustomDetailsProps = {
pipelineRun: PipelineRunKind;
};

const PipelineRunCustomDetails: React.FC<PipelineRunCustomDetailsProps> = ({ pipelineRun }) => {
const { t } = useTranslation();
// FIXME: If they are inline resources, we are not going to render them
const unfilteredResources = pipelineRun.spec.resources as PipelineRunReferenceResource[];
const renderResources =
unfilteredResources
?.filter(({ resourceRef }) => !!resourceRef)
.map((resource) => resource.resourceRef.name) || [];
const pipelineResourceLinks = getPipelineResourceLinks(
pipelineRun.status?.pipelineSpec?.resources,
pipelineRun.spec.resources,
t,
);

return (
<>
<dl>
Expand All @@ -40,22 +41,18 @@ const PipelineRunCustomDetails: React.FC<PipelineRunCustomDetailsProps> = ({ pip
logDetails={getPLRLogSnippet(pipelineRun, t)}
namespace={pipelineRun.metadata.namespace}
/>
{pipelineRefExists(pipelineRun) && (
<dl>
<dt>{t('pipelines-plugin~Pipeline')}</dt>
<dd>
<ResourceLink
kind={referenceForModel(PipelineModel)}
name={pipelineRun.spec.pipelineRef.name}
namespace={pipelineRun.metadata.namespace}
/>
</dd>
</dl>
)}
<dl>
<dt>{t('pipelines-plugin~Pipeline')}</dt>
<dd>
<PipelineResourceRef
{...convertBackingPipelineToPipelineResourceRefProps(pipelineRun, t)}
/>
</dd>
</dl>
<TriggeredBySection pipelineRun={pipelineRun} />
<ResourceLinkList
model={PipelineResourceModel}
links={renderResources}
<DynamicResourceLinkList
links={pipelineResourceLinks}
title={t('pipelines-plugin~Pipeline Resources')}
namespace={pipelineRun.metadata.namespace}
/>
<WorkspaceResourceLinkList
Expand Down
Expand Up @@ -4,6 +4,8 @@ import { TektonParam } from '../../../../types';
import { getPipelineTaskLinks, removeEmptyDefaultFromPipelineParams } from '../utils';
import { PipelineExampleNames, pipelineTestData } from '../../../../test-data/pipeline-data';

const t = jest.fn((v) => v);

describe('removeEmptyDefaultFromPipelineParams omits empty default values', () => {
it('should return pipline parameters by only omitting empty default values', () => {
const result = removeEmptyDefaultFromPipelineParams(pipelineParameters);
Expand Down Expand Up @@ -68,30 +70,22 @@ describe('getPipelineTaskLinks', () => {
},
],
};
it('should return empty arrays for links if there are no regular tasks with taskRef and no finally tasks', () => {
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks({
...simplePipeline,
spec: { ...taskWithoutTaskRef },
});
expect(taskLinks).toHaveLength(0);
expect(finallyTaskLinks).toHaveLength(0);
});
it('should return links for only regular tasks if there are regular tasks with taskRef but no finally tasks', () => {
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks(simplePipeline);
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks(simplePipeline, t);
expect(taskLinks).toHaveLength(2);
expect(finallyTaskLinks).toHaveLength(0);
});
it('should return links for only finally tasks if there are finally tasks but no regular tasks with taskRef', () => {
it('should return links for finally tasks if there are finally tasks and no regular tasks with taskRef', () => {
const pipelineWithoutTaskRef = {
...pipelineWithFinally,
spec: { ...pipelineWithFinally.spec, tasks: taskWithoutTaskRef.tasks },
};
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks(pipelineWithoutTaskRef);
expect(taskLinks).toHaveLength(0);
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks(pipelineWithoutTaskRef, t);
expect(taskLinks).toHaveLength(1);
expect(finallyTaskLinks).toHaveLength(1);
});
it('should return links for both regular tasks and finally tasks if both are present', () => {
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks(pipelineWithFinally);
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks(pipelineWithFinally, t);
expect(taskLinks).toHaveLength(2);
expect(finallyTaskLinks).toHaveLength(1);
});
Expand Down
Expand Up @@ -11,7 +11,7 @@ import { PipelineDetailsTabProps } from '../types';

const PipelineDetails: React.FC<PipelineDetailsTabProps> = ({ obj: pipeline, customData }) => {
const { t } = useTranslation();
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks(pipeline);
const { taskLinks, finallyTaskLinks } = getPipelineTaskLinks(pipeline, t);

return (
<div className="co-m-pane__body">
Expand Down
@@ -1,7 +1,18 @@
import * as _ from 'lodash';
import { getResourceModelFromTaskKind } from '../../../utils/pipeline-augment';
import { PipelineKind, PipelineTask, TektonParam } from '../../../types';
import { TFunction } from 'i18next';
import {
PipelineKind,
PipelineRunKind,
PipelineRunReferenceResource,
PipelineRunResource,
PipelineTask,
TektonParam,
TektonResource,
} from '../../../types';
import { getSafeTaskResourceKind } from '../../../utils/pipeline-augment';
import { ResourceModelLink } from '../resource-overview/DynamicResourceLinkList';
import { PipelineModel, PipelineResourceModel } from '../../../models';
import PipelineResourceRef from '../../shared/common/PipelineResourceRef';

export const removeEmptyDefaultFromPipelineParams = (parameters: TektonParam[]): TektonParam[] =>
_.map(
Expand All @@ -15,19 +26,73 @@ type PipelineTaskLinks = {
finallyTaskLinks: ResourceModelLink[];
};

export const getPipelineTaskLinks = (pipeline: PipelineKind): PipelineTaskLinks => {
export const getPipelineTaskLinks = (pipeline: PipelineKind, t: TFunction): PipelineTaskLinks => {
const toResourceLinkData = (tasks: PipelineTask[]): ResourceModelLink[] => {
if (!tasks) return [];
return tasks
?.filter((pipelineTask) => !!pipelineTask.taskRef)
?.map((task) => ({
model: getResourceModelFromTaskKind(task.taskRef.kind),
name: task.taskRef.name,
displayName: task.name,
}));
return tasks?.map((task) =>
task.taskRef
? {
resourceKind: getSafeTaskResourceKind(task.taskRef.kind),
name: task.taskRef.name,
qualifier: task.name,
}
: {
resourceKind: 'EmbeddedTask',
name: t('pipelines-plugin~Embedded Task'),
qualifier: task.name,
},
);
};
return {
taskLinks: toResourceLinkData(pipeline.spec.tasks),
finallyTaskLinks: toResourceLinkData(pipeline.spec.finally),
};
};

const isResourceRef = (resource: PipelineRunResource): resource is PipelineRunReferenceResource =>
!!(resource as PipelineRunReferenceResource).resourceRef;

export const getPipelineResourceLinks = (
definitionResources: TektonResource[] = [],
runResources: PipelineRunResource[],
t: TFunction,
): ResourceModelLink[] => {
return runResources?.map(
(resource): ResourceModelLink => {
const definitionResource = definitionResources.find(({ name }) => name === resource.name);
const qualifier = definitionResource ? definitionResource.type : undefined;

if (isResourceRef(resource)) {
return {
resourceKind: PipelineResourceModel.kind,
name: resource.resourceRef.name,
qualifier,
};
}

return {
resourceKind: 'EmbeddedPipelineResource',
name: t('pipelines-plugin~Embedded Pipeline Resource'),
qualifier,
};
},
);
};

export const convertBackingPipelineToPipelineResourceRefProps = (
pipelineRun: PipelineRunKind,
t: TFunction,
): React.ComponentProps<typeof PipelineResourceRef> => {
if (pipelineRun.spec.pipelineRef) {
return {
resourceKind: PipelineModel.kind,
resourceName: pipelineRun.spec.pipelineRef.name,
namespace: pipelineRun.metadata.namespace,
};
}

return {
resourceKind: 'EmbeddedPipeLine', // intentional capitalization for EPL
resourceName: t('pipelines-plugin~Embedded Pipeline'),
};
};
Expand Up @@ -6,8 +6,8 @@ import {
PipelineKind,
TektonResource,
PipelineRunKind,
PipelineRunInlineResource,
PipelineRunInlineResourceParam,
PipelineRunEmbeddedResource,
PipelineRunEmbeddedResourceParam,
PipelineRunReferenceResource,
PipelineRunResource,
VolumeClaimTemplateType,
Expand Down Expand Up @@ -182,7 +182,7 @@ export const convertPipelineToModalData = (

export const convertMapToNameValueArray = (map: {
[key: string]: any;
}): PipelineRunInlineResourceParam[] => {
}): PipelineRunEmbeddedResourceParam[] => {
return Object.keys(map).map((name) => {
const value = map[name];
return { name, value };
Expand All @@ -197,7 +197,7 @@ const convertResources = (resource: PipelineModalFormResource): PipelineRunResou
params: convertMapToNameValueArray(resource.data.params),
type: resource.data.type,
},
} as PipelineRunInlineResource;
} as PipelineRunEmbeddedResource;
}

return {
Expand Down
Expand Up @@ -16,6 +16,8 @@ import {
} from '../utils';
import { externalTask, externalTaskWithVarietyParams } from './validation-utils-data';

const t = jest.fn((v) => v);

describe('taskParamIsRequired properly detects what is required', () => {
const structure: TektonParam = {
name: 'test-param',
Expand Down Expand Up @@ -63,50 +65,59 @@ describe('findTaskFromFormikData / findTask', () => {

it('should decompose formik state & handle nulls', () => {
const formValues = createFormValues();
expect(findTaskFromFormikData(formValues, null)).toBe(null);
expect(findTaskFromFormikData(formValues, { name: 'test' })).toBe(null);
expect(findTaskFromFormikData(formValues, { name: 'test', taskRef: { name: 'test' } })).toBe(
expect(findTaskFromFormikData(formValues, null, t)).toBe(null);
expect(findTaskFromFormikData(formValues, { name: 'test' }, t)).toBe(null);
expect(findTaskFromFormikData(formValues, { name: 'test', taskRef: { name: 'test' } }, t)).toBe(
null,
);
});

it('should handle fail states', () => {
expect(findTask(null, null)).toBe(null);
expect(findTask(null, null, t)).toBe(null);
expect(
findTask({ tasksLoaded: true, clusterTasks: [externalTask], namespacedTasks: [] }, null),
findTask({ tasksLoaded: true, clusterTasks: [externalTask], namespacedTasks: [] }, null, t),
).toBe(null);
expect(
findTask(
{ tasksLoaded: true, clusterTasks: [externalTask], namespacedTasks: [] },
{ name: 'test', taskRef: { name: 'unavailable-task' } },
t,
),
).toBe(undefined);
});

it('should be able to find a clusterTask', () => {
const formValues = createFormValues([externalTask]);
expect(
findTask(formValues.taskResources, {
name: 'test',
taskRef: {
name: externalTask.metadata.name,
kind: externalTask.kind,
findTask(
formValues.taskResources,
{
name: 'test',
taskRef: {
name: externalTask.metadata.name,
kind: externalTask.kind,
},
},
}),
t,
),
).toBe(externalTask);
});

it('should be able to find a namespacedTask', () => {
const namespacedTask = { ...externalTask, kind: 'Task' };
const formValues = createFormValues([], [namespacedTask]);
expect(
findTask(formValues.taskResources, {
name: 'test',
taskRef: {
name: namespacedTask.metadata.name,
kind: namespacedTask.kind,
findTask(
formValues.taskResources,
{
name: 'test',
taskRef: {
name: namespacedTask.metadata.name,
kind: namespacedTask.kind,
},
},
}),
t,
),
).toBe(namespacedTask);
});
});
Expand Down

0 comments on commit 97d95e8

Please sign in to comment.