Removes needs of port forwarding + francisation

This commit is contained in:
Gabriel Matte 2024-11-12 02:33:19 -05:00
parent 806935e48c
commit 35d6724d87
5 changed files with 112 additions and 50 deletions

View file

@ -34,7 +34,7 @@ class RoomsController {
return new KubernetesRoomProvider(options);
*/
default:
throw new Error(`Unknown provider type: ${type}`);
throw new Error(`Type d'approvisionement inconnu: ${type}`);
}
}

View file

@ -32,7 +32,7 @@ class RoomRepository {
await this.init();
const existingRoom = await this.collection.findOne({ id: room.id });
if (existingRoom) {
throw new Error(`Room already exists with id: ${room.id}`);
throw new Error(`Érreur: la salle ${room.id} existe déja`);
}
const returnedId = await this.collection.insertOne(room);
return await this.collection.findOne({ _id: returnedId.insertedId });
@ -42,7 +42,7 @@ class RoomRepository {
await this.init();
const existingRoom = await this.collection.findOne({ id: id });
if (!existingRoom) {
console.warn(`Room with id ${id} not found.`);
console.warn(`La sale avec l'identifiant ${id} n'as pas été trouvé.`);
return null;
}
return existingRoom;
@ -77,7 +77,7 @@ class RoomRepository {
await this.init();
const result = await this.collection.deleteOne({ id: id });
if (result.deletedCount === 0) {
console.warn(`Room with id ${id} not found for deletion.`);
console.warn(`La salle ${id} n'as pas été trouvée pour éffectuer sa suppression.`);
return false;
}
return true;

View file

@ -13,30 +13,32 @@ class BaseRoomProvider {
this.roomRepository = roomRepository;
this.quiz_docker_image = process.env.QUIZROOM_IMAGE || "evaluetonsavoir-quizroom";
this.quiz_docker_port = process.env.QUIZROOM_PORT || 4500;
this.quiz_expose_port = process.env.QUIZROOM_EXPOSE_PORT || false;
}
async createRoom(roomId, options) {
throw new Error("Method not implemented");
throw new Error("Fonction non-implantée - classe abstraite");
}
async deleteRoom(roomId) {
throw new Error("Method not implemented");
throw new Error("Fonction non-implantée - classe abstraite");
}
async getRoomStatus(roomId) {
throw new Error("Method not implemented");
throw new Error("Fonction non-implantée - classe abstraite");
}
async listRooms() {
throw new Error("Method not implemented");
throw new Error("Fonction non-implantée - classe abstraite");
}
async cleanup() {
throw new Error("Method not implemented");
throw new Error("Fonction non-implantée - classe abstraite");
}
async syncInstantiatedRooms(){
throw new Error("Method not implemented");
throw new Error("Fonction non-implantée - classe abstraite");
}
async updateRoomsInfo() {
@ -58,7 +60,8 @@ class BaseRoomProvider {
await this.roomRepository.update(room);
} catch (error) {
console.error(`Error updating room ${room.id}:`, error);
room.mustBeCleaned = true;
await this.roomRepository.update(room);
}
}
}

View file

@ -6,11 +6,12 @@ class DockerRoomProvider extends BaseRoomProvider {
constructor(config, roomRepository) {
super(config, roomRepository);
const dockerSocket = process.env.DOCKER_SOCKET || "/var/run/docker.sock";
this.docker = new Docker({ socketPath: dockerSocket });
this.docker_network = 'evaluetonsavoir_quiz_network';
}
async syncInstantiatedRooms(){
async syncInstantiatedRooms() {
let containers = await this.docker.listContainers();
containers = containers.filter(container => container.Image === this.quiz_docker_image);
@ -19,28 +20,28 @@ class DockerRoomProvider extends BaseRoomProvider {
for (let container of containers) {
const container_name = container.Names[0].slice(1);
if (!container_name.startsWith("room_")) {
console.warn(`Container ${container_name} does not follow the room naming convention, removing it.`);
console.warn(`Le conteneur ${container_name} ne suit pas la convention de nommage, il sera supprimé.`);
const curContainer = this.docker.getContainer(container.Id);
await curContainer.stop();
await curContainer.remove();
containerIds.delete(container.Id);
console.warn(`Container ${container_name} removed.`);
console.warn(`Le conteneur ${container_name} a été supprimé.`);
}
else{
console.warn(`Found orphan container : ${container_name}`);
else {
console.warn(`Conteneur orphelin trouvé : ${container_name}`);
const roomId = container_name.slice(5);
const room = await this.roomRepository.get(roomId);
if (!room) {
console.warn(`container not our rooms database`);
console.warn(`Le conteneur n'est pas dans notre base de données.`);
const containerInfo = await this.docker.getContainer(container.Id).inspect();
const containerIP = containerInfo.NetworkSettings.Networks.evaluetonsavoir_quiz_network.IPAddress;
const host = `${containerIP}:4500`;
console.warn(`Creating room ${roomId} in our database - host : ${host}`);
console.warn(`Création de la salle ${roomId} dans notre base de donnée - hôte : ${host}`);
return await this.roomRepository.create(new Room(roomId, container_name, host));
}
console.warn(`room ${roomId} already in our database`);
console.warn(`La salle ${roomId} est déjà dans notre base de données.`);
}
}
}
@ -48,23 +49,81 @@ class DockerRoomProvider extends BaseRoomProvider {
async createRoom(roomId, options) {
const container_name = `room_${roomId}`;
const containerConfig = {
Image: this.quiz_docker_image,
name: container_name,
HostConfig: {
NetworkMode: "evaluetonsavoir_quiz_network"
},
Env: [...options.env || [], `ROOM_ID=${roomId}`]
};
try {
const containerConfig = {
Image: this.quiz_docker_image,
name: container_name,
HostConfig: {
NetworkMode: this.docker_network,
RestartPolicy: {
Name: 'unless-stopped'
}
},
Env: [
`ROOM_ID=${roomId}`,
`PORT=${this.quiz_docker_port}`,
...(options.env || [])
]
};
const container = await this.docker.createContainer(containerConfig);
await container.start();
if (this.quiz_expose_port) {
containerConfig.ExposedPorts = {
[`${this.quiz_docker_port}/tcp`]: {}
};
containerConfig.HostConfig.PortBindings = {
[`${this.quiz_docker_port}/tcp`]: [{ HostPort: '' }] // Empty string for random port
};
}
const containerInfo = await container.inspect();
const containerIP = containerInfo.NetworkSettings.Networks.evaluetonsavoir_quiz_network.IPAddress;
const container = await this.docker.createContainer(containerConfig);
await container.start();
const host = `${containerIP}:${this.quiz_docker_port ?? "4500"}`;
return await this.roomRepository.create(new Room(roomId, container_name, host, 0));
const containerInfo = await container.inspect();
const networkInfo = containerInfo.NetworkSettings.Networks[this.docker_network];
if (!networkInfo) {
throw new Error(`Le conteneur n'as pu se connecter au réseau: ${this.docker_network}`);
}
const containerIP = networkInfo.IPAddress;
const host = `http://${containerIP}:${this.quiz_docker_port}`;
let health = false;
let attempts = 0;
const maxAttempts = 15;
while (!health && attempts < maxAttempts) {
try {
const response = await fetch(`${host}/health`, {
timeout: 1000
});
if (response.ok) {
health = true;
console.log(`Le conteneur ${container_name} est tombé actif en ${attempts + 1} tentatives`);
} else {
throw new Error(`Health check failed with status ${response.status}`);
}
} catch (error) {
attempts++;
console.log(`Attente du conteneur: ${container_name} (tentative ${attempts}/${maxAttempts})`);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
if (!health) {
console.error(`Container ${container_name} failed health check after ${maxAttempts} attempts`);
await container.stop();
await container.remove();
throw new Error(`Room ${roomId} did not respond within acceptable timeout`);
}
return await this.roomRepository.create(new Room(roomId, container_name, host, 0));
} catch (error) {
console.error(`Échec de la création de la salle ${roomId}:`, error);
throw error;
}
}
@ -79,18 +138,18 @@ class DockerRoomProvider extends BaseRoomProvider {
if (containerInfo) {
await container.stop();
await container.remove();
console.log(`Container for room ${roomId} stopped and removed.`);
console.log(`Le conteneur pour la salle ${roomId} a été arrêté et supprimé.`);
}
} catch (error) {
if (error.statusCode === 404) {
console.warn(`Container for room ${roomId} not found, proceeding to delete room record.`);
console.warn(`Le conteneur pour la salle ${roomId} n'as pas été trouvé, la salle sera supprimée de la base de données.`);
} else {
console.error(`Error handling container for room ${roomId}:`, error);
throw new Error("Failed to delete room");
console.error(`Erreur pour la salle ${roomId}:`, error);
throw new Error("La salle :${roomId} n'as pas pu être supprimée.");
}
}
console.log(`Room ${roomId} deleted from repository.`);
console.log(`La salle ${roomId} a été supprimée.`);
}
async getRoomStatus(roomId) {
@ -116,7 +175,7 @@ class DockerRoomProvider extends BaseRoomProvider {
return updatedRoomInfo;
} catch (error) {
if (error.statusCode === 404) {
console.warn(`Container for room ${roomId} not found, room status set to "terminated".`);
console.warn(`Le conteneur pour la salle ${roomId} n'as pas été trouvé, il sera mis en état "terminé".`);
const terminatedRoomInfo = {
...room,
status: "terminated",
@ -131,7 +190,7 @@ class DockerRoomProvider extends BaseRoomProvider {
await this.roomRepository.update(terminatedRoomInfo);
return terminatedRoomInfo;
} else {
console.error(`Error retrieving container status for room ${roomId}:`, error);
console.error(`Une érreur s'est produite lors de l'obtention de l'état de la salle ${roomId}:`, error);
return null;
}
}
@ -149,7 +208,7 @@ class DockerRoomProvider extends BaseRoomProvider {
try {
await this.deleteRoom(room.id);
} catch (error) {
console.error(`Error cleaning up room ${room.id}:`, error);
console.error(`Érreur lors du néttoyage de la salle ${room.id}:`, error);
}
}
}
@ -163,7 +222,7 @@ class DockerRoomProvider extends BaseRoomProvider {
const curContainer = this.docker.getContainer(container.Id);
await curContainer.stop();
await curContainer.remove();
console.warn(`Orphan container ${container.Names[0]} removed.`);
console.warn(`Conteneur orphelin ${container.Names[0]} supprimé.`);
}
}
}

View file

@ -9,7 +9,7 @@ router.get("/", async (req, res)=> {
const data = await roomsController.listRooms();
res.json(data);
} catch (error) {
res.status(500).json({ error: "Failed to list rooms" });
res.status(500).json({ error: "Échec de listage des salle" });
}
});
@ -20,7 +20,7 @@ router.post("/", async (req, res) => {
res.json(data);
} catch (error) {
console.log(error);
res.status(500).json({ error: "Failed to create room :" + error });
res.status(500).json({ error: "Échec de la création de salle :" + error });
}
});
@ -29,7 +29,7 @@ router.put("/:id", async (req, res) => {
const data = await roomsController.updateRoom(req.params.id);
res.json(data);
} catch (error) {
res.status(500).json({ error: "Failed to update rooms" });
res.status(500).json({ error: "Échec de la mise a jour de salle : "+error });
}
});
@ -38,7 +38,7 @@ router.delete("/:id", async (req, res) => {
const data = await roomsController.deleteRoom(req.params.id);
res.json(data);
} catch (error) {
res.status(500).json({ error: `Failed to delete room` });
res.status(500).json({ error: `Échec de suppression de la salle: `+error });
}
});
@ -47,7 +47,7 @@ router.get("/:id", async (req, res) => {
const data = await roomsController.getRoomStatus(req.params.id);
res.json(data);
} catch (error) {
res.status(500).json({ error: "Failed to list room infos" });
res.status(500).json({ error: "Impossible d'afficher les informations de la salle: " + error });
}
});