Install Node-Red on Openshift

Node-Red is a visual tool for wiring the Internet of Things. It allows you to define flows, triggers and outputs that process data. This can be used in all kinds of applications, such as home automation or security. I wanted to use it to implement remote monitoring on my private server I have installed at home. To be able to do this I created a free gear at openshift.com. The reason for selecting Openshift was basically the three free gears they offer.

Create the gear in OpenShift

For this howto I assume that you have created an account at openshift.com and have used the following webpage to create a Node.js instance: Node.js Application Hosting @ Openshift. In short, you can use the command:

rhc app create MyApp nodejs-0.10

or via a webflow, as explained in this blogpost.

Clone via git

In order to be able to checkout the code that Openshift will execute when the applications starts you first need to configure your SSH key at the settings.

After the SSH public key is set up, use the git clone ssh://xxx@appname-openshiftname.rhcloud.com command to clone the source files, you can find this URL in the app details.

Your repository will have the following format:

node_modules/            Any Node modules packaged with the app 
deplist.txt              Deprecated.
package.json             npm package descriptor.
.openshift/              Location for OpenShift specific files
    action_hooks/        See the Action Hooks documentation 
    markers/             See the Markers section below
server.js                The default node.js execution script.

File updates

We will update two files: package.json and server.js. First replace the contents of package.json to:

package.json

{
  "name": "Node-Red",
  "version": "1.0.0",
  "description": "Node RED on Openshift",
  "keywords": [
    "OpenShift",
    "Node.js",
    "application",
    "node-red"
  ],
  "engines": {
    "node": ">= 0.6.0",
    "npm": ">= 1.0.0"
  },

  "dependencies": {
    "express": "4.x",
    "node-red": ">= 0.9
    "atob": "1.1.2",
    "basic-auth-connect": "1.0.0"
  },
  "devDependencies": {},
  "bundleDependencies": [],

  "private": true,
  "main": "server.js"
}

The author and homepage fields are provided by default in the example, but I left them out. This file defines the different dependencies for running the server. The current dependency for node-red points to the last stable release. Since node-red is still in beta, you might sometimes want to use the latest version from github. More on that at the end of the article.

server.js

The default server.js file needs to be replaced with a version that will run node-red.

var http = require('http');
var express = require("express");
var RED = require("node-red");
var atob = require('atob');

var MyRed = function() {

    //  Scope.
    var self = this;


    /*  ================================================================  */
    /*  Helper functions.                                                 */
    /*  ================================================================  */

    /**
     *  Set up server IP address and port # using env variables/defaults.
     */
    self.setupVariables = function() {
        //  Set the environment variables we need.
        self.ipaddress = process.env.OPENSHIFT_NODEJS_IP;
        self.port      = process.env.OPENSHIFT_NODEJS_PORT || 8000;

        if (typeof self.ipaddress === "undefined") {
            //  Log errors on OpenShift but continue w/ 127.0.0.1 - this
            //  allows us to run/test the app locally.
            console.warn('No OPENSHIFT_NODEJS_IP var, using 127.0.0.1');
            self.ipaddress = "127.0.0.1";
        };



        // Create the settings object
        self.redSettings = {
            httpAdminRoot:"/",
            httpNodeRoot: "/api",
            userDir: process.env.OPENSHIFT_DATA_DIR
        };

        if (typeof self.redSettings.userDir === "undefined") {
            console.warn('No OPENSHIFT_DATA_DIR var, using ./');
            self.redSettings.userDir = "./";
        }
    };

     /**
     *  terminator === the termination handler
     *  Terminate server on receipt of the specified signal.
     *  @param {string} sig  Signal to terminate on.
     */
    self.terminator = function(sig){
        if (typeof sig === "string") {
           console.log('%s: Received %s - terminating app ...',
                       Date(Date.now()), sig);
            RED.stop();
           process.exit(1);
        }
        console.log('%s: Node server stopped.', Date(Date.now()) );
    };

    /**
     *  Setup termination handlers (for exit and a list of signals).
     */
    self.setupTerminationHandlers = function(){
        //  Process on exit and signals.
        process.on('exit', function() { self.terminator(); });

        // Removed 'SIGPIPE' from the list - bugz 852598.
        ['SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
         'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGTERM'
        ].forEach(function(element, index, array) {
            process.on(element, function() { self.terminator(element); });
        });
    };

    /*  ================================================================  */
    /*  App server functions (main app logic here).                       */
    /*  ================================================================  */

    /**
     *  Create the routing table entries + handlers for the application.
     */
    self.createRoutes = function() {
        self.routes = { };

        self.routes['/asciimo'] = function(req, res) {
            var link = "http://i.imgur.com/kmbjB.png";
            res.send("<html><body><img src='" + link + "'></body></html>");
        };
    };

    /**
     *  Initialize the server (express) and create the routes and register
     *  the handlers.
     */
    self.initializeServer = function() {
        self.createRoutes();

        // Create an Express app
        self.app = express();

        // Create a server
        self.server = http.createServer(self.app);

        //setup basic authentication
        var basicAuth = require('basic-auth-connect');
        self.app.use(basicAuth(function(user, pass) {
            return user === 'test' && pass === atob('dGVzdA==');
        }));

        // Initialise the runtime with a server and settings
        RED.init(self.server, self.redSettings);
        console.log('%s is the userDir for RED', self.redSettings.userDir);

        // Serve the editor UI from /red
        self.app.use(self.redSettings.httpAdminRoot,RED.httpAdmin);

        // Serve the http nodes UI from /api
        self.app.use(self.redSettings.httpNodeRoot,RED.httpNode);

        // Add a simple route for static content served from 'public'
        //self.app.use("/",express.static("public"));

        //  Add handlers for the app (from the routes).
        for (var r in self.routes) {
            self.app.get(r, self.routes[r]);
        }
    };

    /**
     *  Initializes the sample application.
     */
    self.initialize = function() {
        self.setupVariables();
        self.setupTerminationHandlers();

        // Create the express server and routes.
        self.initializeServer();
    };

    /**
     *  Start the server (starts up the sample application).
     */
    self.start = function() {
        //  Start the app on the specific interface (and port).
        self.server.listen(self.port,self.ipaddress, function() {
            console.log('%s: Node server started on %s:%d ...',
                        Date(Date.now() ), self.ipaddress, self.port);
        });

        // Start the runtime
        RED.start();
    };
}

/**
 *  main():  Main code.
 */
var red = new MyRed();
red.initialize();
red.start();

This is a variation on the default server.js from OpenShift that initializes the RED server. In initializeServer node-red is started. I have added Basic Authentication to prevent unauthorized users from accessing. To prevent having the password in plain-text it is base64 encoded. Via base64encode.org you can encode your password and place it in the server.js file.

Git push

Once you have the files ready, commit the changes via git. Issue a git push remote command to update the remote repository at Openshift. This will trigger some hooks at the server side and start the node server. The output will end in something similar to:

remote: npm info ok 
remote: Preparing build for deployment
remote: Deployment id is d46d7d66
remote: Activating deployment
remote: Starting NodeJS cartridge
remote: Tue Sep 09 2014 03:09:49 GMT-0400 (EDT): Starting application 'red' ...
remote: -------------------------
remote: Git Post-Receive Result: success
remote: Activation status: success
remote: Deployment completed with status: success
To ssh://xxx@red-xxx.rhcloud.com/~/git/red.git/
   6317488..2724c91  master -> master

You can now access your node-red instance at http://red-[openshift namespace].rhcloud.com:8000. The added port 8000 is required because the Openshift proxy currently does not support forwarding WebSockets via port 80. If you open the website at port 80, it will show an error message that the connection to the server is lost. See below for a workaround.

Workaround Openshift Websockets

In order to be able to access node-red at port 80 (and save typing the port :8000 addition) I have created a work-around for this. The portnumber is set fixed in the file public/red/comms.js, it is taken from location.port. I have created a fork and a separate branch where the portnumber is fixed to 8000. This is available at github. In order to use it you need to update the node-red dependency to the following line:

"node-red": "git://github.com/matueranet/node-red.git#067028b59917e98615c87985c02810c4828a25fa"

This also references a specific commit, so that the server is not automatically updated on an update before I decide to do so.