Continuous Deployment, Cloud and Node.js

The goal of this post is to introduce you to continuous deployment and demonstrate the usage of node.js and cloud infrastructure.

Let’s tackle the first Big word in the title of this post “Continuous Deployment”, the simplest understanding of this can be

Deploy continuously as long as you have a successful build and keep promoting the build(package) till it reaches its Release state.

The below figure is a simplistic view of the deployment pipeline where a successful build is promoted up the ladder and ultimately released if all stages are successfully completed. We will be targeting the green block which would enable us to build the entire pipeline.

image

We will use node.js to invoke a cloud computing service to create as many computing instances as we want in order to represent a scaled down version of a production environment.

I have created a openstack private cloud on my Hyper-V enabled windows 8 laptop. This cloud is hosted inside a Virtual machine(VM) running on Ubuntu Server (Trusty). You can consider this as a very small scale representation of the cloud infrastructure that you may have in your organizations.

In order for us to communicate with this cloud, we will be using a “pkgcloud” package which abstracts the complexities and differences among various cloud providers.

Now, follow the steps to set-up your machine

Step 1 : Download the installer from https://nodejs.org/ and install the same on the machine.

Step 2 : Create a folder “ContinuousDeployment” on your machine, this is your root directory.

Step 3 : Create a file package.json

[code language=”javascript”]

{
"name" : "cloudconnect",
"version" : "1.0.0",
"description" : "Connect to any cloud through a command prompt.",
"repository" : "",
"dependencies":
{
"commander":"*",
"pkgcloud":"*",
"underscore":"*"
}
}

[/code]

Step 4 : Run the following command at the root directory through your command prompt .

[code language=”javascript”]
npm install
[/code]

This step will install the necessary packages for our script in your root directory.

Step 5 : Create a file CloudConnect.js and paste the below content in that file.
You can invoke this script as a build step once you have a successful package built for your code.

[code language=”javascript”]

var pkgcloud = require(‘pkgcloud’),
_ = require(‘underscore’);
var program = require(‘commander’);
var fileSystem = require(‘fs’);

program
.version(‘1.0.0’)
.option(‘-c, –cloudProvider <cloudProvider>’, ‘Cloud Provider’, /^(amazon|azure|digitalocean|hp|joyent|openstack|rackspace)$/i, ‘openstack’)
.option(‘-u, –userName <userName>’, ‘User Name’)
.option(‘-p, –password <password>’, ‘Password’)
.option(‘-r, –region <region>’, ‘Region’, /^(RegionOne)$/i, ‘RegionOne’)
.option(‘-a, –authUrl <authUrl>’, ‘Authentical Url’)
.option(‘-e, –requiredEnvironment <requiredEnvironment>’, ‘Required Environment Configuration’)
.parse(process.argv);

console.log("You Entered");
console.log("-c Cloud Provider : %j", program.cloudProvider);
console.log("-u User name : %j", program.userName);
console.log("-r Region : %j", program.region);
console.log("-a AuthUrl: %j", program.authUrl);
console.log("-e RequiredEnvironment: %j", program.requiredEnvironment);

var environmentJsonString = fileSystem.readFileSync(program.requiredEnvironment, "UTF-8");
var reqdEnvironments = JSON.parse(environmentJsonString);

console.log("Hang on folks… we are connecting to cloud now..")

/* create our client with your openstack credentials */
var client = pkgcloud.compute.createClient({
provider: program.cloudProvider,
username: program.userName,
password: program.password,
region: program.region,
authUrl: program.authUrl
});

console.log("Yippie… we are in cloud now..")

var availableFlavours;
var availableImages;

// first we’re going to get our flavors
client.getFlavors(function (err, flavors) {
if (err) {
console.dir(err);
return;
}

availableFlavours = flavors;
console.log("Got all the available flavours…going for images now");

// then get our base images
client.getImages(function (err, images) {
if (err) {
console.dir(err);
return;
}

availableImages = images;
console.log("Got all the available images…get ready I am going for your environment creation now");

try {
_.each(reqdEnvironments.environmentInstances, function (environment) {
createInstance(environment, client)
});
}
catch (exception) {
console.dir(ex);
return;
}
});
});

