Skip to content
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

Show warning when unsupported Devfile features used #727

Merged
merged 7 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/common/src/dto/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ export interface IDevWorkspaceList {
items: V1alpha2DevWorkspace[];
}

export interface IDevworkspaceResources {
export interface IDevWorkspaceResources {
devfileContent: string | undefined;
editorPath: string | undefined;
pluginRegistryUrl: string | undefined;
editorEntry: string | undefined;
editorId: string | undefined;
editorContent: string | undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { api } from '@eclipse-che/common';
import { IPatch } from '@eclipse-che/common/src/dto/api';
import * as mockClient from '@kubernetes/client-node';
import { CustomObjectsApi } from '@kubernetes/client-node';
import { IncomingMessage } from 'http';
import { DevWorkspaceApiService } from '../devWorkspaceApi';

const namespace = 'user-che';
Expand All @@ -32,7 +33,10 @@ describe('DevWorkspace API Service', () => {

const stubCustomObjectsApi = {
createNamespacedCustomObject: () => {
return Promise.resolve({ body: getDevWorkspace() });
return Promise.resolve({
body: getDevWorkspace(),
response: { headers: {} } as IncomingMessage,
});
},
deleteNamespacedCustomObject: () => {
return Promise.resolve({ body: {} });
Expand All @@ -44,7 +48,10 @@ describe('DevWorkspace API Service', () => {
return Promise.resolve({ body: buildListNamespacesCustomObject() });
},
patchNamespacedCustomObject: () => {
return Promise.resolve({ body: getDevWorkspace() });
return Promise.resolve({
body: getDevWorkspace(),
response: { headers: {} } as IncomingMessage,
});
},
replaceNamespacedCustomObject: () => {
return Promise.resolve({ body: getDevWorkspace() });
Expand All @@ -71,10 +78,6 @@ describe('DevWorkspace API Service', () => {
stubCustomObjectsApi,
'patchNamespacedCustomObject',
);
const spyReplaceNamespacedCustomObject = jest.spyOn(
stubCustomObjectsApi,
'replaceNamespacedCustomObject',
);

beforeEach(() => {
const { KubeConfig } = mockClient;
Expand Down Expand Up @@ -123,7 +126,8 @@ describe('DevWorkspace API Service', () => {
} as V1alpha2DevWorkspace;

const res = await devWorkspaceService.create(devWorkspace, namespace);
expect(res).toEqual(getDevWorkspace());
expect(res.devWorkspace).toStrictEqual(getDevWorkspace());
expect(res.headers).toStrictEqual({});
expect(spyCreateNamespacedCustomObject).toHaveBeenCalledWith(
devworkspaceGroup,
devworkspaceLatestVersion,
Expand All @@ -143,7 +147,8 @@ describe('DevWorkspace API Service', () => {
];

const res = await devWorkspaceService.patch(namespace, name, patches);
expect(res).toEqual(getDevWorkspace());
expect(res.devWorkspace).toStrictEqual(getDevWorkspace());
expect(res.headers).toStrictEqual({});
expect(spyPatchNamespacedCustomObject).toHaveBeenCalledWith(
devworkspaceGroup,
devworkspaceLatestVersion,
Expand All @@ -158,28 +163,6 @@ describe('DevWorkspace API Service', () => {
);
});

test('updating', async () => {
const devWorkspace = {
apiVersion: 'workspace.devfile.io/v1alpha2',
kind: 'DevWorkspace',
metadata: {
name: 'wksp-name',
namespace,
},
} as V1alpha2DevWorkspace;

const res = await devWorkspaceService.update(devWorkspace);
expect(res).toEqual(getDevWorkspace());
expect(spyReplaceNamespacedCustomObject).toHaveBeenCalledWith(
devworkspaceGroup,
devworkspaceLatestVersion,
namespace,
devworkspacePlural,
devWorkspace.metadata?.name,
devWorkspace,
);
});

test('deleting', async () => {
await devWorkspaceService.delete(namespace, name);
expect(spyDeleteNamespacedCustomObject).toHaveBeenCalledWith(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { api } from '@eclipse-che/common';
import * as k8s from '@kubernetes/client-node';
import { V1Status } from '@kubernetes/client-node';
import http from 'http';
import http, { IncomingHttpHeaders } from 'http';
import { MessageListener } from '../../services/types/Observer';
import { IDevWorkspaceApi } from '../types';
import { createError } from './helpers/createError';
Expand Down Expand Up @@ -71,10 +71,21 @@ export class DevWorkspaceApiService implements IDevWorkspaceApi {
}
}

private propagateHeaders(resp: { response: http.IncomingMessage; body: unknown }) {
const propagateHeaders = ['warning'];
const headers = Object.entries(resp.response.headers).reduce((acc, [key, value]) => {
if (propagateHeaders.includes(key)) {
acc[key] = value;
}
return acc;
}, {} as Partial<IncomingHttpHeaders>);
return headers;
}

async create(
devworkspace: V1alpha2DevWorkspace,
namespace: string,
): Promise<V1alpha2DevWorkspace> {
): Promise<{ devWorkspace: V1alpha2DevWorkspace; headers: Partial<IncomingHttpHeaders> }> {
try {
if (!devworkspace.metadata?.name && !devworkspace.metadata?.generateName) {
throw new Error(
Expand All @@ -89,46 +100,14 @@ export class DevWorkspaceApiService implements IDevWorkspaceApi {
devworkspacePlural,
devworkspace,
);
return resp.body as V1alpha2DevWorkspace;
const devWorkspace = resp.body as V1alpha2DevWorkspace;
const headers = this.propagateHeaders(resp);
return { devWorkspace, headers };
} catch (e) {
throw createError(e, DEV_WORKSPACE_API_ERROR_LABEL, 'Unable to create devworkspace');
}
}

async update(devworkspace: V1alpha2DevWorkspace): Promise<V1alpha2DevWorkspace> {
try {
if (!devworkspace.metadata?.name || !devworkspace.metadata?.namespace) {
throw new Error('DevWorkspace.metadata with name and namespace are required');
}

// you have to delete some elements from the devworkspace in order to update
if (devworkspace.metadata?.uid) {
devworkspace.metadata.uid = undefined;
}
if (devworkspace.metadata?.creationTimestamp) {
delete devworkspace.metadata.creationTimestamp;
}
if (devworkspace.metadata?.deletionTimestamp) {
delete devworkspace.metadata.deletionTimestamp;
}

const name = devworkspace.metadata.name;
const namespace = devworkspace.metadata.namespace;

const resp = await this.customObjectAPI.replaceNamespacedCustomObject(
devworkspaceGroup,
devworkspaceLatestVersion,
namespace,
devworkspacePlural,
name,
devworkspace,
);
return resp.body as V1alpha2DevWorkspace;
} catch (e) {
throw createError(e, DEV_WORKSPACE_API_ERROR_LABEL, 'Unable to update devworkspace');
}
}

async delete(namespace: string, name: string): Promise<void> {
try {
await this.customObjectAPI.deleteNamespacedCustomObject(
Expand All @@ -154,11 +133,7 @@ export class DevWorkspaceApiService implements IDevWorkspaceApi {
namespace: string,
name: string,
patches: api.IPatch[],
): Promise<V1alpha2DevWorkspace> {
return this.createPatch(namespace, name, patches);
}

private async createPatch(namespace: string, name: string, patches: api.IPatch[]) {
): Promise<{ devWorkspace: V1alpha2DevWorkspace; headers: Partial<IncomingHttpHeaders> }> {
try {
const options = {
headers: {
Expand All @@ -177,7 +152,9 @@ export class DevWorkspaceApiService implements IDevWorkspaceApi {
undefined,
options,
);
return resp.body as V1alpha2DevWorkspace;
const devWorkspace = resp.body as V1alpha2DevWorkspace;
const headers = this.propagateHeaders(resp);
return { devWorkspace, headers };
} catch (e) {
throw createError(e, DEV_WORKSPACE_API_ERROR_LABEL, 'Unable to patch devworkspace');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { api } from '@eclipse-che/common';
import * as k8s from '@kubernetes/client-node';
import { MessageListener } from '../../services/types/Observer';
import { IncomingHttpHeaders } from 'http';

/**
* Holds the methods for working with dockerconfig for devworkspace
Expand Down Expand Up @@ -49,19 +50,17 @@ export interface IDevWorkspaceApi extends IWatcherService {
getByName(namespace: string, name: string): Promise<V1alpha2DevWorkspace>;

/**
* Get list of devworkspaces in the given namespace
* Get list of DevWorkspaces in the given namespace
*/
listInNamespace(namespace: string): Promise<api.IDevWorkspaceList>;

/**
* Create a devworkspace based on the specified configuration.
* Create a DevWorkspace based on the specified configuration.
*/
create(devworkspace: V1alpha2DevWorkspace, namespace: string): Promise<V1alpha2DevWorkspace>;

/**
* Updates the DevWorkspace with the given configuration
*/
update(devworkspace: V1alpha2DevWorkspace): Promise<V1alpha2DevWorkspace>;
create(
devWorkspace: V1alpha2DevWorkspace,
namespace: string,
): Promise<{ devWorkspace: V1alpha2DevWorkspace; headers: Partial<IncomingHttpHeaders> }>;

/**
* Delete the DevWorkspace with given name in the specified namespace
Expand All @@ -71,7 +70,11 @@ export interface IDevWorkspaceApi extends IWatcherService {
/**
* Patches the DevWorkspace with given name in the specified namespace
*/
patch(namespace: string, name: string, patches: api.IPatch[]): Promise<V1alpha2DevWorkspace>;
patch(
namespace: string,
name: string,
patches: api.IPatch[],
): Promise<{ devWorkspace: V1alpha2DevWorkspace; headers: Partial<IncomingHttpHeaders> }>;
}

export interface IEventApi extends IWatcherService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ export function registerDevworkspaceResourcesRoute(server: FastifyInstance) {
`${baseApiPath}/devworkspace-resources`,
getSchema({ tags, body: devWorkspaceResourcesSchema }),
async function (request: FastifyRequest) {
const { devfileContent, editorPath, pluginRegistryUrl, editorEntry, editorContent } =
request.body as api.IDevworkspaceResources;
const { devfileContent, editorPath, pluginRegistryUrl, editorId, editorContent } =
request.body as api.IDevWorkspaceResources;
const context = await generator.generateDevfileContext(
{
devfileContent,
editorPath,
pluginRegistryUrl,
editorEntry,
editorEntry: editorId,
editorContent,
projects: [],
},
Expand Down
18 changes: 14 additions & 4 deletions packages/dashboard-backend/src/routes/api/devworkspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function registerDevworkspacesRoutes(server: FastifyInstance) {
server.post(
`${baseApiPath}/namespace/:namespace/devworkspaces`,
getSchema({ tags, params: namespacedSchema, body: devworkspaceSchema }),
async function (request: FastifyRequest) {
async function (request: FastifyRequest, reply: FastifyReply) {
const { devworkspace } = request.body as restParams.IDevWorkspaceSpecParams;
const { namespace } = request.params as restParams.INamespacedParams;
if (!devworkspace.metadata) {
Expand All @@ -53,7 +53,10 @@ export function registerDevworkspacesRoutes(server: FastifyInstance) {
devworkspace.metadata.namespace = namespace;
const token = getToken(request);
const { devworkspaceApi } = getDevWorkspaceClient(token);
return devworkspaceApi.create(devworkspace, namespace);
const { headers, devWorkspace } = await devworkspaceApi.create(devworkspace, namespace);

reply.headers(headers);
reply.send(devWorkspace);
},
);

Expand All @@ -71,12 +74,19 @@ export function registerDevworkspacesRoutes(server: FastifyInstance) {
server.patch(
`${baseApiPath}/namespace/:namespace/devworkspaces/:workspaceName`,
getSchema({ tags, params: namespacedWorkspaceSchema, body: devworkspacePatchSchema }),
async function (request: FastifyRequest) {
async function (request: FastifyRequest, reply: FastifyReply) {
const { namespace, workspaceName } = request.params as restParams.INamespacedWorkspaceParams;
const patch = request.body as api.IPatch[];
const token = getToken(request);
const { devworkspaceApi } = getDevWorkspaceClient(token);
return devworkspaceApi.patch(namespace, workspaceName, patch);
const { headers, devWorkspace } = await devworkspaceApi.patch(
namespace,
workspaceName,
patch,
);

reply.headers(headers);
reply.send(devWorkspace);
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
V221DevfileComponents,
} from '@devfile/api';
import { api } from '@eclipse-che/common';
import { IncomingHttpHeaders } from 'http';
import {
DevWorkspaceClient,
IDevWorkspaceApi,
Expand Down Expand Up @@ -56,6 +57,7 @@ export const stubDevWorkspace: V1alpha2DevWorkspace = {
apiVersion: 'workspace.devfile.io/v1alpha2',
kind: 'DevWorkspace',
};
export const stubHeaders: IncomingHttpHeaders = {};

export const stubDevWorkspaceTemplatesList = [
{
Expand Down Expand Up @@ -95,11 +97,13 @@ export function getDevWorkspaceClient(_args: Parameters<typeof helper>): ReturnT
getWorkspaceStartTimeout: _cheCustomResource => stubWorkspaceStartupTimeout,
} as IServerConfigApi,
devworkspaceApi: {
create: (_devworkspace, _namespace) => Promise.resolve(stubDevWorkspace),
create: (_devworkspace, _namespace) =>
Promise.resolve({ devWorkspace: stubDevWorkspace, headers: stubHeaders }),
delete: (_namespace, _name) => Promise.resolve(undefined),
getByName: (_namespace, _name) => Promise.resolve(stubDevWorkspace),
listInNamespace: _namespace => Promise.resolve(stubDevWorkspacesList),
patch: (_namespace, _name, _patches) => Promise.resolve(stubDevWorkspace),
patch: (_namespace, _name, _patches) =>
Promise.resolve({ devWorkspace: stubDevWorkspace, headers: stubHeaders }),
} as IDevWorkspaceApi,
dockerConfigApi: {
read: _namespace => Promise.resolve(stubDockerConfig),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ exports[`Loader Progress Step INITIALIZATION snapshot 1`] = `
<svg
aria-hidden={true}
aria-labelledby={null}
className="rotate stepIcon"
className="inProgressIcon rotate stepIcon"
data-testid="step-in-progress-icon"
fill="#0e6fe0"
fill="currentColor"
height="1em"
role="img"
style={
Expand Down Expand Up @@ -135,9 +135,9 @@ exports[`Loader Progress Step INITIALIZATION snapshot 1`] = `
<svg
aria-hidden={true}
aria-labelledby={null}
className="rotate stepIcon"
className="inProgressIcon rotate stepIcon"
data-testid="step-in-progress-icon"
fill="#0e6fe0"
fill="currentColor"
height="1em"
role="img"
style={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,6 @@ export class LoaderProgress extends React.PureComponent<Props> {
this.wizardRef = React.createRef();
}

public componentDidUpdate(): void {
const { currentStepId } = this.props;
const wizardCurrent = this.wizardRef.current;
if (wizardCurrent?.state?.currentStep !== currentStepId) {
wizardCurrent.state.currentStep = currentStepId;
}
}

render(): React.ReactNode {
const { currentStepId, steps } = this.props;

Expand Down
Loading