Skip to content

Commit a0de3fd

Browse files
Merge pull request #2158 from andrewballantyne/handle-outdated-tags
Update jupyter & ds projects to hide outdated images
2 parents 3b03421 + 59593fa commit a0de3fd

File tree

7 files changed

+87
-3
lines changed

7 files changed

+87
-3
lines changed

backend/src/routes/api/images/imageUtils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ const getTagInfo = (imageStream: ImageStream): ImageTagInfo[] => {
186186
if (!checkTagExistence(tag, imageStream)) {
187187
return; //Skip tag
188188
}
189+
if (tagAnnotations[IMAGE_ANNOTATIONS.OUTDATED]) {
190+
return; // tag is outdated - we want to keep it around for existing notebooks, not for new ones
191+
}
189192

190193
//TODO: add build status
191194
const tagInfo: ImageTagInfo = {

backend/src/utils/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const IMAGE_ANNOTATIONS = {
2626
DEPENDENCIES: 'opendatahub.io/notebook-python-dependencies',
2727
IMAGE_ORDER: 'opendatahub.io/notebook-image-order',
2828
RECOMMENDED: 'opendatahub.io/workbench-image-recommended',
29+
OUTDATED: 'opendatahub.io/image-tag-outdated',
2930
};
3031
export const blankDashboardCR: DashboardConfig = {
3132
apiVersion: 'opendatahub.io/v1alpha',

frontend/src/__mocks__/mockImageStreamK8sResource.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const mockImageStreamK8sResource = ({
1515
displayName = 'Test Image',
1616
opts = {},
1717
}: MockResourceConfigType): ImageStreamKind =>
18-
_.merge(
18+
_.mergeWith(
1919
{
2020
apiVersion: 'image.openshift.io/v1',
2121
kind: 'ImageStream',
@@ -81,4 +81,8 @@ export const mockImageStreamK8sResource = ({
8181
},
8282
} as ImageStreamKind,
8383
opts,
84+
// Make sure tags can be overridden
85+
(defaultValue, optsValue) =>
86+
// Allow for emptying the array
87+
Array.isArray(optsValue) && optsValue.length === 0 ? [] : undefined,
8488
);

frontend/src/k8sTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ type ImageStreamSpecTagAnnotations = Partial<{
6565
'opendatahub.io/notebook-software': string;
6666
'opendatahub.io/workbench-image-recommended': string;
6767
'opendatahub.io/default-image': string;
68+
'opendatahub.io/image-tag-outdated': string;
6869
}>;
6970

7071
export type NotebookAnnotations = Partial<{

frontend/src/pages/projects/screens/spawner/__tests__/spawnerUtils.spec.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { AWS_KEYS } from '~/pages/projects/dataConnections/const';
2-
import { isAWSValid } from '~/pages/projects/screens/spawner/spawnerUtils';
2+
import {
3+
getExistingVersionsForImageStream,
4+
isAWSValid,
5+
} from '~/pages/projects/screens/spawner/spawnerUtils';
36
import { EnvVariableDataEntry } from '~/pages/projects/types';
7+
import { mockImageStreamK8sResource } from '~/__mocks__/mockImageStreamK8sResource';
8+
import { IMAGE_ANNOTATIONS } from '~/pages/projects/screens/spawner/const';
49

510
describe('isAWSValid', () => {
611
const getMockAWSData = ({
@@ -67,3 +72,66 @@ describe('isAWSValid', () => {
6772
);
6873
});
6974
});
75+
76+
describe('getExistingVersionsForImageStream', () => {
77+
it('should handle no image tags', () => {
78+
const imageStream = mockImageStreamK8sResource({
79+
opts: { spec: { tags: [] }, status: { tags: [] } },
80+
});
81+
expect(getExistingVersionsForImageStream(imageStream)).toHaveLength(0);
82+
});
83+
84+
it('should return the only default value', () => {
85+
expect(getExistingVersionsForImageStream(mockImageStreamK8sResource({}))).toHaveLength(1);
86+
});
87+
88+
it('should exclude the outdated items', () => {
89+
// Override the first value
90+
const imageStream = mockImageStreamK8sResource({
91+
opts: { spec: { tags: [{ annotations: { [IMAGE_ANNOTATIONS.OUTDATED]: 'true' } }] } },
92+
});
93+
expect(getExistingVersionsForImageStream(imageStream)).toHaveLength(0);
94+
95+
// Add an outdated 2nd value
96+
const imageStream2 = mockImageStreamK8sResource({
97+
opts: {
98+
spec: {
99+
tags: [{}, { name: 'test', annotations: { [IMAGE_ANNOTATIONS.OUTDATED]: 'true' } }],
100+
},
101+
},
102+
});
103+
expect(getExistingVersionsForImageStream(imageStream2)).toHaveLength(1);
104+
});
105+
106+
it('should exclude removed tags', () => {
107+
const imageStream = mockImageStreamK8sResource({
108+
opts: {
109+
spec: {
110+
tags: [{ name: 'not-the-available-tag' }],
111+
},
112+
},
113+
});
114+
expect(getExistingVersionsForImageStream(imageStream)).toHaveLength(0);
115+
});
116+
117+
it('should exclude removed tags & outdated ones', () => {
118+
const imageStream = mockImageStreamK8sResource({
119+
opts: {
120+
spec: {
121+
tags: [
122+
{},
123+
{ name: 'not-the-available-tag' },
124+
{ name: 'should-be-included' },
125+
{ name: 'outdated', annotations: { [IMAGE_ANNOTATIONS.OUTDATED]: 'true' } },
126+
],
127+
},
128+
status: {
129+
tags: [{ tag: 'should-be-included' }, { tag: 'outdated' }],
130+
},
131+
},
132+
});
133+
const result = getExistingVersionsForImageStream(imageStream);
134+
expect(result).toHaveLength(1);
135+
expect(result[0]).toEqual({ name: 'should-be-included' });
136+
});
137+
});

frontend/src/pages/projects/screens/spawner/const.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const ScrollableSelectorID = 'workbench-spawner-page';
1515
export const FAILED_PHASES = [BUILD_PHASE.ERROR, BUILD_PHASE.FAILED];
1616
export const PENDING_PHASES = [BUILD_PHASE.NEW, BUILD_PHASE.PENDING, BUILD_PHASE.CANCELLED];
1717

18+
// TODO: Convert to enum
1819
export const IMAGE_ANNOTATIONS = {
1920
DESC: 'opendatahub.io/notebook-image-desc' as const,
2021
DISP_NAME: 'opendatahub.io/notebook-image-name' as const,
@@ -24,6 +25,7 @@ export const IMAGE_ANNOTATIONS = {
2425
DEPENDENCIES: 'opendatahub.io/notebook-python-dependencies' as const,
2526
IMAGE_ORDER: 'opendatahub.io/notebook-image-order' as const,
2627
RECOMMENDED: 'opendatahub.io/workbench-image-recommended' as const,
28+
OUTDATED: 'opendatahub.io/image-tag-outdated' as const,
2729
};
2830

2931
export const DEFAULT_NOTEBOOK_SIZES = [

frontend/src/pages/projects/screens/spawner/spawnerUtils.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,19 @@ export const getImageVersionSoftwareString = (imageVersion: ImageStreamSpecTagTy
199199
return softwareString.join(', ');
200200
};
201201

202+
const isOutdated = (version: ImageStreamSpecTagType): boolean =>
203+
!!version.annotations?.[IMAGE_ANNOTATIONS.OUTDATED];
204+
202205
/**
203206
* Get all the `imageStream.spec.tags` and filter the ones exists in `imageStream.status.tags`
204207
*/
205208
export const getExistingVersionsForImageStream = (
206209
imageStream: ImageStreamKind,
207210
): ImageStreamSpecTagType[] => {
208211
const allVersions = imageStream.spec.tags || [];
209-
return allVersions.filter((version) => checkVersionExistence(imageStream, version));
212+
return allVersions
213+
.filter((version) => !isOutdated(version))
214+
.filter((version) => checkVersionExistence(imageStream, version));
210215
};
211216

212217
/**

0 commit comments

Comments
 (0)