Skip to content

Commit

Permalink
feat: Restrict the total number of 'Running' workspaces on a cluster (#…
Browse files Browse the repository at this point in the history
…1173)

* feat: Restrict the total number of 'Running' workspaces on a cluster

Signed-off-by: Anatolii Bazko <[email protected]>
  • Loading branch information
tolusha authored Sep 3, 2024
1 parent 85beb72 commit 96c0e10
Show file tree
Hide file tree
Showing 31 changed files with 1,354 additions and 20 deletions.
1 change: 1 addition & 0 deletions packages/dashboard-backend/src/__tests__/app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const mockProcessExit = jest.fn();
throw new Error('exit code ' + code);
});

jest.mock('../routes/api/helpers/getDevWorkspaceClient.ts');
describe('App', () => {
afterEach(() => {
jest.clearAllMocks();
Expand Down
7 changes: 7 additions & 0 deletions packages/dashboard-backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { registerAirGapSampleRoute } from '@/routes/api/airGapSample';
import { registerClusterConfigRoute } from '@/routes/api/clusterConfig';
import { registerClusterInfoRoute } from '@/routes/api/clusterInfo';
import { registerDataResolverRoute } from '@/routes/api/dataResolver';
import { registerDevWorkspaceClusterRoutes } from '@/routes/api/devworkspaceCluster';
import { registerDevworkspaceResourcesRoute } from '@/routes/api/devworkspaceResources';
import { registerDevworkspacesRoutes } from '@/routes/api/devworkspaces';
import { registerDevWorkspaceTemplates } from '@/routes/api/devworkspaceTemplates';
Expand All @@ -33,6 +34,7 @@ import { registerEditorsRoutes } from '@/routes/api/editors';
import { registerEventsRoutes } from '@/routes/api/events';
import { registerGettingStartedSamplesRoutes } from '@/routes/api/gettingStartedSample';
import { registerGitConfigRoutes } from '@/routes/api/gitConfig';
import { getDevWorkspaceSingletonClient } from '@/routes/api/helpers/getDevWorkspaceClient';
import { registerKubeConfigRoute } from '@/routes/api/kubeConfig';
import { registerPersonalAccessTokenRoutes } from '@/routes/api/personalAccessToken';
import { registerPodmanLoginRoute } from '@/routes/api/podmanLogin';
Expand Down Expand Up @@ -69,6 +71,9 @@ export default async function buildApp(server: FastifyInstance): Promise<unknown
},
);

const { devWorkspaceClusterServiceApi } = getDevWorkspaceSingletonClient();
await devWorkspaceClusterServiceApi.watchInAllNamespaces();

server.register(import('@fastify/rate-limit'));

return Promise.allSettled([
Expand All @@ -95,6 +100,8 @@ export default async function buildApp(server: FastifyInstance): Promise<unknown

registerDevWorkspaceTemplates(server),

registerDevWorkspaceClusterRoutes(server),

registerDevworkspacesRoutes(server),

registerDockerConfigRoutes(server),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import {
IAirGapSampleApi,
IDevWorkspaceApi,
IDevWorkspaceClusterApi,
IDevWorkspaceSingletonClient,
IDevWorkspaceTemplateApi,
IDockerConfigApi,
IEditorsApi,
Expand Down Expand Up @@ -84,3 +86,9 @@ export class DevWorkspaceClient implements IDevWorkspaceClient {
throw new Error('Method not implemented.');
}
}

export class DevWorkspaceSingletonClient implements IDevWorkspaceSingletonClient {
get devWorkspaceClusterServiceApi(): IDevWorkspaceClusterApi {
throw new Error('Method not implemented.');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as mockClientNode from '@kubernetes/client-node';
import { KubeConfig } from '@kubernetes/client-node';

import { DevWorkspaceApiService } from '@/devworkspaceClient/services/devWorkspaceApi';
import { DevWorkspaceClusterApiService } from '@/devworkspaceClient/services/devWorkspaceClusterApiService';
import { DevWorkspaceTemplateApiService } from '@/devworkspaceClient/services/devWorkspaceTemplateApi';
import { DockerConfigApiService } from '@/devworkspaceClient/services/dockerConfigApi';
import { EventApiService } from '@/devworkspaceClient/services/eventApi';
Expand All @@ -26,7 +27,7 @@ import { ServerConfigApiService } from '@/devworkspaceClient/services/serverConf
import { SshKeysService } from '@/devworkspaceClient/services/sshKeysApi';
import { UserProfileApiService } from '@/devworkspaceClient/services/userProfileApi';

import { DevWorkspaceClient } from '..';
import { DevWorkspaceClient, DevWorkspaceSingletonClient } from '..';

jest.mock('../services/devWorkspaceApi.ts');

Expand Down Expand Up @@ -59,3 +60,28 @@ describe('DevWorkspace client', () => {
expect(client.sshKeysApi).toBeInstanceOf(SshKeysService);
});
});

describe('DevWorkspace singleton client', () => {
let config: KubeConfig;
beforeEach(() => {
const { KubeConfig } = mockClientNode;
config = new KubeConfig();
config.makeApiClient = jest.fn().mockImplementation(() => ({}));
});

afterEach(() => {
jest.clearAllMocks();
});

test('client', () => {
const client = DevWorkspaceSingletonClient.getInstance(config);

expect(client.devWorkspaceClusterServiceApi).toBeInstanceOf(DevWorkspaceClusterApiService);
});

test('return same instance', () => {
const client = DevWorkspaceSingletonClient.getInstance(config);

expect(client.devWorkspaceClusterServiceApi).toEqual(client.devWorkspaceClusterServiceApi);
});
});
22 changes: 22 additions & 0 deletions packages/dashboard-backend/src/devworkspaceClient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as k8s from '@kubernetes/client-node';

import { AirGapSampleApiService } from '@/devworkspaceClient/services/airGapSampleApi';
import { DevWorkspaceApiService } from '@/devworkspaceClient/services/devWorkspaceApi';
import { DevWorkspaceClusterApiService } from '@/devworkspaceClient/services/devWorkspaceClusterApiService';
import { DevWorkspaceTemplateApiService } from '@/devworkspaceClient/services/devWorkspaceTemplateApi';
import { DockerConfigApiService } from '@/devworkspaceClient/services/dockerConfigApi';
import { EditorsApiService } from '@/devworkspaceClient/services/editorsApi';
Expand All @@ -33,6 +34,8 @@ import {
IAirGapSampleApi,
IDevWorkspaceApi,
IDevWorkspaceClient,
IDevWorkspaceClusterApi,
IDevWorkspaceSingletonClient,
IDevWorkspaceTemplateApi,
IDockerConfigApi,
IEditorsApi,
Expand Down Expand Up @@ -127,3 +130,22 @@ export class DevWorkspaceClient implements IDevWorkspaceClient {
return new WorkspacePreferencesApiService(this.kubeConfig);
}
}

let devWorkspaceSingletonClient: DevWorkspaceSingletonClient;

/**
* Singleton client for devworkspace services.
*/
export class DevWorkspaceSingletonClient implements IDevWorkspaceSingletonClient {
static getInstance(kc: k8s.KubeConfig): DevWorkspaceSingletonClient {
if (!devWorkspaceSingletonClient) {
devWorkspaceSingletonClient = new DevWorkspaceSingletonClient(kc);
}
return devWorkspaceSingletonClient;
}

public readonly devWorkspaceClusterServiceApi: IDevWorkspaceClusterApi;
private constructor(kc: k8s.KubeConfig) {
this.devWorkspaceClusterServiceApi = new DevWorkspaceClusterApiService(kc);
}
}
Loading

0 comments on commit 96c0e10

Please sign in to comment.