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

Add status.js and its tests #238

Merged
merged 1 commit into from Jan 9, 2018
Merged

Conversation

AnkshitJain
Copy link
Collaborator

@prasadtalasila Please review the commit and suggest the necessary changes. I have included a environmentCheck.js file in /util directory that checks whether an environment variable is defined or not.
This module can be used in other js modules as well.

Copy link
Member

@prasadtalasila prasadtalasila left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnkshitJain please see the suggested changes

};

module.exports = {
check,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comma at the end is not required.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The airbnb config file that we extend for eslint has this convention. The rule at eslint says we should be consistent in what we intend to follow in this case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good.

@@ -13,13 +13,15 @@ var http = require('http');
var bodyParser = require('body-parser');
var fs = require('fs');
var sys = require('sys');
var exec = require('child_process').exec;
var { exec } = require('child_process');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please fix the new codeclimate issues. The apriori issues can be left alone.

@@ -17,7 +18,8 @@
],
"author": "Rajat Agarwal",
"contributors": [
"Tejas Sangol", "Gaurav Narula"
"Tejas Sangol",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add your name here

resolve(undefined);
}

const resultJson = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per line-34 of logger.js, we are to use three properties in a json file for node identity. The properties are: role, hostname and port. If we use the same three properties here as well, we would be consistent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another advantage of receiving the role of a node is that the status.js module becomes independent of its ties to execution nodes.

const logger = new Logger(loggerConfig);

