Skip to content
Snippets Groups Projects
Commit c8cd7888 authored by Alexandra's avatar Alexandra
Browse files

wip

parent a1736b79
No related branches found
No related tags found
No related merge requests found
Showing with 288 additions and 231 deletions
......@@ -4,7 +4,6 @@ export const KEYCLOAK_TOKEN_CREATE_URL =
'https://auth.dev.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/token';
export const KEYCLOAK_USER_INFO_URL =
'https://auth.dev.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/userinfo';
export const REGISTRY_MISSION_URL = 'registry.takima.io/deadlock/deadlock-challenges';
export const REJECT_UNAUTHORIZED = false;
export const ENABLE_AUTOMATIC_SAVE = true;
export const ENABLE_RECORDER_HTTP_SERVER = false;
......
......@@ -8,13 +8,14 @@ import ExtensionStore from './extensionStore';
import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
export default class CallApiService {
private callApi: AxiosInstance;
private axiosInstance: AxiosInstance;
constructor(
private keycloackConnection: KeycloakOAuth2DeviceFlowConnection,
private extensionStore: ExtensionStore,
private controller: Controller,
) {
this.callApi = axios.create({
this.axiosInstance = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
......@@ -26,7 +27,7 @@ export default class CallApiService {
}
initApiInterceptor() {
this.callApi.interceptors.request.use(
this.axiosInstance.interceptors.request.use(
async (config) => {
let accessToken = await this.extensionStore.getAccessToken();
if (accessToken && config.headers) {
......@@ -89,14 +90,14 @@ export default class CallApiService {
}
getGiteaPublicProperties(): Promise<GiteaPublicProperties> {
return this.callApi.get<GiteaPublicProperties>(`gitea`).then((res) => res.data);
return this.axiosInstance.get<GiteaPublicProperties>(`gitea`).then((res) => res.data);
}
getUserSshKey(): Promise<SshKeyPair> {
return this.callApi.put<SshKeyPair>(`users/gitea/keypair`).then((res) => res.data);
return this.axiosInstance.put<SshKeyPair>(`users/gitea/keypair`).then((res) => res.data);
}
getUser(): Promise<User> {
return this.callApi.get<User>(`auth`).then((res) => res.data);
return this.axiosInstance.get<User>(`auth`).then((res) => res.data);
}
}
......@@ -13,7 +13,7 @@ import QuickSetupView from '../view/quickSetupView';
import {CHOOSE_MISSION_WORKDIR_COMMAND, CommandHandler, OPEN_URL_IN_BROWSER_COMMAND} from './commandHandler';
import ExtensionStore from './extensionStore';
import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
import Mission from './mission';
import Mission from './mission/mission';
import CallApiService from './callApi.service';
import KeycloakOAuth2DeviceFlowConnectionVSCodeImpl from './keycloakOAuth2DeviceFlowConnectionVSCodeImpl';
import SshKeyManager from './sshKeyManager';
......@@ -45,16 +45,22 @@ export default class Controller {
this.init();
}
private async init() {
const that = this;
vscode.window.registerUriHandler({
handleUri(uri: vscode.Uri) {
const queryParams: URLSearchParams = new URLSearchParams(uri.query);
const action: string | null = queryParams.get('action');
const queryParams = new URLSearchParams(uri.query);
const action = queryParams.get('action');
log('Opening link', uri);
switch (action) {
case 'open-challenge':
const missionId = queryParams.get('missionId');
const missionVersion = queryParams.get('missionVersion');
if (!missionId || !missionVersion) {
//display smth
}
that.launchMission(queryParams.get('missionId'), queryParams.get('missionVersion'));
break;
......@@ -67,6 +73,7 @@ export default class Controller {
const exensionStorage = ExtensionStore.getInstance();
this.quickSetupView.isAlreadyConnected = !!(await exensionStorage.getAccessToken());
}
async chooseMissionWorkdir() {
const actualMissionWorkDir = this.extensionStore.getMissionWorkdir();
......@@ -95,8 +102,9 @@ export default class Controller {
public async createSshKeyPairIfNotExist() {
if (this.sshKeyManager.isSshKeyPairExist()) return;
const {publicKey, privateKey} = await this.callApiService.getUserSshKey();
this.sshKeyManager.createSshKeyFiles(publicKey, privateKey);
await this.sshKeyManager.createSshKeyFiles(publicKey, privateKey);
}
public async authenticate() {
......@@ -109,11 +117,12 @@ export default class Controller {
await this.createSshKeyPairIfNotExist();
this.quickSetupView.isAlreadyConnected = true;
}
public static openBrowserWithUrl(url: string) {
vscode.commands.executeCommand(OPEN_URL_IN_BROWSER_COMMAND.cmd, vscode.Uri.parse(url));
}
public async launchMission(missionId: string | null, missionVersion: string | null) {
public async launchMission(missionId: string, missionVersion: string) {
console.log('LAUCH MISSION : ' + missionId);
if (missionId && missionVersion) {
vscode.window.showInformationMessage(`vous lancez la mission ${missionId}`);
......@@ -126,9 +135,7 @@ export default class Controller {
if (!hadBeenConnected) {
await this.authenticate();
vscode.window.showInformationMessage('Nouvelle connexion validée');
} else {
vscode.window.showInformationMessage('Déjà connecté: session récupérée');
vscode.window.showInformationMessage('Connexion validée');
}
const user: User = await this.callApiService.getUser();
......@@ -147,7 +154,7 @@ export default class Controller {
'opening inside folder ' + this.extensionStore.getMissionWorkdir()! + '/' + missionId,
);
console.log('BEFORE SETUP');
await mission.setup({});
await mission.setup();
console.log('BEFORE open editor');
await mission.openEditorInFolder();
......@@ -155,4 +162,3 @@ export default class Controller {
vscode.commands.executeCommand(OPEN_QUICK_SETUP_COMMAND.cmd);
}
}
}
export interface DockerfileSpecific {
image?;
dockerFile?;
context?;
'build.args'?;
'build.target'?;
'build.cacheFrom'?;
containerEnv?;
containerUser?;
mounts?;
workspaceMount?;
workspaceFolder?;
runArgs?;
}
export interface Base {
name?;
forwardPorts?;
portsAttributes?;
otherPortsAttributes?;
remoteEnv?;
remoteUser?;
updateRemoteUserUID?;
userEnvProbe?;
overrideCommand?;
features?;
shutdownAction?;
}
export interface VSCodespecific {
extensions?;
settings?;
devPort?;
}
export interface LifecycleScripts {
initializeCommand?;
onCreateCommand?;
updateContentCommand?;
postCreateCommand?;
postStartCommand?;
postAttachCommand?;
waitFor?;
}
import {MissionResource} from "./Mission";
import {userSshKeyFolderPath} from "../config";
import ExtensionStore from "../extensionStore";
import {DockerfileSpecific, LifecycleScripts, VSCodespecific} from "./DevContainer";
const DOCKER_IMAGE_URL = 'registry.takima.io/deadlock/deadlock-challenges';
export function createDevContainerFile(mission: MissionResource) {
const hostBaseWorkDir = ExtensionStore.getInstance().getMissionWorkdir() ?? '';
const hostMissionDir = `${hostBaseWorkDir}/${mission.id}`
return fs.promises.writeFile(
this.hostMissionDevcontainerFileDir,
(() => {
const devcontainer: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts> = {
name: `deadlock-${this.params.missionId}`,
image: this.dockerImageURL,
extensions: ['Deadlock.deadlock-coding'],
remoteUser: 'deadlock',
mounts: [
`source=${userSshKeyFolderPath},target=/tmp/.ssh,type=bind,consistency=cached,readonly`,
`source=${this.userMissionConfigPath},target=/home/config/,type=bind,consistency=cached,readonly`,
'source=/etc/hosts,target=/etc/hosts,type=bind,consistency=cached,readonly',
],
userEnvProbe: 'interactiveShell',
settings: {
'terminal.integrated.defaultProfile.linux': 'bash',
'terminal.integrated.profiles.linux': {
bash: {
path: '/bin/bash',
},
},
},
overrideCommand: false,
shutdownAction: 'stopContainer',
workspaceMount: `source=${this.hostMissionMountDir},target=${this.remoteMissionDir},type=bind`,
workspaceFolder: `${this.remoteMissionDir}`,
onCreateCommand: `cp -R ${this.remoteGiteaWorkDir}/. ${this.remoteMissionDir}`,
runArgs: ['--privileged'],
...options,
};
return JSON.stringify(devcontainer, null, 2);
})(),
);
}
export class MissionResource {
constructor(public missionDir: string, public id: string, public version: string) {
}
}
......@@ -2,11 +2,11 @@ import { exec as _exec } from 'child_process';
import * as fs from 'fs';
import * as util from 'util';
import * as vscode from 'vscode';
import { error as err, log } from '../recorder/utils';
import ExtensionStore from './extensionStore';
import { userSshKeyFolderPath } from './config';
import { GiteaPublicProperties } from '../model/giteaPublicProperties.model';
import { User, UserChallengeJson } from '../model/user.model';
import { error as err, log } from '../../recorder/utils';
import ExtensionStore from '../extensionStore';
import { userSshKeyFolderPath } from '../config';
import { GiteaPublicProperties } from '../../model/giteaPublicProperties.model';
import { User, UserChallengeJson } from '../../model/user.model';
/**
* {@link https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback}
......@@ -47,12 +47,6 @@ export default class Mission {
this.giteaPublicProperties = giteaPublicProperties;
}
static async pullImage(url) {
const { stdout, stderr } = await exec(`docker pull ${url}`);
log(stdout);
err(stderr);
}
public async setup(options?: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts>) {
await fs.promises.mkdir(this.hostMissionDevcontainerDir, { recursive: true });
await fs.promises.mkdir(this.hostMissionMountDir, { recursive: true });
......@@ -131,47 +125,4 @@ export default class Mission {
export function getDockerImageURL(base, missionId, missionVersion) {
return `${base}/${missionId}:${missionVersion}`;
}
interface DockerfileSpecific {
image?;
dockerFile?;
context?;
'build.args'?;
'build.target'?;
'build.cacheFrom'?;
containerEnv?;
containerUser?;
mounts?;
workspaceMount?;
workspaceFolder?;
runArgs?;
}
interface Base {
name?;
forwardPorts?;
portsAttributes?;
otherPortsAttributes?;
remoteEnv?;
remoteUser?;
updateRemoteUserUID?;
userEnvProbe?;
overrideCommand?;
features?;
shutdownAction?;
}
interface VSCodespecific {
extensions?;
settings?;
devPort?;
}
interface LifecycleScripts {
initializeCommand?;
onCreateCommand?;
updateContentCommand?;
postCreateCommand?;
postStartCommand?;
postAttachCommand?;
waitFor?;
}
......@@ -4,12 +4,10 @@ import { error } from './recorder/utils';
import { DepNodeProvider } from './theia/deadlockPanel';
import UserConfigTheia from './theia/userConfigTheia';
export const userConfig = new UserConfigTheia();
export async function activate(context: vscode.ExtensionContext) {
vscode.window.showInformationMessage('Bienvenue sur Deadlock!');
const controller = new Controller(context);
extensionContext = context;
const workspaceFolders = vscode.workspace.workspaceFolders?.toString() ?? '';
if (!workspaceFolders) vscode.window.showInformationMessage('Pas de répertoires ouverts');
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment