Skip to content

Commit

Permalink
fix: editor update flow
Browse files Browse the repository at this point in the history
Signed-off-by: Oleksii Orel <[email protected]>
  • Loading branch information
olexii4 committed Feb 4, 2025
1 parent 1635623 commit 53628ab
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const getVSCodeDevWorkspaceTemplate = (cpuLimit = '1500m'): devfileApi.DevWorksp
metadata: {
annotations: {
'che.eclipse.org/components-update-policy': 'managed',
'che.eclipse.org/plugin-registry-url': 'che-incubator/che-code/latest',
'che.eclipse.org/plugin-registry-url':
'http://127.0.0.1:8080/dashboard/api/editors/devfile?che-editor=che-incubator/che-code/latest',
},
creationTimestamp: new Date('2024-05-30T12:51:45Z'),
generation: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import { V230Devfile } from '@devfile/api';

const getVSCodeEditorDefinition = (): V230Devfile => {
const getVSCodeEditorDefinition = (cpuLimit = '500m'): V230Devfile => {
return {
attributes: {
version: null,
Expand All @@ -36,7 +36,7 @@ const getVSCodeEditorDefinition = (): V230Devfile => {
{
container: {
command: ['/entrypoint-init-container.sh'],
cpuLimit: '500m',
cpuLimit,
cpuRequest: '30m',
image: 'quay.io/che-incubator/che-code:latest',
memoryLimit: '256Mi',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/

import mockAxios from 'axios';
import { dump } from 'js-yaml';

import { container } from '@/inversify.config';
import { dashboardBackendPrefix } from '@/services/backend-client/const';
Expand All @@ -24,11 +25,6 @@ import {
REGISTRY_URL,
} from '@/services/workspace-client/devworkspace/devWorkspaceClient';

const mockFetchData = jest.fn();
jest.mock('@/services/registry/fetchData', () => ({
fetchData: (...args: unknown[]) => mockFetchData(...args),
}));

describe('DevWorkspace client editor update', () => {
const namespace = 'admin-che';
const client = container.get(DevWorkspaceClient);
Expand Down Expand Up @@ -134,53 +130,49 @@ describe('DevWorkspace client editor update', () => {

describe('DevWorkspaceTemplate with plugin registry URL', () => {
it('should return patch for an editor if it has been updated', async () => {
const template = getVSCodeDevWorkspaceTemplate('1000m');
const editor = getVSCodeEditorDefinition('1000m') as devfileApi.Devfile;
const template = getVSCodeDevWorkspaceTemplate('500m');
template.metadata.annotations = {
'che.eclipse.org/components-update-policy': 'managed',
'che.eclipse.org/plugin-registry-url':
'https://192.168.64.24.nip.io/plugin-registry/v3/plugins/che-incubator/che-code/latest/devfile.yaml',
'https://192.168.64.24.nip.io/che-incubator/che-code/devfile.yaml',
};

const mockPatch = mockAxios.get as jest.Mock;
mockPatch.mockResolvedValueOnce(new Promise(resolve => resolve({ data: template })));
const mockGet = mockAxios.get as jest.Mock;
mockGet.mockResolvedValueOnce(new Promise(resolve => resolve({ data: template })));

// if cpuLimit changed from '1000m' to '500m'
const newTemplate = getVSCodeDevWorkspaceTemplate('500m');
newTemplate.metadata.annotations = {
'che.eclipse.org/components-update-policy': 'managed',
'che.eclipse.org/plugin-registry-url':
'https://192.168.64.24.nip.io/plugin-registry/v3/plugins/che-incubator/che-code/latest/devfile.yaml',
};
const mockPost = mockAxios.post as jest.Mock;
mockPost.mockResolvedValueOnce(new Promise(resolve => resolve({ data: dump(editor) })));

const editors: devfileApi.Devfile[] = [getVSCodeEditorDefinition() as devfileApi.Devfile];
const editorName = newTemplate.metadata.name;
const editorName = template.metadata.name;

const patch = await client.checkForTemplatesUpdate(
editorName,
namespace,
editors,
[editor],
pluginRegistryUrl,
pluginRegistryInternalUrl,
undefined,
);

expect(mockPatch.mock.calls).toEqual([
expect(mockGet.mock.calls).toEqual([
[`${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates/${editorName}`],
]);

expect(patch).toEqual([
{
op: 'replace',
path: '/metadata/annotations',
value: {
[COMPONENT_UPDATE_POLICY]: 'managed',
[REGISTRY_URL]: 'che-incubator/che-code/latest',
expect(mockPost.mock.calls).toEqual([
[
`${dashboardBackendPrefix}/data/resolver`,
{
url: 'https://192.168.64.24.nip.io/che-incubator/che-code/devfile.yaml',
},
},
],
]);

expect(patch).toEqual([
{
op: 'replace',
path: '/spec',
value: newTemplate.spec,
value: getVSCodeDevWorkspaceTemplate('1000m').spec,
},
]);
});
Expand All @@ -196,16 +188,13 @@ describe('DevWorkspace client editor update', () => {
const mockPatch = mockAxios.get as jest.Mock;
mockPatch.mockResolvedValueOnce(new Promise(resolve => resolve({ data: template })));

// if cpuLimit changed from '1000m' to '500m'
const newTemplate = getVSCodeDevWorkspaceTemplate('500m');
newTemplate.metadata.annotations = {
'che.eclipse.org/components-update-policy': 'managed',
'che.eclipse.org/plugin-registry-url':
'https://192.168.64.24.nip.io/custom-registry/v3/plugins/che-incubator/che-code/latest/devfile.yaml',
};
const editors: devfileApi.Devfile[] = [
getVSCodeEditorDefinition('1000m') as devfileApi.Devfile,
];
const editorName = template.metadata.name;

const editors: devfileApi.Devfile[] = [getVSCodeEditorDefinition() as devfileApi.Devfile];
const editorName = newTemplate.metadata.name;
const mockPost = mockAxios.post as jest.Mock;
mockPost.mockResolvedValueOnce(new Promise(resolve => resolve({ data: editors[0] })));

const patch = await client.checkForTemplatesUpdate(
editorName,
Expand All @@ -216,8 +205,13 @@ describe('DevWorkspace client editor update', () => {
undefined,
);

expect(mockPatch.mock.calls).toEqual([
[`${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates/${editorName}`],
expect(mockPost.mock.calls).toEqual([
[
`${dashboardBackendPrefix}/data/resolver`,
{
url: 'https://192.168.64.24.nip.io/custom-registry/v3/plugins/che-incubator/che-code/latest/devfile.yaml',
},
],
]);

expect(patch).toEqual([]);
Expand All @@ -230,10 +224,6 @@ describe('DevWorkspace client editor update', () => {
'che.eclipse.org/plugin-registry-url':
'https://192.168.64.24.nip.io/plugin-registry/v3/plugins/che-incubator/che-code/latest/devfile.yaml',
};

const mockPatch = mockAxios.get as jest.Mock;
mockPatch.mockResolvedValueOnce(new Promise(resolve => resolve({ data: template })));

// if cpuLimit changed from '1000m' to '500m'
const newTemplate = getVSCodeDevWorkspaceTemplate('500m');
newTemplate.metadata.annotations = {
Expand All @@ -242,6 +232,9 @@ describe('DevWorkspace client editor update', () => {
'https://192.168.64.24.nip.io/plugin-registry/v3/plugins/che-incubator/che-code/latest/devfile.yaml',
};

const mockGet = mockAxios.get as jest.Mock;
mockGet.mockResolvedValueOnce(new Promise(resolve => resolve({ data: template })));

const editors: devfileApi.Devfile[] = [getVSCodeEditorDefinition() as devfileApi.Devfile];
const editorName = newTemplate.metadata.name;

Expand All @@ -254,7 +247,7 @@ describe('DevWorkspace client editor update', () => {
undefined,
);

expect(mockPatch.mock.calls).toEqual([
expect(mockGet.mock.calls).toEqual([
[`${dashboardBackendPrefix}/namespace/${namespace}/devworkspacetemplates/${editorName}`],
]);

Expand All @@ -264,7 +257,8 @@ describe('DevWorkspace client editor update', () => {
path: '/metadata/annotations',
value: {
[COMPONENT_UPDATE_POLICY]: 'managed',
[REGISTRY_URL]: 'che-incubator/che-code/latest',
[REGISTRY_URL]:
'http://127.0.0.1:8080/dashboard/api/editors/devfile?che-editor=che-incubator/che-code/latest',
},
},
]);
Expand Down Expand Up @@ -311,7 +305,8 @@ describe('DevWorkspace client editor update', () => {
path: '/metadata/annotations',
value: {
[COMPONENT_UPDATE_POLICY]: 'managed',
[REGISTRY_URL]: 'che-incubator/custom-editor/latest',
[REGISTRY_URL]:
'http://127.0.0.1:8080/dashboard/api/editors/devfile?che-editor=che-incubator/custom-editor/latest',
},
},
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from '@devfile/api';
import { api } from '@eclipse-che/common';
import { inject, injectable } from 'inversify';
import { load } from 'js-yaml';
import cloneDeep from 'lodash/cloneDeep';
import isEqual from 'lodash/isEqual';

Expand All @@ -36,6 +37,7 @@ import {
import { delay } from '@/services/helpers/delay';
import { isWebTerminal } from '@/services/helpers/devworkspace';
import { DevWorkspaceStatus } from '@/services/helpers/types';
import { fetchData } from '@/services/registry/fetchData';
import { WorkspaceAdapter } from '@/services/workspace-adapter';
import {
devWorkspaceApiGroup,
Expand Down Expand Up @@ -670,64 +672,87 @@ export class DevWorkspaceClient {

const patch: api.IPatch[] = [];

let editorReference = managedTemplate.metadata?.annotations?.[REGISTRY_URL];
const editorIdOrPath = managedTemplate.metadata?.annotations?.[REGISTRY_URL];
const updatePolicy = managedTemplate.metadata?.annotations?.[COMPONENT_UPDATE_POLICY];

if (
!editorReference ||
managedTemplate.metadata?.annotations?.[COMPONENT_UPDATE_POLICY] !== 'managed'
) {
if (!editorIdOrPath || updatePolicy !== 'managed') {
console.log('Template is not managed');
return patch;
}

if (/^(https?:\/\/)/.test(editorReference)) {
// ------------------------------------
if (/^(https?:\/\/)/.test(editorIdOrPath)) {
// Define a regular expression pattern to match URLs containing 'plugin-registry/v3/plugins'
// and ending with 'devfile.yaml'. The part between 'v3/plugins/' and '/devfile.yaml' is captured.
const pluginRegistryURLPattern = /plugin-registry\/v3\/plugins\/(.+?)\/devfile\.yaml$/;
const match = editorReference.match(pluginRegistryURLPattern);
const match = editorIdOrPath.match(pluginRegistryURLPattern);

if (match) {
editorReference = match[1];
const annotations = {
[COMPONENT_UPDATE_POLICY]: 'managed',
[REGISTRY_URL]: editorReference,
[REGISTRY_URL]: `http://127.0.0.1:8080/dashboard/api/editors/devfile?che-editor=${match[1]}`,
};
// Create a patch to update the annotations by replacing plugin registry URL with the editor reference
patch.push({
op: 'replace',
path: '/metadata/annotations',
value: annotations,
});
} else {
console.log('Template is not managed');
return patch;
}
} else {
const annotations = {
[COMPONENT_UPDATE_POLICY]: 'managed',
[REGISTRY_URL]: `http://127.0.0.1:8080/dashboard/api/editors/devfile?che-editor=${editorIdOrPath}`,
};
patch.push({
op: 'replace',
path: '/metadata/annotations',
value: annotations,
});
}

const originalEditor: devfileApi.Devfile | undefined = editors.find(editor => {
return (
editor.metadata?.attributes?.publisher +
'/' +
editor.metadata?.name +
'/' +
editor.metadata?.attributes?.version ===
editorReference
);
});
if (!originalEditor) {
let plugin: devfileApi.Devfile | undefined = undefined;
// Found the target editors
if (editorIdOrPath.match(/^http:\/\/127.0.0.1:8080\/dashboard\/api\/editors\/devfile/)) {
const searchParams = new URLSearchParams(editorIdOrPath.split('?')[1]);
const editorReference = searchParams.get('che-editor');

const _plugin = editors.find(editor => {
return (
editor.metadata?.attributes?.publisher +
'/' +
editor.metadata?.name +
'/' +
editor.metadata?.attributes?.version ===
editorReference
);
});
if (_plugin !== undefined) {
plugin = cloneDeep(_plugin);
}
} else {
const pluginContent = await fetchData<string | devfileApi.Devfile>(editorIdOrPath);
if (typeof pluginContent === 'string') {
plugin = load(pluginContent) as devfileApi.Devfile;
} else if (typeof pluginContent === 'object') {
plugin = pluginContent;
}
}

if (plugin === undefined) {
console.warn('Failed to get plugin');
return patch;
}

const spec: Partial<V1alpha2DevWorkspaceTemplateSpec> = {};
for (const key in originalEditor) {
for (const key in plugin) {
if (key !== 'schemaVersion' && key !== 'metadata') {
if (key === 'components') {
originalEditor.components?.forEach(component => {
plugin.components?.forEach(component => {
if (component.container && !component.container.sourceMapping) {
component.container.sourceMapping = '/projects';
}
});
spec.components = originalEditor.components;
spec.components = plugin.components;
this.addEnvVarsToContainers(
spec.components,
pluginRegistryUrl,
Expand All @@ -736,7 +761,7 @@ export class DevWorkspaceClient {
clusterConsole,
);
} else {
spec[key] = originalEditor[key];
spec[key] = plugin[key];
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ describe('getEditor', () => {

expect(result).toEqual({
content: 'dumped devfile content',
editorYamlUrl: 'che-incubator/che-idea/next',
editorYamlUrl:
'http://127.0.0.1:8080/dashboard/api/editors/devfile?che-editor=che-incubator/che-idea/next',
});
expect(dispatch).not.toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,28 @@ export async function getEditor(
if (devfileObj) {
const content = devfileObj.content;
const error = devfileObj.error;
return Object.assign({ content, editorYamlUrl, error });
return { content, editorYamlUrl, error };
}
throw new Error(`Failed to fetch editor yaml by URL: ${editorYamlUrl}.`);
} else {
const editors = state.dwPlugins.cmEditors || [];
const editorId = editorIdOrPath;
// Find the editor by id
const editor: devfileApi.Devfile | undefined = editors.find(e => {
return (
e.metadata.attributes.publisher +
'/' +
e.metadata.name +
'/' +
e.metadata.attributes.version ===
editorIdOrPath
editorId
);
});
if (editor) {
return Object.assign({ content: dump(editor), editorYamlUrl: editorIdOrPath });
return {
content: dump(editor),
editorYamlUrl: `http://127.0.0.1:8080/dashboard/api/editors/devfile?che-editor=${editorId}`,
};
} else {
throw new Error(`Failed to fetch editor yaml by id: ${editorIdOrPath}.`);
}
Expand Down

0 comments on commit 53628ab

Please sign in to comment.