diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/__tests__/index.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/__tests__/index.spec.ts index d7c44a92a..e5c68dcdc 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/__tests__/index.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/__tests__/index.spec.ts @@ -12,7 +12,7 @@ import { api } from '@eclipse-che/common'; import * as mockClient from '@kubernetes/client-node'; -import { CoreV1Api, V1ConfigMap } from '@kubernetes/client-node'; +import { CoreV1Api, V1Secret } from '@kubernetes/client-node'; import { IncomingMessage } from 'http'; import { GitConfigApiService } from '..'; @@ -22,22 +22,22 @@ jest.mock('@/devworkspaceClient/services/helpers/retryableExec.ts'); const namespace = 'user-che'; const responseBody = { data: { - gitconfig: `[user]\n\tname = User One\n\temail = user-1@che`, + gitconfig: btoa(`[user]\n\tname = User One\n\temail = user-1@che`), }, }; describe('Gitconfig API', () => { let gitConfigApiService: GitConfigApiService; - describe('configmap not found', () => { + describe('secret not found', () => { const stubCoreV1Api = { - createNamespacedConfigMap: () => { + createNamespacedSecret: () => { return Promise.resolve({ - body: responseBody as V1ConfigMap, + body: responseBody as V1Secret, response: {} as IncomingMessage, }); }, - readNamespacedConfigMap: () => { + readNamespacedSecret: () => { return Promise.reject({ body: {}, response: {} as IncomingMessage, @@ -47,7 +47,7 @@ describe('Gitconfig API', () => { }); }, } as unknown as CoreV1Api; - const spyCreateNamespacedConfigMap = jest.spyOn(stubCoreV1Api, 'createNamespacedConfigMap'); + const spyCreateNamespacedSecret = jest.spyOn(stubCoreV1Api, 'createNamespacedSecret'); beforeEach(() => { const { KubeConfig } = mockClient; @@ -61,7 +61,7 @@ describe('Gitconfig API', () => { jest.clearAllMocks(); }); - it('should create the configmap and return gitconfig', async () => { + it('should create the secret and return gitconfig', async () => { const resp = await gitConfigApiService.read(namespace); expect(resp.gitconfig).toStrictEqual({ @@ -71,13 +71,13 @@ describe('Gitconfig API', () => { }, }); - expect(spyCreateNamespacedConfigMap).toHaveBeenCalledTimes(1); - expect(spyCreateNamespacedConfigMap).toHaveBeenCalledWith(namespace, { + expect(spyCreateNamespacedSecret).toHaveBeenCalledTimes(1); + expect(spyCreateNamespacedSecret).toHaveBeenCalledWith(namespace, { data: { - gitconfig: `[user] + gitconfig: btoa(`[user] name="" email="" -`, +`), }, metadata: { annotations: { @@ -86,32 +86,32 @@ email="" }, labels: { 'controller.devfile.io/mount-to-devworkspace': 'true', - 'controller.devfile.io/watch-configmap': 'true', + 'controller.devfile.io/watch-secret': 'true', }, - name: 'workspace-userdata-gitconfig-configmap', + name: 'devworkspace-gitconfig-automaunt-secret', namespace, }, }); }); }); - describe('configmap found', () => { + describe('secret found', () => { const stubCoreV1Api = { - readNamespacedConfigMap: () => { + readNamespacedSecret: () => { return Promise.resolve({ - body: responseBody as V1ConfigMap, + body: responseBody as V1Secret, response: {} as IncomingMessage, }); }, - patchNamespacedConfigMap: () => { + patchNamespacedSecret: () => { return Promise.resolve({ - body: responseBody as V1ConfigMap, + body: responseBody as V1Secret, response: {} as IncomingMessage, }); }, } as unknown as CoreV1Api; - const spyReadNamespacedConfigMap = jest.spyOn(stubCoreV1Api, 'readNamespacedConfigMap'); - const spyPatchNamespacedConfigMap = jest.spyOn(stubCoreV1Api, 'patchNamespacedConfigMap'); + const spyReadNamespacedSecret = jest.spyOn(stubCoreV1Api, 'readNamespacedSecret'); + const spyPatchNamespacedSecret = jest.spyOn(stubCoreV1Api, 'patchNamespacedSecret'); beforeEach(() => { const { KubeConfig } = mockClient; @@ -138,12 +138,12 @@ email="" }), ); - expect(spyReadNamespacedConfigMap).toHaveBeenCalledTimes(1); - expect(spyPatchNamespacedConfigMap).not.toHaveBeenCalled(); + expect(spyReadNamespacedSecret).toHaveBeenCalledTimes(1); + expect(spyPatchNamespacedSecret).not.toHaveBeenCalled(); }); it('should throw', async () => { - spyReadNamespacedConfigMap.mockRejectedValueOnce('404 not found'); + spyReadNamespacedSecret.mockRejectedValueOnce('404 not found'); try { await gitConfigApiService.read(namespace); @@ -156,8 +156,8 @@ email="" ); } - expect(spyReadNamespacedConfigMap).toHaveBeenCalledTimes(1); - expect(spyPatchNamespacedConfigMap).not.toHaveBeenCalled(); + expect(spyReadNamespacedSecret).toHaveBeenCalledTimes(1); + expect(spyPatchNamespacedSecret).not.toHaveBeenCalled(); }); }); @@ -173,17 +173,17 @@ email="" } as api.IGitConfig; await gitConfigApiService.patch(namespace, newGitConfig); - expect(spyReadNamespacedConfigMap).toHaveBeenCalledTimes(1); - expect(spyPatchNamespacedConfigMap).toHaveBeenCalledTimes(1); - expect(spyPatchNamespacedConfigMap).toHaveBeenCalledWith( - 'workspace-userdata-gitconfig-configmap', + expect(spyReadNamespacedSecret).toHaveBeenCalledTimes(1); + expect(spyPatchNamespacedSecret).toHaveBeenCalledTimes(1); + expect(spyPatchNamespacedSecret).toHaveBeenCalledWith( + 'devworkspace-gitconfig-automaunt-secret', 'user-che', { data: { - gitconfig: `[user] + gitconfig: btoa(`[user] email="user-2@che" name="User Two" -`, +`), }, }, undefined, @@ -195,8 +195,8 @@ name="User Two" ); }); - it('should throw when can`t read the ConfigMap', async () => { - spyReadNamespacedConfigMap.mockRejectedValueOnce('404 not found'); + it('should throw when can`t read the secret', async () => { + spyReadNamespacedSecret.mockRejectedValueOnce('404 not found'); const newGitConfig = { gitconfig: { @@ -218,12 +218,12 @@ name="User Two" ); } - expect(spyReadNamespacedConfigMap).toHaveBeenCalledTimes(1); - expect(spyPatchNamespacedConfigMap).toHaveBeenCalledTimes(0); + expect(spyReadNamespacedSecret).toHaveBeenCalledTimes(1); + expect(spyPatchNamespacedSecret).toHaveBeenCalledTimes(0); }); - it('should throw when failed to patch the ConfigMap', async () => { - spyPatchNamespacedConfigMap.mockRejectedValueOnce('some error'); + it('should throw when failed to patch the secret', async () => { + spyPatchNamespacedSecret.mockRejectedValueOnce('some error'); const newGitConfig = { gitconfig: { @@ -245,23 +245,23 @@ name="User Two" ); } - expect(spyReadNamespacedConfigMap).toHaveBeenCalledTimes(1); - expect(spyPatchNamespacedConfigMap).toHaveBeenCalledTimes(1); + expect(spyReadNamespacedSecret).toHaveBeenCalledTimes(1); + expect(spyPatchNamespacedSecret).toHaveBeenCalledTimes(1); }); it('should throw when conflict detected', async () => { - spyReadNamespacedConfigMap.mockResolvedValueOnce({ + spyReadNamespacedSecret.mockResolvedValueOnce({ body: { metadata: { resourceVersion: '2', }, data: { - gitconfig: `[user] + gitconfig: btoa(`[user] name="User Two" email="user-2@che" -`, +`), }, - } as V1ConfigMap, + } as V1Secret, response: {} as IncomingMessage, }); @@ -290,8 +290,8 @@ email="user-2@che" 'Conflict detected. The gitconfig was modified in the namespace "user-che".', ); - expect(spyReadNamespacedConfigMap).toHaveBeenCalledTimes(1); - expect(spyPatchNamespacedConfigMap).toHaveBeenCalledTimes(0); + expect(spyReadNamespacedSecret).toHaveBeenCalledTimes(1); + expect(spyPatchNamespacedSecret).toHaveBeenCalledTimes(0); }); }); }); diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/index.ts b/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/index.ts index 254e41e87..daf38d799 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/index.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/index.ts @@ -21,7 +21,7 @@ import { } from '@/devworkspaceClient/services/helpers/prepareCoreV1API'; import { IGitConfigApi } from '@/devworkspaceClient/types'; -const GITCONFIG_CONFIGMAP = 'workspace-userdata-gitconfig-configmap'; +const GITCONFIG_SECRET = 'devworkspace-gitconfig-automaunt-secret'; const GITCONFIG_API_ERROR_LABEL = 'CORE_V1_API_ERROR'; export class GitConfigApiService implements IGitConfigApi { @@ -37,13 +37,13 @@ export class GitConfigApiService implements IGitConfigApi { */ public async read(namespace: string): Promise { try { - const response = await this.coreV1API.readNamespacedConfigMap(GITCONFIG_CONFIGMAP, namespace); + const response = await this.coreV1API.readNamespacedSecret(GITCONFIG_SECRET, namespace); return this.toGitConfig(response.body); } catch (error) { if (helpers.errors.isKubeClientError(error) && error.statusCode === 404) { - // Create gitconfig configmap if it does not exist - return this.createGitConfigMap(namespace); + // Create gitconfig secret if it does not exist + return this.createGitConfigSecret(namespace); } const message = `Unable to read gitconfig in the namespace "${namespace}"`; @@ -53,35 +53,37 @@ export class GitConfigApiService implements IGitConfigApi { /** * @throws - * Creates `gitconfig` ConfigMap in the given `namespace`. + * Creates `gitconfig` Secret in the given `namespace`. */ - private async createGitConfigMap(namespace: string): Promise { - const configMap = new k8s.V1ConfigMap(); - configMap.metadata = { - name: GITCONFIG_CONFIGMAP, + private async createGitConfigSecret(namespace: string): Promise { + const secret = new k8s.V1Secret(); + secret.metadata = { + name: GITCONFIG_SECRET, namespace, labels: { 'controller.devfile.io/mount-to-devworkspace': 'true', - 'controller.devfile.io/watch-configmap': 'true', + 'controller.devfile.io/watch-secret': 'true', }, annotations: { 'controller.devfile.io/mount-as': 'subpath', 'controller.devfile.io/mount-path': '/etc/', }, }; - configMap.data = { - gitconfig: this.fromGitConfig({ - gitconfig: { - user: { - name: '', - email: '', + secret.data = { + gitconfig: btoa( + this.fromGitConfig({ + gitconfig: { + user: { + name: '', + email: '', + }, }, - }, - }), + }), + ), }; try { - const response = await this.coreV1API.createNamespacedConfigMap(namespace, configMap); + const response = await this.coreV1API.createNamespacedSecret(namespace, secret); return this.toGitConfig(response.body); } catch (error) { const message = `Unable to create gitconfig in the namespace "${namespace}"`; @@ -114,12 +116,12 @@ export class GitConfigApiService implements IGitConfigApi { try { const gitconfigStr = this.fromGitConfig(gitConfig); - const response = await this.coreV1API.patchNamespacedConfigMap( - GITCONFIG_CONFIGMAP, + const response = await this.coreV1API.patchNamespacedSecret( + GITCONFIG_SECRET, namespace, { data: { - gitconfig: gitconfigStr, + gitconfig: btoa(gitconfigStr), }, }, undefined, @@ -153,11 +155,11 @@ export class GitConfigApiService implements IGitConfigApi { /** * @throws - * Extracts `resourceVersion` and `data.gitconfig` from given `ConfigMap`. + * Extracts `resourceVersion` and `data.gitconfig` from given `Secret`. */ - private toGitConfig(configMapBody: k8s.V1ConfigMap): api.IGitConfig { - const resourceVersion = configMapBody.metadata?.resourceVersion; - const gitconfigStr = configMapBody.data?.gitconfig; + private toGitConfig(secretBody: k8s.V1ConfigMap): api.IGitConfig { + const resourceVersion = secretBody.metadata?.resourceVersion; + const gitconfigStr = secretBody.data?.gitconfig; const parser = new ini.Parser(); @@ -165,7 +167,7 @@ export class GitConfigApiService implements IGitConfigApi { throw new Error('Unexpected data type'); } - const gitconfigLines = gitconfigStr.split(/\r?\n/); + const gitconfigLines = atob(gitconfigStr).split(/\r?\n/); const gitconfig = parser.parse(gitconfigLines); if (!isGitConfig(gitconfig)) { diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/prepareCoreV1API.ts b/packages/dashboard-backend/src/devworkspaceClient/services/helpers/prepareCoreV1API.ts index 9a30e9405..ec58be210 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/helpers/prepareCoreV1API.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/helpers/prepareCoreV1API.ts @@ -22,6 +22,7 @@ export type CoreV1API = Pick< | 'listNamespacedEvent' | 'listNamespacedPod' | 'listNamespacedSecret' + | 'patchNamespacedSecret' | 'patchNamespacedConfigMap' | 'readNamespacedConfigMap' | 'readNamespacedPod' @@ -48,6 +49,8 @@ export function prepareCoreV1API(kc: k8s.KubeConfig): CoreV1API { retryableExec(() => coreV1API.listNamespacedSecret(...args)), patchNamespacedConfigMap: (...args: Parameters) => retryableExec(() => coreV1API.patchNamespacedConfigMap(...args)), + patchNamespacedSecret: (...args: Parameters) => + retryableExec(() => coreV1API.patchNamespacedSecret(...args)), readNamespacedConfigMap: (...args: Parameters) => retryableExec(() => coreV1API.readNamespacedConfigMap(...args)), readNamespacedPod: (...args: Parameters) => diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/workspacePreferencesApi.ts b/packages/dashboard-backend/src/devworkspaceClient/services/workspacePreferencesApi.ts index d9b7815f6..96d2d7cce 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/workspacePreferencesApi.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/workspacePreferencesApi.ts @@ -35,7 +35,7 @@ export class WorkspacePreferencesApiService implements IWorkspacePreferencesApi async getWorkspacePreferences(namespace: string): Promise { try { - const response = await this.coreV1API.readNamespacedConfigMap( + const response = await this.coreV1API.readNamespacedSecret( DEV_WORKSPACE_PREFERENCES_CONFIGMAP, namespace, );