Skip to content

Commit

Permalink
Development of health route (#39)
Browse files Browse the repository at this point in the history
* wip: creation of health objects/algo

* [test]: maj de request

* wip: it works for sameProcess services

* works for newProcesses

* add type inside response, update tests and doc

* [fix] delete read message

* [doc] fix changelog version
  • Loading branch information
lgrd committed Mar 9, 2023
1 parent c971ecd commit 43d8f56
Show file tree
Hide file tree
Showing 29 changed files with 994 additions and 88 deletions.
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## 2.1.0

CHANGED:
- La documentation de l'API d'administration a été grandement enrichie.
- La route /health a une réponse plus complète et est vraiment codée pour prendre en compte l'état de chaque service et chaque source disponibles.

## 2.0.0

ADDED:
Expand Down
1 change: 1 addition & 0 deletions docker/dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ services:
command : "npm run debug -- --ROAD2_CONF_FILE=../config/road2.json"
ports:
- 8080:8080
- 8079:8079
- 9229:9229
- 9230:9230
- 443:443
Expand Down
3 changes: 3 additions & 0 deletions documentation/test/integration/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les
- operation (parameter)
- resourceParameter (parameter)
- serverManager (server, ExpressJS, log4js, fs, assert)
- healthRequest (request)
- healthResponse (response)

- Deuxième niveau:
- routeRequest (request, point)
Expand Down Expand Up @@ -123,6 +125,7 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les
- Router()
- router.use()
- json()
- set

- got
- utils/httpQuery.js
Expand Down
109 changes: 109 additions & 0 deletions src/js/administrator/administrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const assert = require('assert').strict;
const serverManager = require('../server/serverManager');
const serviceManager = require('../service/serviceManager');
const apisManager = require('../apis/apisManager');
const HealthResponse = require('../responses/healthResponse');

// Création du LOGGER
const LOGGER = log4js.getLogger("ADMINISTRATOR");
Expand Down Expand Up @@ -324,6 +325,9 @@ module.exports = class Administrator {
// Application Express
let administrator = express();

// Stockage de l'instance Administrator dans l'app expressJS afin que les informations soient accessibles par les requêtes
administrator.set("administrator", this);

// Gestion des en-têtes avec helmet selon les préconisations d'ExpressJS
administrator.use(helmet());

Expand Down Expand Up @@ -474,6 +478,111 @@ module.exports = class Administrator {

}

/**
*
* @function
* @name computeHealthRequest
* @description Gestion de la requête d'état du serveur
* Cette fonction ne suit pas le m$eme chemin que les autres car elle est globale
* et ne concerne pas un service particulier
* Elle renvoit une healthResponse complète et c'est au niveau de l'API qu'on peut la modifier
* @param {HealthRequest} healthRequest - Instance de la classe HealthRequest
*
*/

async computeHealthRequest(healthRequest) {

LOGGER.info("computeHealthRequest...");

let gotOrange = false;
let gotRed = false;

let healthResponse = new HealthResponse();

// Étant donné que l'administrateur est en train de répondre, on le met au vert
// À voir s'il y a des fonctionnalités qui pourraient le mettre à l'orange ou au rouge
// Dans ce cas là, il faudra que l'administrator ait un attribut d'état et que celui-ci soit lu dans cette fonction
healthResponse.adminState = "green";

// Pour chaque service, on demande au serviceManager l'état du service
for (let i = 0; i < this._configuration.administration.services.length; i++) {

let curServiceId = this._configuration.administration.services[i].id;
LOGGER.debug("Demande de l'état du service : " + curServiceId);

// Le passage potentiel par IPC fait perdre les méthodes donc dans la suite, on est obligé de prendre les attributs avec _
let curHealthResponse = await this._serviceManager.computeRequest(curServiceId, healthRequest);

if (curHealthResponse._type !== "healthResponse") {
// Ce n'est pas normal, on renvoit une erreur pour ce service
// et on met le flag rouge
LOGGER.error("Le service " + curServiceId + " n'a pas donné de réponse du bon type");
gotRed = true;
healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"});
continue;
}

if (!curHealthResponse._serviceStates[0]) {
// Ce n'est pas normal, on renvoit une erreur pour ce service
// et on met le flag rouge
LOGGER.error("Le service " + curServiceId + " n'a pas donné de réponse");
gotRed = true;
healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"});
continue;
}

if (!curHealthResponse._serviceStates[0].state) {
// Ce n'est pas normal, on renvoit une erreur pour ce service
// et on met le flag rouge
LOGGER.error("Le service " + curServiceId + " n'a pas donné d'état");
gotRed = true;
healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"});
continue;
}

// Pour la suite, on note la présence d'orange et de rouge dans les services
if (curHealthResponse._serviceStates[0].state === "orange") {
LOGGER.debug("Le service " + curServiceId + " est orange");
gotOrange = true;
} else if (curHealthResponse._serviceStates[0].state === "red") {
LOGGER.debug("Le service " + curServiceId + " est red");
gotRed = true;
} else if (curHealthResponse._serviceStates[0].state === "green") {
// Tout va bien, rien à faire
LOGGER.debug("Le service " + curServiceId + " est green");
} else {
// Cela ne devrait pas arriver, on renvoit une erreur pour ce service
// et on met le flag rouge
LOGGER.error("Le service " + curServiceId + " est dans un état inconnu");
gotRed = true;
healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"});
continue;
}

// On stocke le retour de ce service
curHealthResponse._serviceStates[0].id = curServiceId;
healthResponse.serviceStates.push(curHealthResponse._serviceStates[0]);

}

// En fonction des états définis précédemment, on va définir l'état global
// Pour faire simple :
// - global est orange si un des services est orange
// - global est rouge si un des services est rouge
if (gotRed) {
healthResponse.globalState = "red";
} else {
if (gotOrange) {
healthResponse.globalState = "orange";
} else {
healthResponse.globalState = "green";
}
}

return healthResponse;

}

}


83 changes: 83 additions & 0 deletions src/js/apis/admin/1.0.0/controller/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'use strict';

const errorManager = require('../../../../utils/errorManager');
const log4js = require('log4js');
const HealthRequest = require('../../../../requests/healthRequest');

var LOGGER = log4js.getLogger("CONTROLLER");

module.exports = {

/**
*
* @function
* @name checkHealthParameters
* @description Vérification des paramètres d'une requête sur /health
* @param {object} parameters - ensemble des paramètres de la requête
* @return {object} HealthRequest - Instance de la classe HealthRequest
*
*/

checkHealthParameters: function(parameters) {

LOGGER.debug("checkHealthParameters()");

// Il n'y a aucun paramètre obligatoire donc on peut créer l'objet request
let healthRequest = new HealthRequest();

// Verbose
if (parameters.verbose) {

LOGGER.debug("user verbose:");
LOGGER.debug(parameters.verbose);

if (parameters.verbose === "true") {
healthRequest.verbose = true;
} else if (parameters.verbose === "false") {
healthRequest.verbose = false;
} else {
throw errorManager.createError(" Parameter 'verbose' is invalid: value should be 'true' or 'false'", 400);
}

} else {
// paramètre non obligatoire
LOGGER.debug("default verbose used: false");
}

return healthRequest;

},

/**
*
* @function
* @name writeHealthResponse
* @description Ré-écriture de la réponse pour une requête sur /health
* @param {object} HealthRequest - Instance de la classe HealthRequest
* @param {object} HealthResponse - Instance de la classe HealthResponse
* @return {object} userResponse - Réponse envoyée à l'utilisateur
*
*/

writeHealthResponse: function(healthRequest, healthResponse) {

let userResponse = {};

LOGGER.debug("writeHealthResponse()");

userResponse.state = healthResponse.globalState;

if (!healthRequest.verbose) {
return userResponse;
}

userResponse.administrator = {};
userResponse.administrator.state = healthResponse.adminState;

userResponse.services = healthResponse.serviceStates;

return userResponse;

}

}
31 changes: 26 additions & 5 deletions src/js/apis/admin/1.0.0/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const express = require('express');
const log4js = require('log4js');
const packageJSON = require('../../../../../package.json');
const controller = require('./controller/controller');

var LOGGER = log4js.getLogger("ADMIN");
var router = express.Router();
Expand Down Expand Up @@ -32,18 +33,38 @@ router.route("/version")

// Health
// Pour avoir l'état du service
// TODO: implémenter une véritable vérification de l'état
router.route("/health")

.get(async function(req, res, next) {

LOGGER.debug("requete GET sur /admin/1.0.0/health?");
LOGGER.debug(req.originalUrl);

res.set('content-type', 'application/json');
res.status(200).json({
"state": "green"
});
// On récupère l'instance d'Administrator pour répondre aux requêtes
let administrator = req.app.get("administrator");

// on récupère l'ensemble des paramètres de la requête
let parameters = req.query;
LOGGER.debug(parameters);

try {

// Vérification des paramètres de la requête
const healthRequest = controller.checkHealthParameters(parameters);
LOGGER.debug(healthRequest);
// Envoie au service et récupération de l'objet réponse
const healthResponse = await administrator.computeHealthRequest(healthRequest);
LOGGER.debug(healthResponse);
// Formattage de la réponse
const userResponse = controller.writeHealthResponse(healthRequest, healthResponse);
LOGGER.debug(userResponse);

res.set('content-type', 'application/json');
res.status(200).json(userResponse);

} catch (error) {
return next(error);
}

});

Expand Down
54 changes: 54 additions & 0 deletions src/js/requests/healthRequest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

const Request = require('./request');

/**
*
* @class
* @name healthRequest
* @description Classe modélisant une requête d'état du serveur Road2.
*
*/

module.exports = class healthRequest extends Request {

/**
*
* @function
* @name constructor
* @description Constructeur de la classe healthRequest
*
*/

constructor() {

super("health","healthRequest");

this._verbose = false;

}

/**
*
* @function
* @name get verbose
* @description Récupérer le caractère verbeux de la requête
*
*/
get verbose() {
return this._verbose;
}

/**
*
* @function
* @name set verbose
* @description Attribuer le caractère verbeux de la requête.
* @param {Boolean}
*
*/
set verbose(v) {
this._verbose = v;
}

}
Loading

0 comments on commit 43d8f56

Please sign in to comment.