function getComponentStatus(node) {
return new Promise((resolve) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The closure object is too long and has too many initializations. Is it possible to split this object into two smaller objects?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A coding rule known as step down rule from Robert Martin's Clean Code book will certainly help get a good structure. In step down rule, if f2() calls f1(), then code for f2() comes first in the file followed by f1().

@AnkshitJain
Copy link
Collaborator Author

@prasadtalasila Please have a look at the updated commit.

Copy link
Member

@prasadtalasila prasadtalasila left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnkshitJain please see the suggested changes.

@@ -1,22 +1,27 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of uniformity, it is better to change all the configuration files to have complete node identities. There is going to be some redundancy in configuration which can be taken care of later during code refactoring.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you suggest making changes to all the execution node configuration files i.e. conf.json and scores.json?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also to the configuration files of main_server as well.


var job_queue = [];
if(process.env.mode !== "TESTING")
{
server.listen(nodes_data.host_port.port);
console.log("Listening at "+nodes_data.host_port.port);
server.listen(nodes_data.load_balancer.port);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please fix the code quality issues raised here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check doesn't show any new errors here. Can you tell what the error is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may need to install the codeclimate plugin on chrome / chromium browser to have the codeclimate issues show up on the PR page.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect 4 errors here:

  1. Opening curly brace does not appear on the same line as controlling statement.
  2. Strings must use single quote.
  3. Unexpected string concatenation.
  4. Infix operators must be spaced.
    These are previously existing quality errors, and I haven't tried to fix them. The only reason this line has a change is because the host name property was added there and the object in the configuration file nodes_data_config.json had the property name changed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more issues exist. I am not able to copy paste the consolidated list.

lbStatus.status = 'up';
status.checkStatus((result) => {
//since the request is being processed, it is assumed that load balancer is up
const statusJson = result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fine, but there is a better library named lodash that is good at manipulating json objects. You might want to use it.

@@ -0,0 +1,102 @@
/* eslint import/no-dynamic-require: 0 */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this module can make use of lodash module to simplify the logic. Please see if you can use it to reduce the code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be its just me, but the code of status.js module does not look readable to me. Is there a way to simplify the code of status.js?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the lodash vs es6 debate. Its definitely worth using lodash in the project. I would rather introduce lodash more strategically by identifying the features of lodash that help us, note them down for use during refactoring. Nothing much to do now.

@AnkshitJain
Copy link
Collaborator Author

@prasadtalasila Please have a look at the updated commit.

Copy link
Member

@prasadtalasila prasadtalasila left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnkshitJain please check the code review comments.

"hostname": "localhost",
"port": "8086"
}
],
"server_info": {
"role": "main_server",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The database component would have role property too. Its value is to be set to database.

@@ -150,6 +150,7 @@
- "../log/load_balancer:/log"
env:
LOGGERCONFIG: "/etc/util/logger.json"
LBCONFIG: "/etc/load_balancer/nodes_data_conf.json"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be better to set the configuration environment variables for all the components.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LOGGERCONFIG variable would be used by all the components and is included as an environment variable for main_server, load_balancer and execution_nodes containers. The same cannot be said for the LBCONFIG variable. It has the node details and that is not required by the other containers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not ask for adding LBCONFIG environment variable to other containers. I suggested adding mainserver configuration variable to mainserver, execution node configuration variable to execution node. Done that way, there would not be any code replacements in the tests.


const logger = new Logger(loggerConfig);

const getComponentResponse = function getComponentResponse(node, resolve) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that the resolve() callback has been put in place for testability. Does the callback serve any other purpose?

this.nodes = nodes;
this.promises = [];
for (let i = 0; i < this.nodes.length; i += 1) {
this.promises.push(getComponentStatus(this.nodes[i]));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting the invocation of getComponentStatus() only in the constructor will result in staus update at the time of object creation. This is not the desirable behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At best, the code can resolve status of each component only once.


var job_queue = [];
if(process.env.mode !== "TESTING")
{
server.listen(nodes_data.host_port.port);
console.log("Listening at "+nodes_data.host_port.port);
server.listen(nodes_data.load_balancer.port);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more issues exist. I am not able to copy paste the consolidated list.

@@ -0,0 +1,102 @@
/* eslint import/no-dynamic-require: 0 */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the lodash vs es6 debate. Its definitely worth using lodash in the project. I would rather introduce lodash more strategically by identifying the features of lodash that help us, note them down for use during refactoring. Nothing much to do now.

resolve('down');
} else if (body.toString() === 'true') {
logger.debug(`Node at ${node.hostname}:${node.port} is up and running.`);
resolve('up');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the name of the callback function consistent with the nomeclature of promises?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the promises callback are resolve and reject.

@AnkshitJain
Copy link
Collaborator Author

@prasadtalasila The suggested structure of having the Status class before the module private methods gives an error according to our eslint conventions. Should this error be ignored or we stick with having method declarations at the top?

@prasadtalasila
Copy link
Member

@AnkshitJain Then lets follow the established conventions of ES6. Let's ignore the step down rule for now.

@prasadtalasila
Copy link
Member

@AnkshitJain async/await looks good. Please use the same.

@AnkshitJain
Copy link
Collaborator Author

@prasadtalasila Please have a look at the updated commit. Code climate gives an error related to the code complexity. Initially when the method getComponentStatus was in two separate parts, it gave 2 errors saying the code was similar. Can you suggest what can be done in this case?

@prasadtalasila
Copy link
Member

@AnkshitJain Please modify the README.md file by updating the codeclimate badge to the following code.

[![Maintainability](https://api.codeclimate.com/v1/badges/e1668db2aeb3196311bc/maintainability)](https://codeclimate.com/github/AutolabJS/AutolabJS/maintainability)

Right next to codeclimate code quality badge, you can put the code coverage badge.

[![Test Coverage](https://api.codeclimate.com/v1/badges/e1668db2aeb3196311bc/test_coverage)](https://codeclimate.com/github/AutolabJS/AutolabJS/test_coverage)

Right now, this badge shows a question mark until we put in a code coverage tool. That's okay.

Copy link
Member

@prasadtalasila prasadtalasila left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnkshitJain I am yet to complete the code review. In the mean while, please go through the following comments.


const logger = new Logger(loggerConfig);

async function getComponentStatus(node) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the following code structure? (BTW, there is a compilation error in the code snippet, please only use the idea, not the actual code)

function isValidNode(node) {
        hostname = node.hostname;
        port = node.port;
        role = node.role;

        if (hostname === undefined || port === undefined || role == undefined)
                return false;
        else
                return true;
}

async function getComponentStatus(node) {
        if (!isValidNode(node))
                throw new InvalidNodeException();

        const resultJson = Object.assign({}, node);
        try {
                const body = await request.get(`https://${node.hostname}:${node.port}/connectionCheck`);
                if (body.toString() === 'true') {
                        logger.debug(`Node at ${node.hostname}:${node.port} is up and running.`);
                resultJson.status = 'up';
            }
        } catch (err) {
                logger.error(`Error connecting to ${node.hostname}:${node.port}`);
                logger.error(err);
                resultJson.status = 'down';
}

You also need to define InvalidNodeException class. I see the value in putting both isValidNode() and InvalidNodeException in a module inside util/. That way, other components can cross check their configuration values as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What properties wil the error class would require apart from an error message?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just the error status message is sufficient.

return resultJson;
}

async function getPromises(nodes) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous name of this function sounds better. getPromises() name is too generic and unintuitive.

Path in docker containers: "/etc/main_server/APIKeys.json"
*/

check('MSCONFIG');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check function is checking the definition of the environment variable. It can also check for the existence of the file and the read permissions. Once the existence and permissions checks pass, we can subject the configuration file to validity check by iterating the isNodeValid() function over all the entries in the configuration file.

This is just an idea. Please suggest alternatives and improvements.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnkshitJain
The scope of the above suggestion is very big. I have opened a new issue #250 to keep track of the suggestion. Let's wrap up this PR with the existing check() functionality.

Copy link
Member

@prasadtalasila prasadtalasila left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnkshitJain a few more suggestions.

@@ -0,0 +1,9 @@
const check = function check(variable) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are missing for this function. You can also think of putting in the isValidNode() and InValidNodeException in this module itself.

@prasadtalasila
Copy link
Member

@AnkshitJain I am done with my review. I don't have any further suggestions to make. Everything else looks good for now.

@AnkshitJain
Copy link
Collaborator Author

AnkshitJain commented Nov 30, 2017

@prasadtalasila I have modified the method name getPromises and added the isValidNode method in status.js. I will add tests for environmentCheck.js along with the work concerning the custom error class. Please have a look at the updated commit.

@prasadtalasila
Copy link
Member

@AnkshitJain the latest commit looks good.

@prasadtalasila
Copy link
Member

@AnkshitJain please provide a tentative timeline to finish up with the pending work on this branch.

@AnkshitJain
Copy link
Collaborator Author

@prasadtalasila Only the tests of environmentCheck.js are remaining, which I plan to add along with the custom error class. The tests will require adding some additional util functions. If the current PR is acceptable, it can be merged and closed, otherwise I will add the custom error class after 14th and it can be left open till then.

@prasadtalasila
Copy link
Member

prasadtalasila commented Dec 20, 2017

@AnkshitJain any progress on this work? Because of a few commits on dev branch from my side, there are merge conflicts. You have to rebase your branch.

Copy link
Member

@prasadtalasila prasadtalasila left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnkshitJain please make the changes.

}



setInterval(function () {
connection.query('SELECT 1' ,function(err, rows, fields) {
console.log("keep alive query");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment the console log line.

setInterval(function () {
connection.query('SELECT 1' ,function(err, rows, fields) {
console.log("keep alive query");
});
}, 10000);
}, 100000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout value here would be half of the socket timeout set in MySQL config file.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for timeout is 8 hrs. We can have out own custom timeout value with the wait_timeout variable. It was verified and here is the same.

mysql> SHOW global VARIABLES like "wait_timeout";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.07 sec)

We can set the timeout value to 14400 seconds here.

tests/test.sh Outdated
# change the config file paths in all the relevant js files
sed -i 's/\/etc\/load_balancer/\.\.\/deploy\/configs\/load_balancer/' load_balancer/load_balancer.js
sed -i 's/\/etc\/main_server/\.\.\/deploy\/configs\/main_server/' main_server/main_server.js
#sed -i 's/\/etc\/load_balancer/\.\.\/deploy\/configs\/load_balancer/' load_balancer/load_balancer.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove the next two commented lines. There are equivalent environment config variables.

@AnkshitJain
Copy link
Collaborator Author

@prasadtalasila The commit has been updated. Please have a look at it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants