Skip to content
This repository has been archived by the owner on Apr 4, 2023. It is now read-only.

Commit

Permalink
fixup! chore: store gitconfig in a configmap
Browse files Browse the repository at this point in the history
  • Loading branch information
vinokurig committed Apr 1, 2022
1 parent ca32a38 commit 9f92e3f
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 66 deletions.
4 changes: 2 additions & 2 deletions extensions/eclipse-che-theia-git-provisioner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
"fs-extra": "7.0.1",
"@eclipse-che/theia-remote-api": "^0.0.1",
"@eclipse-che/theia-plugin-ext": "^0.0.1",
"@eclipse-che/theia-remote-impl-k8s": "^0.0.1",
"@theia/core": "1.18.0-next.120",
"@theia/preferences": "1.18.0-next.120",
"ini": "^1.3.5",
"nsfw": "^2.1.2",
"@eclipse-che/theia-user-preferences-synchronizer": "0.0.1",
"@kubernetes/client-node": "^0.12.1",
"@eclipse-che/theia-remote-impl-che-server": "0.0.1"
"@kubernetes/client-node": "^0.12.1"
},
"devDependencies": {
"@types/ini": "^1.3.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ import { Disposable, Emitter } from '@theia/core';
import { createFile, pathExists, readFile, writeFile } from 'fs-extra';
import { inject, injectable } from 'inversify';

import { CheK8SService } from '@eclipse-che/theia-remote-api/lib/common/k8s-service';
import { CheK8SServiceImpl } from '@eclipse-che/theia-remote-impl-che-server/lib/node/che-server-k8s-service-impl';
import { CheTheiaUserPreferencesSynchronizer } from '@eclipse-che/theia-user-preferences-synchronizer/lib/node/che-theia-preferences-synchronizer';
import { K8SServiceImpl } from '@eclipse-che/theia-remote-impl-k8s/lib/node/k8s-service-impl';
import { WorkspaceService } from '@eclipse-che/theia-remote-api/lib/common/workspace-service';
import { homedir } from 'os';
import { resolve } from 'path';
Expand All @@ -42,14 +41,12 @@ export interface GitConfiguration {

@injectable()
export class GitConfigurationController implements CheGitService {
constructor(
@inject(WorkspaceService)
private readonly workspaceService: WorkspaceService,
@inject(CheK8SService)
private readonly cheK8SService: CheK8SServiceImpl
) {
this.createGitconfigConfigmapIfNeeded().then(exists => {
if (exists) {
@inject(WorkspaceService)
private readonly workspaceService: WorkspaceService;

constructor(@inject(K8SServiceImpl) private readonly cheK8SService: K8SServiceImpl) {
this.createGitconfigConfigmapIfNeeded().then(configmapExists => {
if (configmapExists) {
this.updateUserGitconfigFromConfigmap();
}
});
Expand Down Expand Up @@ -78,21 +75,12 @@ export class GitConfigurationController implements CheGitService {
const configmap: k8s.V1ConfigMap = {
metadata: {
name: GITCONFIG_CONFIGMAP_NAME,
labels: {
'controller.devfile.io/mount-to-devworkspace': 'true',
'controller.devfile.io/watch-configmap': 'true',
},
annotations: {
'controller.devfile.io/mount-as': 'subpath',
'controller.devfile.io/mount-path': '/etc/',
},
},
data: { gitconfig: fs.existsSync(GIT_USER_CONFIG_PATH) ? fs.readFileSync(GIT_USER_CONFIG_PATH).toString() : '' },
};
try {
await this.cheK8SService
.makeApiClient(k8s.CoreV1Api)
.createNamespacedConfigMap(await this.workspaceService.getCurrentNamespace(), configmap);
const client = await this.cheK8SService.makeApiClient(k8s.CoreV1Api);
await client.createNamespacedConfigMap(await this.workspaceService.getCurrentNamespace(), configmap);
} catch (e) {
console.error('Failed to create gitconfig configmap. ' + e);
}
Expand All @@ -101,9 +89,8 @@ export class GitConfigurationController implements CheGitService {

private async isGitconfigConfigmapExists(): Promise<boolean> {
try {
const request = await this.cheK8SService
.makeApiClient(k8s.CoreV1Api)
.listNamespacedConfigMap(await this.workspaceService.getCurrentNamespace());
const client = await this.cheK8SService.makeApiClient(k8s.CoreV1Api);
const request = await client.listNamespacedConfigMap(await this.workspaceService.getCurrentNamespace());
return (
request.body.items.find(
configmap => configmap.metadata && configmap.metadata.name === GITCONFIG_CONFIGMAP_NAME
Expand All @@ -117,7 +104,7 @@ export class GitConfigurationController implements CheGitService {

private async createGitconfigConfigmapIfNeededAndUpdate(): Promise<void> {
await this.createGitconfigConfigmapIfNeeded();
const client = this.cheK8SService.makeApiClient(k8s.CoreV1Api);
const client = await this.cheK8SService.makeApiClient(k8s.CoreV1Api);
client.defaultHeaders = {
'Content-Type': k8s.PatchUtils.PATCH_FORMAT_STRATEGIC_MERGE_PATCH,
};
Expand All @@ -136,9 +123,11 @@ export class GitConfigurationController implements CheGitService {

private async updateUserGitconfigFromConfigmap(): Promise<void> {
try {
const request = await this.cheK8SService
.makeApiClient(k8s.CoreV1Api)
.readNamespacedConfigMap(GITCONFIG_CONFIGMAP_NAME, await this.workspaceService.getCurrentNamespace());
const client = await this.cheK8SService.makeApiClient(k8s.CoreV1Api);
const request = await client.readNamespacedConfigMap(
GITCONFIG_CONFIGMAP_NAME,
await this.workspaceService.getCurrentNamespace()
);
const content = request.body.data!.gitconfig;
fs.ensureFileSync(GIT_USER_CONFIG_PATH);
fs.writeFileSync(GIT_USER_CONFIG_PATH, content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import 'reflect-metadata';
import * as fs from 'fs-extra';
import * as path from 'path';

import { CheK8SService, WorkspaceService } from '@eclipse-che/theia-remote-api/lib/common';
import {
GIT_USER_CONFIG_PATH,
GitConfigurationController,
Expand All @@ -23,6 +22,8 @@ import {

import { CheTheiaUserPreferencesSynchronizer } from '@eclipse-che/theia-user-preferences-synchronizer/lib/node/che-theia-preferences-synchronizer';
import { Container } from 'inversify';
import { K8SServiceImpl } from '@eclipse-che/theia-remote-impl-k8s/lib/node/k8s-service-impl';
import { WorkspaceService } from '@eclipse-che/theia-remote-api/lib/common';

describe('Test GitConfigurationController', () => {
let container: Container;
Expand Down Expand Up @@ -50,7 +51,7 @@ describe('Test GitConfigurationController', () => {
container = new Container();
container.bind(CheTheiaUserPreferencesSynchronizer).toConstantValue(cheTheiaUserPreferencesSynchronizer);
container.bind(WorkspaceService).toConstantValue(workspaceService);
container.bind(CheK8SService).toConstantValue(k8sServiceMock);
container.bind(K8SServiceImpl).toConstantValue(k8sServiceMock);
container.bind(GitConfigurationController).toSelf().inSingletonScope();
gitConfigurationController = container.get(GitConfigurationController);
k8sServiceMakeApiClientMethod.mockReturnValue(coreV1ApiMock);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class K8sDevfileServiceImpl implements DevfileService {

async get(fromCustomObject?: boolean): Promise<Devfile> {
if (fromCustomObject) {
const customObjectsApi = this.k8SService.makeApiClient(k8s.CustomObjectsApi);
const customObjectsApi = await this.k8SService.makeApiClient(k8s.CustomObjectsApi);
const group = 'workspace.devfile.io';
const version = 'v1alpha2';

Expand All @@ -64,7 +64,7 @@ export class K8sDevfileServiceImpl implements DevfileService {

async getWorkspacePod(): Promise<V1Pod> {
// get workspace pod
const k8sCoreV1Api = this.k8SService.makeApiClient(k8s.CoreV1Api);
const k8sCoreV1Api = await this.k8SService.makeApiClient(k8s.CoreV1Api);
const labelSelector = `controller.devfile.io/devworkspace_id=${this.env.getWorkspaceId()}`;
const { body } = await k8sCoreV1Api.listNamespacedPod(
this.env.getWorkspaceNamespace(),
Expand Down Expand Up @@ -155,7 +155,7 @@ export class K8sDevfileServiceImpl implements DevfileService {

async patch(path: string, newValue: object): Promise<void> {
// Grab custom resource object
const customObjectsApi = this.k8SService.makeApiClient(k8s.CustomObjectsApi);
const customObjectsApi = await this.k8SService.makeApiClient(k8s.CustomObjectsApi);
const group = 'workspace.devfile.io';
const version = 'v1alpha2';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,74 @@ import * as path from 'path';
import { CheK8SService, K8SRawResponse } from '@eclipse-che/theia-remote-api/lib/common/k8s-service';

import { ApiType } from '@kubernetes/client-node';
import { Emitter } from '@theia/core';
import { injectable } from 'inversify';

const request = require('request');

@injectable()
export class K8SServiceImpl implements CheK8SService {
private kc: k8s.KubeConfig;
private onK8sUserUpdatedEmitter = new Emitter<boolean>();
private onK8sUserUpdatedEvent = this.onK8sUserUpdatedEmitter.event;
private isK8sUserUpdated: boolean = false;

constructor() {
const kubeconfig: k8s.KubeConfig = JSON.parse(
fs.readFileSync(path.resolve(os.homedir(), '.kube', 'config')).toString()
);
const tokenPath = path.resolve(os.homedir(), '.kube', 'token');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const k8sUser: any = kubeconfig.users.find(user => user.name === 'developer');
if (k8sUser) {
fs.ensureFileSync(tokenPath);
fs.writeFileSync(tokenPath, k8sUser.user.token);
}
const user = {
name: 'inClusterUser',
authProvider: {
name: 'tokenFile',
config: {
tokenFile: tokenPath,
},
},
};
const kubeconfigPath = path.resolve(os.homedir(), '.kube', 'config');
const tokenPath: string = path.resolve(os.homedir(), '.kube', 'token');
this.checkExistsWithTimeout(kubeconfigPath.toString(), 300000)
.then(() => {
const kubeconfig: k8s.KubeConfig = JSON.parse(fs.readFileSync(kubeconfigPath).toString());
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const kubeconfigUser: any = kubeconfig.users.find(user => user.name === 'developer');
if (kubeconfigUser) {
fs.writeFileSync(tokenPath, kubeconfigUser.user.token);
}
// this.kc.loadFromCluster();
this.kc.users[this.kc.users.findIndex(user => user.name === 'inClusterUser')].authProvider.config.tokenFile =
tokenPath;
this.isK8sUserUpdated = true;
this.onK8sUserUpdatedEmitter.fire(true);
})
.catch(() => {
this.onK8sUserUpdatedEmitter.fire(false);
// this.kc.loadFromCluster();
});
this.kc = new k8s.KubeConfig();
this.kc.loadFromCluster();
this.kc.users = [];
this.kc.addUser(user);
}

private checkExistsWithTimeout(filePath: string, timeout: number): Promise<void> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
watcher.close();
reject(new Error('File did not exists and was not created during the timeout.'));
}, timeout);

fs.ensureDirSync(path.resolve(os.homedir(), '.kube'));
const dir = path.dirname(filePath);
const basename = path.basename(filePath);
const watcher = fs.watch(dir, (eventType, filename) => {
if (eventType === 'rename' && filename === basename) {
clearTimeout(timer);
watcher.close();
resolve();
}
});
});
}

private async checkIfK8sUserUpdated(): Promise<void> {
if (!this.isK8sUserUpdated) {
return new Promise(resolve => {
this.onK8sUserUpdatedEvent(() => resolve());
});
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
sendRawQuery(requestURL: string, opts: any): Promise<K8SRawResponse> {
async sendRawQuery(requestURL: string, opts: any): Promise<K8SRawResponse> {
await this.checkIfK8sUserUpdated();
this.kc.applyToRequest(opts);
const cluster = this.kc.getCurrentCluster();
if (!cluster) {
Expand Down Expand Up @@ -79,7 +111,8 @@ export class K8SServiceImpl implements CheK8SService {
return this.kc;
}

makeApiClient<T extends ApiType>(apiClientType: new (server: string) => T): T {
async makeApiClient<T extends ApiType>(apiClientType: new (server: string) => T): Promise<T> {
await this.checkIfK8sUserUpdated();
return this.kc.makeApiClient(apiClientType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class K8sWorkspaceServiceImpl implements WorkspaceService {
public async stop(): Promise<void> {
// stopping the workspace is changing the started state to false

const customObjectsApi = this.k8SService.makeApiClient(k8s.CustomObjectsApi);
const customObjectsApi = await this.k8SService.makeApiClient(k8s.CustomObjectsApi);
const group = 'workspace.devfile.io';
const version = 'v1alpha2';
const namespace = this.env.getWorkspaceNamespace();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,11 @@ export class CheTheiaUserPreferencesSynchronizer {
await ensureDir(dirname(THEIA_USER_PREFERENCES_PATH));
let content = '';
try {
const request = await this.cheK8SService
.makeApiClient(k8s.CoreV1Api)
.readNamespacedConfigMap(
WORKSPACE_PREFERENCES_CONFIGMAP_NAME,
await this.workspaceService.getCurrentNamespace()
);
const client = await this.cheK8SService.makeApiClient(k8s.CoreV1Api);
const request = await client.readNamespacedConfigMap(
WORKSPACE_PREFERENCES_CONFIGMAP_NAME,
await this.workspaceService.getCurrentNamespace()
);
if (request.body && request.body.data && request.body.data[THEIA_PREFERENCES_KEY]) {
content = JSON.stringify(JSON.parse(request.body.data[THEIA_PREFERENCES_KEY]), undefined, 3);
}
Expand Down Expand Up @@ -114,7 +113,7 @@ export class CheTheiaUserPreferencesSynchronizer {
return;
}

const client = this.cheK8SService.makeApiClient(k8s.CoreV1Api);
const client = await this.cheK8SService.makeApiClient(k8s.CoreV1Api);
client.defaultHeaders = {
Accept: 'application/json',
'Content-Type': k8s.PatchUtils.PATCH_FORMAT_STRATEGIC_MERGE_PATCH,
Expand Down

0 comments on commit 9f92e3f

Please sign in to comment.