API Actions: Lambda

API Actions: Lambda

The following is a sample Node.js implementation of an AWS Lambda function that calls the CloudBasic API to execute the following actions:

  • Activate an Instance
  • Create a User
  • Create a new Replication
  • Delete a Replication
  • Retrieve the Status of a Replication
  • Analyze a Replication
  • Create a new Cluster
  • Delete a Cluster

app.js

/*
Sample AWS Lambda function to call CBR API

Uses the aws4 node.js library (https://github.com/mhart/aws4) to simplify call signing

For more information on packaging your custom Lambda function see the AWS documentation (http://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html) 

*/
'use strict';

var httpClient = require('./http-client.js'),
    deepcopy = require("deepcopy"),
    CONFIG = require('./config.json');

exports.handler = (event, context, callback) => {

    scenario();

    function scenario() {

        CONFIG.xml = event.xml;
        CONFIG.https = event.https;
        
        var testCase = "Test case: " + (CONFIG.xml ? " XML " : " JSON ") + '/' + (CONFIG.https ? " HTTPS " : " HTTP ") + "version";

        console.log(testCase);

       var credentials = { accessKeyId: 'ACCESSKEYIDONE', secretAccessKey: 'SECRETACCESSKEYONE' };
    var secondCreds = { accessKeyId: 'ACCESSKEYIDTWO', secretAccessKey: 'SECRETACCESSKEYTWO' };

    activateInstance('i-011c1c1edd1111dc1', function (credentials) {
        console.log(`Create Instance ended!`);
        console.log(credentials);

        createUser(credentials, function (user) {
            console.log(` `);
            console.log(`create user ended!`);
            console.log(user);

            createReplication(credentials, function (replication) {
                console.log(` `);
                console.log(`create replication ended!`);
                console.log(replication);

                replicationStatus(credentials, replication.replicationId, function (replicationStatus) {
                    console.log(` `);
                    console.log(`Replication Status ended!`);
                    console.log(replicationStatus);

                    analyzeReplication(credentials, replication.replicationId, function (data) {
                        console.log(` `);
                        console.log(`Analyze Replication ended!`);
                        console.log(data);


                        deleteReplication(credentials, replication.replicationId, function (data) {
                            console.log(` `);
                            console.log(`Delete Replication ended!`);
                        });
                    });
                });

                httpClient.changeHost('your.server2.IP.address');

                activateInstance('i-022c2c2edd2222dc2', function (seconCreds) {
                    console.log(` `);
                    console.log(`Create Second Instance ended!`);
                    console.log(seconCreds);
                });

                httpClient.changeHost('your.server1.IP.address');

                createCluster(credentials, 'your.server2.IP.address', secondCreds, function (data) {
                    console.log(` `);
                    console.log(`Create Cluster ended!`);

                    deleteCluster(credentials, 'your.server1.IP.address', function (data) {
                        console.log(` `);
                        console.log(`Delete Cluster ended!`);
                    });
                });

            });
        });
    //});
}


    //Activate the insance
    function activateInstance(instanceId, callback) {
        var credentials = { accessKeyId: '', secretAccessKey: '' };

        var model = deepcopy(CONFIG.activateInstance);
        model.ActivateInstanceRequest.InstanceId = instanceId;

        // we can now query the CBR API 
        httpClient.post('ActivateInstance', model, function (res) {
            //set instance credentials
            credentials.accessKeyId = res.publicKey;
            credentials.secretAccessKey = res.privateKey;

            callback(credentials);
        });
    }

    //Create User
    function createUser(creds, callback) {
        var model = deepcopy(CONFIG.createUser);

        httpClient.post('CreateUser', model, function (res) {            
            callback(res);
        }, creds);
    }

    //Create Replication
    function createReplication(creds, callback) {
        var model = deepcopy(CONFIG.createReplication);

        httpClient.post('CreateReplication', model, function (res) {           
            callback(res);
        }, creds);
    }

    //Delete Replication
    function deleteReplication(creds, replicationId, callback) {
        var model = deepcopy(CONFIG.deleteReplication);
        model.DeleteReplicationRequest.ReplicationId = replicationId;

        httpClient.post('DeleteReplication', model, function (res) {
            callback("Ok");
        }, creds);
    }

    //Replication Status
    function replicationStatus(creds, replicationId, callback) {
        var model = deepcopy(CONFIG.replicationStatus);
        model.ReplicationStatusRequest.ReplicationId = replicationId;

        httpClient.post('ReplicationStatus', model, function (res) {
            callback(res);
        }, creds);
    }

    //Analyze Replication
    function analyzeReplication(creds, replicationId, callback) {
        var model = deepcopy(CONFIG.analyzeReplication);
        model.AnalyzeReplicationRequest.ReplicationId = replicationId;

        httpClient.post('AnalyzeReplication', model, function (res) {
            callback(res);
        }, creds);
    }

    //Create Cluster
    function createCluster(creds, serverIP, serverCreds, callback) {
        var model = deepcopy(CONFIG.createCluster);
        model.CreateClusterRequest.RemoteServer = serverIP;

        model.CreateClusterRequest.PublicKey = serverCreds.accessKeyId;
        model.CreateClusterRequest.PrivateKey = serverCreds.secretAccessKey;

        httpClient.post('CreateCluster', model, function (res) {
            callback("Ok");
        }, creds);
    }

    //Delete Cluster
    //StandaloneServer - Standalone server becomes primary. Schedules on the other server are inactivated.
    //The API call can be executed against the API of either of the cluster servers 
    function deleteCluster(creds, serverIP, callback) {
        var model = deepcopy(CONFIG.deleteCluster);
        model.DeleteClusterRequest.StandaloneServer = serverIP;

        httpClient.post('DeleteCluster', model, function (res) {
            callback("Ok");
        }, creds);
    }
}

http-client.js


var http = require('http'),
    https = require('https'),
    aws4 = require('aws4'),
    xml2js = require('xml2js'),
    deepcopy = require("deepcopy"),
    CONFIG = require('./config.json');

function sign(opts, creds) {
    aws4.sign(opts, creds);
}

function createBody(model) {
    if (CONFIG.xml) {
        var builder = new xml2js.Builder({
            explicitRoot: false,
            xmldec: {
                version: "1.0",
                encoding: "utf-8"
            }
        });

        return builder.buildObject(model);
    }
    else {
        var body = firstPropToCamelCase(model);

        return body;
    }
}

//get first property of the model and translate first letter to lower case  
//skip ActivateInstanceRequest, DeleteReplicationRequest, ReplicationStatusRequest etc.
function firstPropToCamelCase(obj) {
    return JSON.stringify(obj[Object.keys(obj)[0]], function (key, value) {
        if (value && typeof value === 'object') {
            var replacement = {};
            for (var k in value) {
                if (Object.hasOwnProperty.call(value, k)) {
                    replacement[k && k.charAt(0).toLowerCase() + k.substring(1)] = value[k];
                }
            }
            return replacement;
        }
        return value;
    });
}

function changeHost(host) {
    var options = CONFIG.options;
    options.host = host;
}

function createOptions(path) {
    var options = deepcopy(CONFIG.options);
    options.path = `${CONFIG.addOptions.basePath}${path}`;
    options.port = CONFIG.https ? CONFIG.addOptions.httpsPort : CONFIG.addOptions.httpPort;
        
    if (!CONFIG.xml) {
        options.headers["Content-Type"] = "application/json"
    }

    return options;
}

function post(path, model, callback, creds = undefined) {
    var options = createOptions(path);

    options.method = 'POST';
    options.body = createBody(model);
        
    if (creds) {
        sign(options, creds);
    }

    return request(options, callback);
};

function get(path, callback, creds = undefined) {
    var options = createOptions(path);

    if (creds) {
        sign(options, creds);
    }

    return request(options, callback);
};

function request(options, callback) {
    var protocol = CONFIG.https ? https : http;

    var req = protocol.request(options, function (res) {
        var res, resJS;
        if (res.statusCode == 200) {
            res.setEncoding('utf8');
            res.on('data', (res) => {
                if (CONFIG.xml) {
                    var parser = new xml2js.Parser({
                        explicitArray: false
                    });

                    parser.parseString(res, function (err, result) {
                        resJS = JSON.parse(firstPropToCamelCase(result));
                    });
                }
                else {
                    resJS = JSON.parse(res);
                }
            });

            res.on('end', function () {
                callback(resJS);
            });
        } else {
            res.setEncoding('utf8');
            res.on('data', (chunk) => {
                console.log(``);
                console.log(`Error: ${chunk}`);
            });
        }
    });
    req.write(options.body);
    req.end();
}

module.exports = {
    post: post,
    get: get,
    changeHost: changeHost
};

config.json

{
  "xml": false,
  "https": true,
  "addOptions": {
    "httpPort": "82",
    "httpsPort": "4432",
    "basePath": "/api/"
  },
  "options": {
    "host": "your.server1.IP.address",
    "port": "4432",    
    "method": "GET",
    "service": "cloudbasic",
    "rejectUnauthorized": false,
    "headers": {
      "X-Amz-Content-Sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "Content-Type": "application/xml"
    },
    "timeout": 120
  },
  "activateInstance": {
    "ActivateInstanceRequest": {
      "Email": "your_user@your_domain.com",
      "InstanceId": "i-011c1c1edd1111dc1",
      "Password": "YourPassword",
      "IsProduction": true,
      "Company": "test",
      "ContactPerson": "",
      "ActivationKey": ""
    }
  },
  "createUser": {
    "CreateUserRequest": {
      "Email": "new_email@your_domain.com",
      "Username": "new_user",
      "FirstName": "New",
      "LastName": "User",
      "Password": "YourPassword",
      "IsAdmin": true
    }
  },
  "createReplication": {
    "CreateReplicationRequest": {
      "ReplicationMethod": "ContinuousWithAutoSeeding",
      "ReplicateLoginsUsersAndRoles": false,
      "ReplicateTablesOnly": false,
      "ParallelTablesLimit": "5",
      "RebuildIndexes": false,      
      "Source": {
        "ConnectionString": "Data Source=your.server.name;Initial Catalog=YourDatabaseName;Persist Security Info=False;User ID=YourUser;Password=YourPassword;Connect Timeout=1280",
        "EncryptDataInTransit": false,
        "ChangeTrackingRetentionPeriod": "2 DAYS",
        "ResetChangeTracking": false
      },
      "Destination": {
        "ConnectionString": "Data Source=your.destination.server.name;Initial Catalog=YourDestinationDatabaseName;Persist Security Info=False;User ID=YourUser;Password=YourPassword;Connect Timeout=1280",
        "EncryptDataInTransit": false,
        "ReplicateCompatibilityLevel": true,
        "AzureServerTier": null,
        "AzureMaxDbSize": null,
        "CustomDbCreateScript": null,
        "PreSeedingCustomAction": null,
        "PostSeedingCustomAction": null
      }      
    }
  },
  "deleteReplication": {
    "DeleteReplicationRequest": {
      "ReplicationId": ""
    }
  },
  "replicationStatus": {
    "ReplicationStatusRequest": {
      "ReplicationId": ""
    }
  },
  "analyzeReplication": {
    "AnalyzeReplicationRequest": {
      "ReplicationId": ""
    }
  },
  "createCluster": {
    "CreateClusterRequest": {
      "RemoteServer": "your.server2.IP.address",
      "Port": "81",
      "PublicKey": "YOUR.PUBLIC.KEY.ID",
      "PrivateKey": "YOUR.PRIVATE.KEY.VALUE"
    }
  },
  "deleteCluster": {
    "DeleteClusterRequest": {
      "StandaloneServer": ""
    }
  }
}