function createInstance(environment, cloudClient) {
// Pick the required flavour
var flavour = _.findWhere(availableFlavours, { name: environment.flavour });

//Pick the required image.
var image = _.findWhere(availableImages, { name: environment.image }); // Check if this version is correct

// Create instance
cloudClient.createServer({
name: environment.name,
image: image,
flavor: flavour
}, handleServerResponse);
}

// This function will handle our server creation,
// as well as waiting for the server to come online after we’ve
// created it.
function handleServerResponse(err, server) {
if (err) {
console.dir(err);
return;
}

console.log(‘INSTANCE CREATED: ‘ + server.name + ‘, waiting for active status’);

// Wait for status: RUNNING on our server, and then callback
server.setWait({ status: server.STATUS.running }, 5000, function (err) {
if (err) {
console.dir(err);
return;
}

console.log(‘INSTANCE INFO => %j | %j | %j | %j’, server.id, server.name, server.status, server.addresses.private);
});
}

[/code]

Step 6 : Create MyEnvironment.json and paste the below script in it

[code language=”javascript”]

{"environmentInstances" : [
{"name": "AppServer1", "flavour":"m1.tiny","image":"cirros-0.3.2-x86_64-uec"},
{"name": "WebServer1", "flavour":"m1.nano","image":"cirros-0.3.2-x86_64-uec"}
]}

[/code]

And you are ready to run the script!!!.

Invoke the script at your root directory. A sample usage is as shown

[code language=”javascript”]

node CloudConnect.js -c "openstack" -u "demo" -p "password" -r "RegionOne" -a "http://<ipaddress>:<port>" -e "MyEnvironment.json"

[/code]

The above results into spawning of 2 VM’s in your cloud with the flavour and the image that you specified in the MyEnvironment.json.

image

Sample output of the command is as below

[code language=”text”]

You Entered
-c Cloud Provider : "openstack"
-u User name : "demo"
-r Region : "RegionOne"
-a AuthUrl: "http://<ipaddress>:<port>"
-e RequiredEnvironment: "MyEnvironment.json"
Hang on folks… we are connecting to cloud now..
Yippie… we are in cloud now..
Got all the available flavours…going for images now
Got all the available images…get ready I am going for your environment creatio
n now
INSTANCE CREATED: WebServer1, waiting for active status
INSTANCE CREATED: AppServer1, waiting for active status
INSTANCE INFO => "5ab5bc20-f1ca-4855-8299-7f990f3dcf80" | "WebServer1" |
"RUNNING" | [{"OS-EXT-IPS-MAC:mac_addr":"<macaddress>","version":4,"addr":"10.0.0.4","OS-EXT-IPS:type":"fixed"}]
INSTANCE INFO => "dedf0833-f35e-495b-9767-b05581623963" | "AppServer1" |
"RUNNING" | [{"OS-EXT-IPS-MAC:mac_addr":"<macaddress>","version":4,"addr":"10.0.0.5","OS-EXT-IPS:type":"fixed"}]

[/code]
Note: Your code deployments should be targeted towards the newly generated VM’s using the Fixed IP Addresses. You can expect more work with this script as I have only tried to cover the VM creation part.

This brings us to the end of this post, it was a bit lengthy but I thoroughly enjoyed this experiment at home on Windows 8 Laptop with Hyper-V,Ubuntu and openstack.

Please feel free to “like” or write back on the post.

References
OpenStack(devstack)
pkgcloud

DISCLAIMER

THE OPINIONS EXPRESSED HEREIN ARE MY OWN PERSONAL OPINIONS AND DO NOT REPRESENT MY EMPLOYER’S VIEW IN ANY WAY. ALL THE RESOURCES USED IN MAKING THIS POST WERE PERSONAL AND WERE DONE TO GAIN KNOWLEDGE.