-
Notifications
You must be signed in to change notification settings - Fork 606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add workspaces to Pipeline Details Page #8238
Add workspaces to Pipeline Details Page #8238
Conversation
/retest |
@bgliwa01 @karthikjeeyar Unable to restore the commit in previous PR, please look into this one instead. regretting the inconvenience. |
@karthik I've taken care og the volumeClaimTemplate thing. |
<VolumeClaimTemplateLink | ||
labels={labels} | ||
workspaceName={workspace.name} | ||
namespace={namespace} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unique key is missing here, causing the error in console.
<VolumeClaimTemplateLink | |
labels={labels} | |
workspaceName={workspace.name} | |
namespace={namespace} | |
/> | |
<VolumeClaimTemplateLink | |
key={workspace.name} | |
labels={labels} | |
workspaceName={workspace.name} | |
namespace={namespace} | |
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added key
@@ -45,6 +46,7 @@ const PipelineDetails: React.FC<PipelineDetailsProps> = ({ | |||
links={taskLinks} | |||
title={t('pipelines-plugin~Tasks')} | |||
/> | |||
<WorkspaceDefinitionList workspaces={pipeline?.spec.workspaces} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spec is not optional but workspaces is
<WorkspaceDefinitionList workspaces={pipeline?.spec.workspaces} /> | |
<WorkspaceDefinitionList workspaces={pipeline.spec?.workspaces} /> |
@@ -57,6 +58,10 @@ const PipelineRunCustomDetails: React.FC<PipelineRunCustomDetailsProps> = ({ pip | |||
links={renderResources} | |||
namespace={pipelineRun.metadata.namespace} | |||
/> | |||
<WorkspaceResourceLinkList | |||
workspaces={pipelineRun?.spec.workspaces} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
workspaces={pipelineRun?.spec.workspaces} | |
workspaces={pipelineRun.spec?.workspaces} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
import { TaskModel } from '../../models'; | ||
import { TaskKind } from '../../types'; | ||
import WorkspaceDefinitionList from '../shared/workspaces/WorkspaceDefinitionList'; | ||
import './TaskDetails.scss'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: new line before scss imports
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
@@ -290,3 +290,76 @@ export const mockRunDurationTest = [ | |||
pipelineTestData[PipelineExampleNames.WORKSPACE_PIPELINE].pipelineRuns[DataState.SUCCESS], | |||
pipelineTestData[PipelineExampleNames.CLUSTER_PIPELINE].pipelineRuns[DataState.SUCCESS], | |||
]; | |||
|
|||
export const pipelineWithWorkspace: PipelineKind[] = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be good if we reuse the existing pipeline test data from pipelines-plugin/src/test-data/pipeline-data.ts
. This file exclusively contains different pipeline test data including one for WORKSPACE_PIPELINE
. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using existing PL
<ResourceSummary resource={task} /> | ||
</div> | ||
<div className="col-sm-6 odc-task-details__status"> | ||
<WorkspaceDefinitionList workspaces={task?.spec.workspaces} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<WorkspaceDefinitionList workspaces={task?.spec.workspaces} /> | |
<WorkspaceDefinitionList workspaces={task.spec?.workspaces} /> |
<ResourceSummary resource={task} /> | ||
</div> | ||
<div className="col-sm-6 odc-cluster-task-details__status"> | ||
<WorkspaceDefinitionList workspaces={task?.spec.workspaces} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<WorkspaceDefinitionList workspaces={task?.spec.workspaces} /> | |
<WorkspaceDefinitionList workspaces={task.spec?.workspaces} /> |
@@ -38,6 +39,10 @@ const TaskRunDetailsStatus = ({ taskRun }) => { | |||
</dd> | |||
</dl> | |||
)} | |||
<WorkspaceResourceLinkList | |||
workspaces={taskRun?.spec.workspaces} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
workspaces={taskRun?.spec.workspaces} | |
workspaces={taskRun.spec?.workspaces} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the TaskRun is there, I'm pretty sure Spec has to be there too... I don't think conditional checks are needed. You just have to support no workspaces (empty array or undefined) in the component.
69b77b4
to
516a463
Compare
@bgliwa01 For Empty Directory scenario, |
namespace, | ||
}) => { | ||
const { t } = useTranslation(); | ||
return workspaces?.length > 0 ? ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Ideally for cleaner code, you should just return early instead of making a return into a large ternary statement.
expect(WorkspaceDefinitionListWrapper.find('dd').exists()).toBe(false); | ||
expect(WorkspaceDefinitionListWrapper.find('dt').exists()).toBe(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not look for the root element? dl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
|
||
it('Should render workspace list', () => { | ||
const { pipeline } = pipelineTestData[PipelineExampleNames.WORKSPACE_PIPELINE]; | ||
WorkspaceDefinitionListWrapperProps = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WorkspaceDefinitionListWrapperProps = { | |
workspaceDefinitionListWrapperProps = { |
workspaces: PipelineWorkspace[]; | ||
} | ||
|
||
const WorkspaceDefinitionList: React.FC<WorkspaceDefinitionListProps> = ({ workspaces }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this component? Is this not just the same component as the WorkspaceResourceLinkList
with less bells and whistles? I think they both will render a div list 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the two components need different set of props to function. They also render lists based out of different types of objects.
There is also a need of certain additional mandatory props in ResourceLinkList which will not be required in DefinitionList
selector: { matchLabels: labels }, | ||
}); | ||
|
||
if (loaded && !loadError && pvcResource?.metadata) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: for readability, you typically want to return null
s early so that the proper render is always at the bottom. It obv doesn't impact the flow, but it helps to be consistent in a codebase this large.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the suggestion. Updated :)
const [pvcResource, loaded, loadError] = useK8sWatchResource<K8sResourceKind>({ | ||
kind: PersistentVolumeClaimModel.kind, | ||
isList: false, | ||
selector: { matchLabels: labels }, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you also need to look at the owner field 🤔
Otherwise you're going to fetch the first PVC that matches a label and it's a coin flip if you get the one you want.
Suggested new path:
- Fetch all PVCs that match the label
- Look at the metadata.ownerReferences and trim down to the PLR / TR that matches
- List only the remaining PVCs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated with this logic and also added tests
expect(WorkspaceDefinitionListWrapper.find('dd').exists()).toBe(true); | ||
expect(WorkspaceDefinitionListWrapper.find('dt').exists()).toBe(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing static markup here makes the test very fragile to component changes. You can omit these tag present assertions.
To test the condition that the component renders null or something, you can perform an exists check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
516a463
to
63ea712
Compare
export const getMatchedPVCs: GetMatchedPVCs = (pvcResources, ownerReference) => { | ||
return pvcResources.filter((pvc) => { | ||
const { ownerReferences } = pvc.metadata; | ||
return ownerReferences.find((reference) => reference.name === ownerReference); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
metadata.ownerReferences
is an optional field, If you call this function with a PVC without ownerReferences chances are this will throw a error in console, so add an empty array as a fallback value and you could also add test for the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the owner reference not include a kind/type as well? 🤔
example
PLR name for instance could match another resource very easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated. checking Kind as well, doing a null check and added tests
WorkspaceResourceLinkListProps, | ||
} from '../WorkspaceResourceLinkList'; | ||
import { taskRunWithWorkspaces } from '../../../taskruns/__tests__/taskrun-test-data'; | ||
import { SecretModel, PersistentVolumeClaimModel, ConfigMapModel } from '@console/internal/models'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: sort imports
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
63ea712
to
b0a76b8
Compare
Verified locally, works fine. We need to match the Empty directory scenario once we have the design for it in a different story. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm cancel
I see a couple issues that will generate bugs if we leave them.
- You make labels required for the fetching of the volumeClaimTemplate PVCs... which is not valid since a user could create a PLR without labels and it breaks down immediately.
- The fetching on the PLRs today will cause duplicates in references as each workspace can render n-number of PVC links based on the number of volumeClaimTemplate workspaces there are. Ie, if I have two volumeClaimTemplates, you'll get 2 PVC links for each workspace:
I started a conversation with Tekton on how we could use PLRs to properly fetch them, an oversight I did not see when reviewing the designs / story -- my apologizes. They think it's a bug upstream that we don't have these values on the PLR, so this may not be fixable today -- at least not directly.
I think our best option to avoid the duplicate links is to actually go through the TRs to get the values today. We may need to fix this later if the upstream code can support PLRs knowing their PVCs that get created from volumeClaimTemplates.
Suggested path to fix (WDYT): see history if you want to see my message -- we don't want this as there is a bug upstream
type GetMatchedPVCs = ( | ||
pvcResources: PersistentVolumeClaimKind[], | ||
ownerReference: string, | ||
) => PersistentVolumeClaimKind[]; | ||
|
||
export const getMatchedPVCs: GetMatchedPVCs = (pvcResources, ownerReference) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically when you're tying a function it's for when you want to make multiple functions of the same signature (like an interface)... Not that it matters a ton, but ideally gravitate towards direct types as it's easier to read.
return pvcResources.filter((pvc) => { | ||
const { ownerReferences = [] } = pvc.metadata; | ||
|
||
return ownerReferences.find( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return ownerReferences.find( | |
return ownerReferences.some( |
Filter takes a boolean result and you don't actually need the item.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
<WorkspaceResourceLinkList | ||
workspaces={taskRun.spec.workspaces} | ||
namespace={taskRun.metadata.namespace} | ||
ownerReference={taskRun.metadata.name} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside from ownerReference
not really speaking to the name of the prop... more like resourceName
or ownerResourceName
imo. But from this flow, TaskRun, you compare PipelineRun model to the owner reference.
|
||
if (!ownerReference) return null; | ||
|
||
if (loaded && !loadError && pvcResources?.length > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a nit, but you really should return early with nulls.
Ideally you code up your component like so:
() => {
if ...
return null
if ...
return null
if ...
return <small rendering> // ie. an Alert
return <main rendering> // ie. the main purpose of the component
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
const [pvcResources, loaded, loadError] = useK8sWatchResource<PersistentVolumeClaimKind[]>({ | ||
kind: PersistentVolumeClaimModel.kind, | ||
isList: true, | ||
selector: { matchLabels: labels }, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And you need to constrain this on namespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added namespace
workspaceDefinitionListWrapper = shallow( | ||
<WorkspaceDefinitionList {...workspaceDefinitionListWrapperProps} />, | ||
); | ||
expect(workspaceDefinitionListWrapper.find('dl').exists()).toBe(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think based on Christian's response, we want this:
workspaceDefinitionListWrapper = shallow( | |
<WorkspaceDefinitionList {...workspaceDefinitionListWrapperProps} />, | |
); | |
expect(workspaceDefinitionListWrapper.find('dl').exists()).toBe(false); | |
workspaceDefinitionListWrapper = shallow( | |
<WorkspaceDefinitionList {...workspaceDefinitionListWrapperProps} />, | |
); | |
expect(workspaceDefinitionListWrapper.isEmptyRender()).toBe(true); |
Ref: https://enzymejs.github.io/enzyme/docs/api/ShallowWrapper/isEmptyRender.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
<WorkspaceResourceLinkList {...workspaceResourceListWrapperProps} />, | ||
); | ||
expect(workspaceResourceListWrapper.find('dd').exists()).toBe(false); | ||
expect(workspaceResourceListWrapper.find('dt').exists()).toBe(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll want to use .isEmptyRender
here too... (and other places in this file)
@abhinandan13jan As requested by @bgliwa01 on slack, please drop the |
b0a76b8
to
dc28822
Compare
it('should return PVCs correctly matched with name and kind', () => { | ||
const matchedPVCs = getMatchedPVCs(pvcWithPipelineOwnerRef, 'pipeline2', PipelineRunModel.kind); | ||
expect(matchedPVCs.length).toBe(2); | ||
expect(matchedPVCs[0].metadata.name).toBe(pvcWithPipelineOwnerRef[1].metadata.name); | ||
expect(matchedPVCs[1].metadata.name).toBe(pvcWithPipelineOwnerRef[3].metadata.name); | ||
}); | ||
|
||
it('should not match PVCs when ownerRef matches name but not kind', () => { | ||
const matchedPVCs = getMatchedPVCs(pvcWithPipelineOwnerRef, 'pipeline2', PipelineRunModel.kind); | ||
expect(matchedPVCs.length).toBe(2); | ||
expect(matchedPVCs[0].metadata.name).toBe(pvcWithPipelineOwnerRef[1].metadata.name); | ||
expect(matchedPVCs[1].metadata.name).toBe(pvcWithPipelineOwnerRef[3].metadata.name); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let workspaceResourceListWrapper: ShallowWrapper<WorkspaceResourceLinkListProps>; | ||
let workspaceResourceListWrapperProps: WorkspaceResourceLinkListProps; | ||
|
||
it('Should not render dd or dt heading if no workspaces are found', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good use case for not explaining the expected output, speak to the reason for the test.
it('should not render if there are no workspaces'
let workspaceDefinitionListWrapper: ShallowWrapper<WorkspaceDefinitionListProps>; | ||
let workspaceDefinitionListWrapperProps: WorkspaceDefinitionListProps; | ||
|
||
it('Should not render root dl if no workspaces are found', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
root dl
?
<ResourceLink | ||
name={pvcResource.metadata.name} | ||
key={pvcResource.metadata.name} | ||
namespace={namespace} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Technically this should be pvcResource.metadata.namespace
-- the logic around this makes the two match but your namespace needs to be that of the pvcResource
not of your PLR/TR
return <div key={workspace.name}>{workspace.name}</div>; | ||
})} | ||
<VolumeClaimTemplatesLink | ||
namespace={namespace} | ||
ownerResourceName={ownerResourceName} | ||
ownerResourceKind={ownerResourceKind} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@abhinandan13jan Please test before you request a review... You are double rendering...
Or if you prefer, with my two volumeClaimTemplate
example from before:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need access to my resources, I posted as part of the upstream request for them to add volumeClaimTemplate PVC names to the |
dc28822
to
6d6c11b
Compare
if (!ownerResourceName) return null; | ||
|
||
if (!loaded || loadError || pvcResources?.length === 0) return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!ownerResourceName) return null; | |
if (!loaded || loadError || pvcResources?.length === 0) return null; | |
if (!ownerResourceName || !loaded || loadError || pvcResources?.length === 0) return null; |
|
||
export interface TaskRunDetailsStatusProps { | ||
taskRun: TaskRunKind; | ||
} | ||
|
||
const TaskRunDetailsStatus = ({ taskRun }) => { | ||
const { t } = useTranslation(); | ||
const ownerReference = taskRun.metadata.ownerReferences?.[0] || taskRun.metadata.name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
taskRun.metadata.ownerReferences?.[0]
This one is an object which is passed to ownerResourceName
in WorkspaceResourceLinkList
below. Shouldn't this be taskRun.metadata.ownerReferences?.[0].name
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this more, do we need this at all? TaskRun's ownerRef will be PipelineRun, but for ownerResourceKind
is tied to taskrun.kind always, so it will never match 🤔 or am i missing something here?
ie:
<WorkspaceResourceLinkList
workspaces={taskRun.spec.workspaces}
namespace={taskRun.metadata.namespace}
ownerResourceName={ownerReference} // <PipelineRun-name> (if taskrun is NOT standalone)
ownerResourceKind={taskRun.kind} // TaskRun
/>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, u r correct. I misplaced the ownerReference. Updated now
<WorkspaceResourceLinkList
workspaces={taskRun.spec.workspaces}
namespace={taskRun.metadata.namespace}
ownerResourceName={ownerReference ? ownerReference.name : taskRun.metadata.name}
ownerResourceKind={ownerReference ? ownerReference.kind : taskRun.kind}
/>
Workspace section is displayed without any list of workspaces for standalone Taskrun uses volumeClaimTemplate (failed due to errors). Used invalid taskRef name to reproduce this issue. invalid_task_ref.yaml
|
7824f8d
to
144625c
Compare
@abhinandan13jan You still need to fix the issue from my last comment. PTAL |
144625c
to
3b62136
Compare
{matchedPVCs.map((pvcResource) => { | ||
return ( | ||
<ResourceLink | ||
name={pvcResource.metadata.name} | ||
key={pvcResource.metadata.name} | ||
namespace={pvcResource.metadata.namespace} | ||
kind={PersistentVolumeClaimModel.kind} | ||
/> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of repeating this block twice, it would be nice if you could add it to a const and contidionally render it based on the showHeader
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
Verified, everything else looks good apart from my last minor comment, I will add lgtm once that is fixed. |
3b62136
to
c8ee149
Compare
@debsmita1 @karthikjeeyar @andrewballantyne This happens if we use pipelineRun as the ownerReference for taskRun, which I did because of a review comment. I've reverted that particular change and it works fine now. |
Thanks for all the changes @abhinandan13jan |
works fine. Thanks ! |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: abhinandan13jan, andrewballantyne, karthikjeeyar The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Addresses
In continuation to #8216
https://issues.redhat.com/browse/ODC-4444
Screenshot
PR Test
Add following YAMLs and check the details pages
Tests
Added unit tests
Browser conformance
Chrome 88