Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development of health route #39

Merged
merged 7 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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