├── .gitignore ├── LICENSE ├── README.md ├── fixtures ├── hellojava │ └── hellojava.war └── helloworld │ ├── foo │ └── foo.js │ └── main.js ├── lib ├── comms.js ├── vmcjs.js └── vmcutils.js ├── makefile ├── package.json └── test └── test_vmcjs.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov/ 2 | dist/ 3 | output/ 4 | man/ 5 | node_modules/ 6 | *~ 7 | nohup.out 8 | test/config.json 9 | test/tmp.js -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 FeedHenry Ltd, All Rights Reserved 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | This software may download additional open source software components upon install 22 | that are distributed under separate terms and conditions. Please see the license 23 | information provided in the individual software components for more information. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vmcjs [![NPM version](https://badge.fury.io/js/vmcjs.png)](http://badge.fury.io/js/vmcjs) 2 | 3 | A node.js library version of the CloudFoundry VMC gem. 4 | 5 | _Copyright 2011, FeedHenry Ltd. Licensed under the 6 | MIT license, please see the LICENSE file. All rights reserved._ 7 | 8 | ## Installation 9 | npm install vmcjs 10 | 11 | ## Example usage 12 | 13 | var vmcjs = require('vmcjs'); 14 | var vmc = new vmcjs.VMC(target, user, pwd); 15 | vmc.login(function(err) { 16 | vmc.apps(function(err, apps){ 17 | console.dir(apps) 18 | }); 19 | }); 20 | 21 | See the tests for more sample API usage. 22 | 23 | ## Running tests 24 | 25 | You need to set your own CloudFoundry account credentials before running the tests. First, set the following three environment variables: 26 | 27 | export CF_TARGET= e.g. for a local micro instance http://api..cloudfoundry.me 28 | export CF_EMAIL= e.g. foo@bar.com 29 | export CF_PWD= 30 | export CF_ADMIN_EMAIL= e.g. foo@vmware.com 31 | export CF_ADMIN_PWD= 32 | 33 | Then run the tests with: 34 | expresso -I lib 35 | -------------------------------------------------------------------------------- /fixtures/hellojava/hellojava.war: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feedhenry/vmcjs/fea336ab80fe213c825b2ff1d4ffe71251a8912b/fixtures/hellojava/hellojava.war -------------------------------------------------------------------------------- /fixtures/helloworld/foo/foo.js: -------------------------------------------------------------------------------- 1 | 2 | function hello() { 3 | console.log('hello!'); 4 | } 5 | 6 | exports.hello = hello; 7 | -------------------------------------------------------------------------------- /fixtures/helloworld/main.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var port = process.env.VCAP_APP_PORT || 8080; 3 | var util = require('util'); 4 | var foo = require('./foo/foo.js'); 5 | 6 | http.createServer(function (req, res) { 7 | res.writeHead(200, {'Content-Type': 'text/plain'}); 8 | console.log("Got request: " + util.inspect(req)); 9 | foo.hello(); 10 | res.end('Hello World\n'); 11 | }).listen(port, "127.0.0.1"); 12 | console.log('Server running at on port: ' + port); 13 | 14 | -------------------------------------------------------------------------------- /lib/comms.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var https = require('https'); 3 | var util = require('util'); 4 | var fs = require('fs'); 5 | 6 | function request(method, host, port, isSecure, path, params, options, callback) { 7 | var payload; 8 | var headers = {}; 9 | var timeout = options.timeout; 10 | if (method !== 'DELETE') { 11 | headers["accept"] = "application/json"; 12 | headers["content-type"] = "application/json"; 13 | } else { 14 | // Odd - CloudController delete seems to require that content lenght be set.. 15 | headers["content-length"] = 0; 16 | } 17 | 18 | if(options.token != undefined) { 19 | headers['AUTHORIZATION'] = options.token; 20 | } 21 | 22 | if(options.proxyUser != undefined){ 23 | headers['PROXY-USER'] = options.proxyUser; 24 | } 25 | 26 | if (params != undefined){ 27 | payload = JSON.stringify(params); 28 | headers["content-length"] = payload.length; 29 | } 30 | 31 | options = { 32 | host: host, 33 | port: port, 34 | path: path, 35 | method: method, 36 | headers: headers 37 | }; 38 | var protocol = isSecure ? https : http; 39 | 40 | var req = protocol.request(options, function (res) { 41 | res.setEncoding('utf8'); 42 | var data = ''; 43 | res.on('data', function(chunk) { 44 | data += chunk; 45 | }); 46 | 47 | res.on('end', function() { 48 | // TODO - improve error handling here.. 49 | var error = { 50 | host: host, 51 | method: method, 52 | status: res.statusCode, 53 | message: data, 54 | path: path, 55 | params: params, 56 | headers: headers 57 | }; 58 | 59 | if(res.statusCode < 200 || res.statusCode >= 400) return callback(error); 60 | data = data.trim(); 61 | try { // some data, e.g. logfiles, are not json format, so return as is 62 | data = data === '' ? {} : JSON.parse(data); 63 | } catch(e) { 64 | //ignore parse errors and return data 65 | } 66 | return callback(undefined, data); 67 | }); 68 | }); 69 | 70 | req.on('error', function(e) { 71 | return callback(e); 72 | }); 73 | 74 | if (params != undefined){ 75 | req.write(payload); 76 | } 77 | 78 | if (timeout) { 79 | req.on('socket', function (socket) { 80 | socket.setTimeout(timeout); 81 | socket.on('timeout', function() { 82 | req.emit("error", {status:408, message: "Request Timeout"}); 83 | req.abort(); 84 | }); 85 | }); 86 | } 87 | 88 | req.end(); 89 | }; 90 | 91 | // Uploads zipfile to CF 92 | function upload(host, port, isSecure, options, file, path, resources, callback) { 93 | 94 | var boundary = Math.random(); 95 | var post_data = []; 96 | 97 | post_data.push(new Buffer(encodeFieldPart(boundary, 'resources', resources), 'ascii')); 98 | post_data.push(new Buffer(encodeFieldPart(boundary, '_method', 'put'), 'ascii')); 99 | post_data.push(new Buffer(encodeFilePart(boundary, 'application/zip', 'application', file))); 100 | 101 | var file_reader = fs.createReadStream(file, {encoding: 'binary'}); 102 | var file_contents = ''; 103 | file_reader.on('data', function(data){ 104 | file_contents += data; 105 | }); 106 | file_reader.on('end', function(){ 107 | post_data.push(new Buffer(file_contents, 'binary')); 108 | post_data.push(new Buffer("\r\n--" + boundary + "--"), 'ascii'); 109 | doUpload(host, port, isSecure, options, path, post_data, boundary, callback); 110 | }); 111 | }; 112 | 113 | function encodeFieldPart(boundary, name, value) { 114 | var return_part = "--" + boundary + "\r\n"; 115 | return_part += "Content-Disposition: form-data; name=\"" + name + "\"\r\n\r\n"; 116 | return_part += value + "\r\n"; 117 | return return_part; 118 | } 119 | 120 | function encodeFilePart(boundary, type, name, filename) { 121 | var return_part = "--" + boundary + "\r\n"; 122 | return_part += "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n"; 123 | return_part += "Content-Type: " + type + "\r\n\r\n"; 124 | return return_part; 125 | } 126 | 127 | function doUpload(host, port, isSecure, options, path, post_data, boundary, callback) { 128 | var length = 0; 129 | 130 | for(var i = 0; i < post_data.length; i++) { 131 | length += post_data[i].length; 132 | } 133 | 134 | var headers = { 135 | 'Content-Type' : 'multipart/form-data; boundary=' + boundary, 136 | 'Content-Length' : length, 137 | 'Accept-Encoding' : 'gzip, deflate', 138 | 'Accept' : '*/*; q=0.5, application/xml' 139 | }; 140 | 141 | if(options.token != undefined) { 142 | headers['AUTHORIZATION'] = options.token; 143 | } 144 | 145 | if(options.proxyUser != undefined){ 146 | headers['PROXY-USER'] = options.proxyUser; 147 | } 148 | 149 | options = { 150 | host: host, 151 | port: port, 152 | path: path, 153 | method: 'POST', 154 | headers: headers 155 | }; 156 | 157 | var protocol = isSecure ? https : http; 158 | var req = protocol.request(options, function(res){ 159 | // TODO - not sure what POST /apps//application returns here exactly (some binary..) 160 | // but seems safe to ignore if the call is successful.. 161 | res.setEncoding('utf8'); 162 | var data = ''; 163 | res.on('data', function(chunk){ 164 | data = data + chunk; 165 | }); 166 | 167 | res.on('end', function(){ 168 | return callback(); 169 | }); 170 | 171 | res.on('error', function(err) { 172 | return callback(err, data); 173 | }); 174 | }); 175 | 176 | for (var i = 0; i < post_data.length; i++) { 177 | req.write(post_data[i]); 178 | } 179 | req.end(); 180 | } 181 | 182 | exports.upload = upload; 183 | exports.request = request; -------------------------------------------------------------------------------- /lib/vmcjs.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var http = require('http'); 3 | var async = require('async'); 4 | var url = require('url'); 5 | var path = require('path'); 6 | var comms = require('./comms.js'); 7 | var vmcutils = require('./vmcutils.js'); 8 | 9 | // Get CloudFoundry Information 10 | VMC.prototype.info = function(options, callback) { 11 | if (typeof(options) == 'function') { 12 | callback = options; 13 | options = undefined; 14 | } 15 | var path = '/info'; 16 | this.get(path, options, function(err, data) { 17 | return callback(err, data); 18 | }); 19 | }; 20 | 21 | // Get CloudFoundry User infomration 22 | VMC.prototype.userInfo = function(email, options, callback) { 23 | if (typeof(options) == 'function') { 24 | callback = options; 25 | options = undefined; 26 | } 27 | var path = '/users/' + email; 28 | this.get(path, options, function(err, data) { 29 | return callback(err, data); 30 | }); 31 | }; 32 | 33 | // Add a new user on Cloud Foundry 34 | VMC.prototype.addUser = function(email, password, options, callback) { 35 | if (typeof(options) == 'function') { 36 | callback = options; 37 | options = undefined; 38 | } 39 | var path = '/users/'; 40 | var args = {email: email, password: password}; 41 | this.post(path, args, options, function(err, data) { 42 | return callback(err, data); 43 | }); 44 | }; 45 | 46 | // Delete a user from Cloud Foundry 47 | VMC.prototype.deleteUser = function(email, options, callback) { 48 | if (typeof(options) == 'function') { 49 | callback = options; 50 | options = undefined; 51 | } 52 | var path = '/users/' + email; 53 | this.del(path, options, function(err, data) { 54 | return callback(err, data); 55 | }); 56 | }; 57 | 58 | 59 | // Get CloudFoundry App infomation 60 | VMC.prototype.appInfo = function(name, options, callback) { 61 | if (typeof(options) == 'function') { 62 | callback = options; 63 | options = undefined; 64 | } 65 | var path = '/apps/' + name; 66 | this.get(path, options, function(err, data) { 67 | return callback(err, data); 68 | }); 69 | }; 70 | 71 | // Get CloudFoundry App stats 72 | VMC.prototype.appStats = function(name, options, callback) { 73 | if (typeof(options) == 'function') { 74 | callback = options; 75 | options = undefined; 76 | } 77 | var path = '/apps/' + name + '/stats'; 78 | this.get(path, options, function(err, data) { 79 | return callback(err, data); 80 | }); 81 | }; 82 | 83 | function constructLogFilePath (appName, instance) { 84 | if (!instance) { 85 | instance = 0; 86 | } 87 | instance = instance.toString(); 88 | return path.join('/apps/', appName, '/instances/', instance, '/files/logs'); 89 | } 90 | 91 | // Get list of CloudFoundry App logs 92 | // result returned: 93 | // [ 94 | // { modified: "", name: "file1.log", size: ""}, 95 | // ... 96 | // ] 97 | VMC.prototype.appLogs = function(appName, instance, options, callback) { 98 | if (typeof(options) == 'function') { 99 | callback = options; 100 | options = undefined; 101 | } 102 | if (!instance) { 103 | instance = 0; 104 | } 105 | var logFilePath = constructLogFilePath(appName, instance); 106 | 107 | this.get(logFilePath, options, function(err, data) { 108 | if(err) return callback(err); 109 | var i; 110 | var ret = []; 111 | // data from CF returned as: 112 | // file1.log 23B 113 | // file2.log 123B 114 | // file3.log 0B 115 | var list = data.split('\n'); 116 | for (i = 0; (list) && (i < list.length); i += 1) { 117 | var thisOne = {modified: ""}; 118 | thisOne.name = list[i].replace(/\s+\S+$/, ''); // remove whitespace and text at end 119 | thisOne.size = list[i].replace(/^.+\s+/, ''); // remove text and whitespace at beginning 120 | ret.push(thisOne); 121 | } 122 | return callback(err, ret); 123 | }); 124 | }; 125 | 126 | // Get CloudFoundry App logs 127 | VMC.prototype.appLog = function(appName, fileName, instance, options, callback) { 128 | if (typeof(options) == 'function') { 129 | callback = options; 130 | options = undefined; 131 | } 132 | if (!instance) { 133 | instance = 0; 134 | } 135 | if(!fileName) { // if no logfile specified then get latest 136 | return this.appDefaultLog(appName, instance, options, callback); 137 | } else { 138 | var logFile = path.join(constructLogFilePath(appName, instance), fileName); 139 | this.get(logFile, options, function(err, data) { 140 | return callback(err, {name:fileName, contents: data}); 141 | }); 142 | } 143 | }; 144 | 145 | // Get CloudFoundry App logs 146 | // get list of files, then ask for contents of each one, construct response 147 | VMC.prototype.appDefaultLog = function(appName, instance, options, callback) { 148 | if (typeof(options) == 'function') { 149 | callback = options; 150 | options = undefined; 151 | } 152 | if (!instance) { 153 | instance = 0; 154 | } 155 | var self = this; 156 | self.appLogs(appName, instance, function(err, logs) { 157 | var fileContents = {}; 158 | if(err) return callback(err); 159 | async.forEachSeries(logs, function (log, cb) { 160 | self.appLog(appName, log.name, instance, options, function (err, contents) { 161 | if(err) return cb(err); 162 | fileContents[contents.name] = contents.contents; 163 | cb(); 164 | }); 165 | }, function (err) { 166 | return callback(err, fileContents); 167 | }); 168 | }); 169 | }; 170 | 171 | 172 | // Get all our CloudFoundry Apps.. 173 | VMC.prototype.apps = function(options, callback) { 174 | if (typeof(options) == 'function') { 175 | callback = options; 176 | options = undefined; 177 | } 178 | var path = '/apps'; 179 | this.get(path, options, callback); 180 | }; 181 | 182 | // Updates an Apps details 183 | VMC.prototype.updateApp = function(name, app, options, callback) { 184 | if (typeof(options) == 'function') { 185 | callback = options; 186 | options = undefined; 187 | } 188 | var path = '/apps/' + name; 189 | this.put(path, app, options, callback); 190 | }; 191 | 192 | // Start App 193 | VMC.prototype.start = function(name, options, callback) { 194 | if (typeof(options) == 'function') { 195 | callback = options; 196 | options = undefined; 197 | } 198 | var self = this; 199 | this.appInfo(name, options, function(err, app) { 200 | if (err) return callback(err); 201 | app.state = 'STARTED'; 202 | self.updateApp(name, app, options, function(err, data) { 203 | if (err) return callback(err); 204 | return callback(undefined, app); 205 | }); 206 | }); 207 | }; 208 | 209 | // Stop App 210 | VMC.prototype.stop = function(name, options, callback) { 211 | if (typeof(options) == 'function') { 212 | callback = options; 213 | options = undefined; 214 | } 215 | var self = this; 216 | this.appInfo(name, options, function(err, app) { 217 | if (err) return callback(err); 218 | app.state = 'STOPPED'; 219 | self.updateApp(name, app, options, callback); 220 | }); 221 | }; 222 | 223 | // Restart App 224 | VMC.prototype.restart = function(name, options, callback) { 225 | if (typeof(options) == 'function') { 226 | callback = options; 227 | options = undefined; 228 | } 229 | var self = this; 230 | this.stop(name, options, function(err, data) { 231 | if (err) return callback(err); 232 | self.start(name, options, callback); 233 | }); 234 | }; 235 | 236 | // Get CF System Services 237 | VMC.prototype.systemServices = function(options, callback) { 238 | if (typeof(options) == 'function') { 239 | callback = options; 240 | options = undefined; 241 | } 242 | this.get('/info/services', options, callback); 243 | }; 244 | 245 | // Get CF Provisioned Services 246 | VMC.prototype.provisionedServices = function(options, callback) { 247 | if (typeof(options) == 'function') { 248 | callback = options; 249 | options = undefined; 250 | } 251 | this.get('/services', options, callback); 252 | }; 253 | 254 | // Create a new Provisioned Service 255 | VMC.prototype.createService = function(service, vendor, options, callback) { 256 | if (typeof(options) == 'function') { 257 | callback = options; 258 | options = undefined; 259 | } 260 | var self = this; 261 | this.systemServices(options, function(err, services){ 262 | var vendorService = vmcutils.vendorService(services, vendor); 263 | if (vendorService == undefined) return callback(new Error('System Service not found: ' + vendor)); 264 | var serviceHash = vmcutils.serviceHash(vendorService, service); 265 | self.post('/services', serviceHash, options, callback); 266 | }); 267 | }; 268 | 269 | // Delete existing Provisioned Service 270 | VMC.prototype.deleteService = function(service, options, callback) { 271 | if (typeof(options) == 'function') { 272 | callback = options; 273 | options = undefined; 274 | } 275 | var self = this; 276 | this.provisionedServices(options, function(err, services){ 277 | if( err ){ 278 | return callback(err, []); 279 | }else{ 280 | var serviceNames = []; 281 | for (var i=0; i= 0) { 620 | return vmcutils.copy(dir + '/' + file, zipFile, function(err, data) { 621 | if (err) return callback(err); 622 | self.uploadZip(zipFile, path, '[]', options, fileUploaded); 623 | }); 624 | } 625 | } 626 | vmcutils.zip(dir, zipFile, function(err, data) { 627 | if (err) return callback(err); 628 | var path = '/apps/' + name + '/application'; 629 | self.uploadZip(zipFile, path, '[]', options, fileUploaded); 630 | }); 631 | }); 632 | }; 633 | } 634 | 635 | exports.VMC = VMC; 636 | 637 | -------------------------------------------------------------------------------- /lib/vmcutils.js: -------------------------------------------------------------------------------- 1 | var spawn = require('child_process').spawn; 2 | 3 | exports.vendorService = function(services, vendor) { 4 | var vendorService; 5 | for (var serviceType in services) { 6 | var service = services[serviceType]; 7 | for (var product in service) { 8 | var productService = service[product]; 9 | for (var versionedProduct in productService) { 10 | var prod = productService[versionedProduct]; 11 | if (prod.vendor == vendor) { 12 | vendorService = prod; 13 | break; 14 | } 15 | } 16 | } 17 | } 18 | return vendorService; 19 | }; 20 | 21 | // reference client.rb create_service in VMC gem 22 | exports.serviceHash = function(service, name) { 23 | var serviceHash = { 24 | type: service.type, 25 | version: service.version, 26 | tier: 'free', 27 | vendor: service.vendor, 28 | version: service.version, 29 | name: name 30 | }; 31 | return serviceHash; 32 | }; 33 | 34 | exports.zip = function(dir, zipFile, callback) { 35 | var excludes = "\\.. \\. \\*~ \\#*# \\*.log"; 36 | // TODO - exclude git files.. 37 | var zip = spawn('zip', ['-y', '-q', '-r', zipFile, '.', '-x', excludes], {cwd: dir}); 38 | 39 | zip.on('exit', function(code){ 40 | if (code != 0) return callback(new Error('Error during zip!')); 41 | return callback(undefined, code); 42 | }); 43 | }; 44 | 45 | exports.copy = function (src, dst, callback) { 46 | var fs =require('fs'); 47 | function copy(err) { 48 | if (!err) return callback(new Error("File " + dst + " exists.")); 49 | 50 | return fs.stat(src, function (err) { 51 | if (err) return callback(err); 52 | var is = fs.createReadStream(src); 53 | var os = fs.createWriteStream(dst); 54 | return require('util').pump(is, os, callback); 55 | }); 56 | } 57 | 58 | fs.stat(dst, copy); 59 | }; -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | PACKAGE = vmcjs 3 | 4 | # Get the Major/Release/Hotfix numbers from package.json. 5 | PKG_VER:=$(shell grep version package.json| sed s/\"//g| sed s/version://g| sed s/-BUILD-NUMBER//g| tr -d ' '| tr -d ',') 6 | MAJOR:=$(shell echo $(PKG_VER)| cut -d '.' -f1) 7 | RELEASE:=$(shell echo $(PKG_VER)| cut -d '.' -f2) 8 | HOTFIX:=$(shell echo $(PKG_VER)| cut -d '.' -f3) 9 | 10 | VERSION = $(MAJOR).$(RELEASE).$(HOTFIX) 11 | DIST_DIR = ./dist 12 | OUTPUT_DIR = ./output 13 | MODULES = ./node_modules 14 | COV_DIR = ./lib-cov 15 | RELEASE_FILE = $(PACKAGE)-$(VERSION).tar.gz 16 | RELEASE_DIR = $(PACKAGE)-$(VERSION) 17 | 18 | all: clean npm_deps test 19 | 20 | test: 21 | env NODE_PATH=`pwd`/lib expresso 22 | 23 | dist: npm_deps 24 | rm -rf $(DIST_DIR) $(OUTPUT_DIR) 25 | mkdir -p $(DIST_DIR) $(OUTPUT_DIR)/$(RELEASE_DIR) 26 | cp -r ./lib $(OUTPUT_DIR)/$(RELEASE_DIR) 27 | cp ./package.json $(OUTPUT_DIR)/$(RELEASE_DIR) 28 | tar -czf $(DIST_DIR)/$(RELEASE_FILE) -C $(OUTPUT_DIR) $(RELEASE_DIR) 29 | 30 | npm_deps: 31 | npm install . 32 | 33 | clean: 34 | rm -rf $(MODULES) $(COV_DIR) $(OUTPUT_DIR) $(DIST_DIR) 35 | 36 | .PHONY: test dist clean npm_deps 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vmcjs", 3 | "description": "Node.js VMC library", 4 | "version": "0.1.12", 5 | "author": "Damian Beresford ", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/feedhenry/vmcjs" 9 | }, 10 | "directories": { 11 | "lib": "lib" 12 | }, 13 | "main": "./lib/vmcjs.js", 14 | "engines": { 15 | "node": "*" 16 | }, 17 | "dependencies": { 18 | "async": "*" 19 | }, 20 | "license": "MIT" 21 | } -------------------------------------------------------------------------------- /test/test_vmcjs.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var util = require('util'); 3 | var vmcjs = require("vmcjs"); 4 | var fs = require('fs'); 5 | var async = require('async'); 6 | 7 | var target = process.env.CF_TARGET || assert.fail('CF_TARGET not set, please set in your environment'); 8 | var email = process.env.CF_EMAIL || assert.fail('CF_EMAIL not set, please set in your environment'); 9 | var pwd = process.env.CF_PWD || assert.fail('CF_PWD not set, please set in your environment'); 10 | var adminEmail = process.env.CF_ADMIN_EMAIL || assert.fail('CF_ADMIN_EMAIL not set, please set in your environment'); 11 | var adminPwd = process.env.CF_ADMIN_PWD || assert.fail('CF_ADMIN_PWD not set, please set in your environment'); 12 | var appprefix = process.env.APP_PREFIX || ""; 13 | 14 | module.exports = { 15 | 16 | 'test info': function(){ 17 | var vmc = new vmcjs.VMC(target, email, pwd); 18 | vmc.info(function(err, info){ 19 | assert.equal(err, undefined, "Unexpected err in info: " + util.inspect(err)); 20 | assert.equal(info.description, "VMware\'s Cloud Application Platform"); 21 | }); 22 | }, 23 | 24 | 'test userInfo': function(){ 25 | var vmc = new vmcjs.VMC(target, email, pwd); 26 | vmc.login(function(err, token) { 27 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 28 | vmc.userInfo(email, function(err, info) { 29 | assert.equal(err, undefined, "Unexpected err in userInfo: " + util.inspect(err)); 30 | assert.equal(info.email, email); 31 | }); 32 | }); 33 | }, 34 | 35 | 'test basic target/login & list apps' : function() { 36 | var vmc = new vmcjs.VMC(target, email, pwd); 37 | vmc.login(function(err, token) { 38 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 39 | vmc.apps(function(err, apps) { 40 | assert.equal(err, undefined, "Unexpected err in apps: " + util.inspect(err)); 41 | }); 42 | }); 43 | }, 44 | 45 | 'test push and update' : function() { 46 | var vmc = new vmcjs.VMC(target, email, pwd); 47 | vmc.login(function(err, token) { 48 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 49 | var appName = appprefix + 'test3'; 50 | var appDir = './fixtures/helloworld'; 51 | 52 | // delete our test app if already exists (purposely ignore any errors) 53 | vmc.deleteApp(appName, function(err, data){ 54 | vmc.push(appName, appDir, function(err) { 55 | assert.equal(err, undefined, "Unexpected err in push: " + util.inspect(err)); 56 | 57 | var serviceName = 'redis-' + appName; 58 | var vendor = 'redis'; 59 | vmc.createService(serviceName, vendor, function(err, data) { 60 | assert.equal(err, undefined, "Unexpected err in createService: " + util.inspect(err)); 61 | vmc.bindService(serviceName, appName, function(err, data) { 62 | assert.equal(err, undefined, "Unexpected err in bindService: " + util.inspect(err)); 63 | testEnv(vmc, appName, function(err){ 64 | vmc.start(appName, function(err, data){ 65 | assert.equal(err, undefined, "Unexpected err in start: " + util.inspect(err)); 66 | testAppOk(vmc, appName, serviceName, function(err){ 67 | // finally test update 68 | vmc.update(appName, appDir, function(err, data) { 69 | assert.equal(err, undefined, "Unexpected err in update: " + util.inspect(err)); 70 | }); 71 | }); 72 | }); 73 | }); 74 | }); 75 | }); 76 | }); 77 | }); 78 | }); 79 | }, 80 | 81 | 'test push with manifest' : function() { 82 | var vmc = new vmcjs.VMC(target, email, pwd); 83 | vmc.login(function(err, token) { 84 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 85 | var appName = appprefix + 'test-with-manifest'; 86 | var appDir = './fixtures/helloworld'; 87 | var uri = require('url').parse(target); 88 | var rootTarget = uri.hostname.replace('api', ''); 89 | var manifest = { 90 | resources: { 91 | memory:128 92 | }, 93 | instances:2, 94 | name: appName, 95 | staging:{ 96 | framework:'node', 97 | runtime: null 98 | }, 99 | uris:[appName + rootTarget, appName + '-2' + rootTarget] 100 | }; 101 | 102 | // delete our test app if already exists (purposely ignore any errors) 103 | vmc.deleteApp(appName, function(err, data){ 104 | vmc.push(appName, appDir, manifest, function(err) { 105 | assert.equal(err, undefined, "Unexpected err in push: " + util.inspect(err)); 106 | }); 107 | }); 108 | }); 109 | }, 110 | 111 | 'test push-war': function(){ 112 | var vmc = new vmcjs.VMC(target, email, pwd); 113 | vmc.login(function(err, token) { 114 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 115 | var appName = appprefix + 'test-push-war'; 116 | var appDir = './fixtures/hellojava/'; 117 | var uri = require('url').parse(target); 118 | var rootTarget = uri.hostname.replace('api', ''); 119 | var manifest = { 120 | resources: { 121 | memory:512 122 | }, 123 | instances:1, 124 | name: appName, 125 | staging:{ 126 | framework:'java_web', 127 | runtime: 'java' 128 | }, 129 | uris:[appName + rootTarget, appName + '-2' + rootTarget] 130 | }; 131 | 132 | // delete our test app if already exists (purposely ignore any errors) 133 | vmc.deleteApp(appName, function(err, data){ 134 | vmc.push(appName, appDir, manifest, function(err) { 135 | assert.equal(err, undefined, "Unexpected err in push: " + util.inspect(err)); 136 | vmc.start(appName, function(err, data){ 137 | assert.equal(err, undefined, "Unexpected err in start: " + util.inspect(err)); 138 | // finally test update 139 | vmc.update(appName, appDir, function(err, data) { 140 | assert.equal(err, undefined, "Unexpected err in update: " + util.inspect(err)); 141 | }); 142 | }); 143 | }); 144 | }); 145 | }); 146 | }, 147 | 148 | 'test services' : function() { 149 | var vmc = new vmcjs.VMC(target, email, pwd); 150 | vmc.login(function(err, token) { 151 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 152 | var service = appprefix + 'Redis-112345'; 153 | var vendor = 'redis'; 154 | vmc.createService(service, vendor, function(err, data) { 155 | assert.equal(err, undefined, "Unexpected err in createService: " + util.inspect(err)); 156 | vmc.deleteService(service, function(err, data){ 157 | assert.equal(err, undefined, "Unexpected err in deleteService: " + util.inspect(err)); 158 | }); 159 | }); 160 | }); 161 | }, 162 | 163 | 'test createApp in series..' : function() { 164 | var vmc = new vmcjs.VMC(target, email, pwd); 165 | var appDir = './fixtures/helloworld'; 166 | 167 | createApp(vmc, 'test-db1', appDir, function(err, results){ 168 | assert.equal(err, undefined, "Unexpected err in createApp: " + util.inspect(err)); 169 | }); 170 | }, 171 | 172 | 'test delete app with bound service' : function(){ 173 | var vmc = new vmcjs.VMC(target, email, pwd); 174 | var appDir = './fixtures/helloworld'; 175 | var appName = appprefix + 'test-db2'; 176 | createApp(vmc, appName, appDir, function(err, results){ 177 | assert.equal(err, undefined, "Unexpected err in createApp: " + util.inspect(err)); 178 | // function testAppOk(vmc, appName, service, callback) { 179 | testAppOk(vmc, appName, 'redis-' + appName, function(err, results){ 180 | assert.equal(err, undefined, "Unexpected err in the service existance: " + util.inspect(err)); 181 | vmc.deleteApp(appName, function(err, results){ 182 | assert.equal(err, undefined, "Unexpected err in deleting app: " + util.inspect(err)); 183 | vmc.provisionedServices(function(err, results){ 184 | assert.equal(err, undefined, "Unexpected err in list services: " + util.inspect(err)); 185 | var services = results.filter(function(v){ 186 | return v.name == 'redis-test-db2'; 187 | }); 188 | assert.equal(services.length, 0, 'Bound service is not deleted.'); 189 | }); 190 | }); 191 | }); 192 | }); 193 | }, 194 | 195 | 'test delete app without bound service' : function(){ 196 | var vmc = new vmcjs.VMC(target, email, pwd); 197 | var appDir = './fixtures/helloworld'; 198 | var appName = appprefix + 'test-db3'; 199 | // ensure service does not exist at first. 200 | vmc.deleteService('redis-' + appName, function(err, results){ 201 | // create app with service. 202 | createApp(vmc, appName, appDir, function(err, results){ 203 | assert.equal(err, undefined, "Unexpected err in createApp: " + util.inspect(err)); 204 | testAppOk(vmc, appName, 'redis-' + appName, function(err, results){ 205 | assert.equal(err, undefined, "Unexpected err in the service existance: " + util.inspect(err)); 206 | // delete without service false flag. 207 | vmc.deleteApp(appName, false, function(err, results){ 208 | assert.equal(err, undefined, "Unexpected err in deleting app: " + util.inspect(err)); 209 | vmc.provisionedServices(function(err, results){ 210 | assert.equal(err, undefined, "Unexpected err in list services: " + util.inspect(err)); 211 | var services = results.filter(function(v){ 212 | return v.name == 'redis-' + appName; 213 | }); 214 | assert.equal(services.length, 1, 'Bound service is not deleted.'); 215 | }); 216 | }); 217 | }); 218 | }); 219 | }); 220 | }, 221 | 222 | 'test bad credentials..' : function() { 223 | var gotError = false; 224 | try { 225 | var vmc = new vmcjs.VMC(undefined, undefined, undefined); 226 | } catch (x) { 227 | gotError = true; 228 | } 229 | assert.ok(gotError); 230 | }, 231 | 232 | 'test update nonexistent app..' : function() { 233 | var vmc = new vmcjs.VMC(target, email, pwd); 234 | vmc.login(function(err, token) { 235 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 236 | var appDir = './fixtures/helloworld'; 237 | vmc.update('ihopefullydontexist', appDir, function(err, app) { 238 | assert.equal(err.status, 404, "Expected err to return a 404"); 239 | }); 240 | }); 241 | }, 242 | 243 | 'test logs' : function() { 244 | console.log('tgt: ' + target + ', email: ' + email + ', pwd: ' + pwd); 245 | var vmc = new vmcjs.VMC(target, email, pwd); 246 | var appDir = './fixtures/helloworld'; 247 | var appName = 'applogtest2'; 248 | var instance = 0; 249 | createApp(vmc, appName, appDir, function(err, results){ 250 | assert.equal(err, undefined, "Unexpected err in createApp: " + util.inspect(err)); 251 | vmc.appLogs(appName, instance, function(err, logs){ 252 | console.log("logs: " + util.inspect(logs)); 253 | assert.ok(!err, "Error returned from appLogs: " + JSON.stringify(err)); 254 | assert.ok(util.isArray(logs), "Expected an array of log file names"); 255 | assert.ok(logs.length >= 2, "Expected 2 or more log file names"); 256 | console.log('got list of logfiles: ', logs); 257 | var numfiles = 0; 258 | async.forEachSeries(logs, function (logFile, cb) { 259 | assert.ok(logFile.name, "should have log file name"); 260 | assert.ok(logFile.size, "should have logfile size field"); 261 | vmc.appLog(appName, logFile.name, instance, function(err, logData) { 262 | assert.ok(!err); 263 | numfiles += 1; 264 | console.log('got file: ' + logFile.name + ', data: ', logData); 265 | return cb(); 266 | }); 267 | }, function (err) { 268 | assert.ok(!err); 269 | assert.ok(numfiles > 1, "Should be at least 2 logfiles"); 270 | }); 271 | }); 272 | }); 273 | }, 274 | 275 | 'test request timeout': function() { 276 | var vmc = new vmcjs.VMC(target, email, pwd); 277 | vmc.info({timeout: 10}, function(err, info) { 278 | assert.notEqual(err, undefined); 279 | }); 280 | }, 281 | 282 | //Admin Tests 283 | 'test push and update by proxy user': function(){ 284 | var vmc = new vmcjs.VMC(target, email, pwd); 285 | var adminVmc = new vmcjs.VMC(target, adminEmail, adminPwd); 286 | vmc.login(function(err, token){ 287 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 288 | adminVmc.login(function(err, token){ 289 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 290 | // set proxy user 291 | adminVmc.proxyUser = email; 292 | var appName = appprefix + 'test-proxy-user'; 293 | var appDir = './fixtures/helloworld'; 294 | // delete app if exists as normal user. 295 | vmc.deleteApp(appName, function(err, data){ 296 | // push app as admin user but it is pushed on the normal user 297 | adminVmc.push(appName, appDir, function(err) { 298 | assert.equal(err, undefined, "Unexpected err in push: " + util.inspect(err)); 299 | // the normal user can start app deployed by adminVmc 300 | vmc.start(appName, function(err, data){ 301 | assert.equal(err, undefined, "Unexpected err in start: " + util.inspect(err)); 302 | // finally test update 303 | vmc.update(appName, appDir, function(err, data) { 304 | assert.equal(err, undefined, "Unexpected err in update: " + util.inspect(err)); 305 | }); 306 | }); 307 | }); 308 | }); 309 | 310 | }); 311 | }); 312 | }, 313 | 314 | 'test add/delete User': function(){ 315 | var vmc = new vmcjs.VMC(target, adminEmail, adminPwd); 316 | vmc.login(function(err, token) { 317 | var email = 'testuser1@example.com'; 318 | var pwd = 'testuser1'; 319 | assert.equal(err, undefined, "Unexpected err in login: " + util.inspect(err)); 320 | vmc.addUser(email, pwd, function(err, info) { 321 | assert.equal(err, undefined, "Unexpected err in addUser: " + util.inspect(err)); 322 | var userVmc = new vmcjs.VMC(target, email, pwd); 323 | userVmc.login(function(err, token){ 324 | assert.equal(err, undefined, "Unexpected err in the created user login: " + util.inspect(err)); 325 | vmc.deleteUser(email, function(err, info){ 326 | assert.equal(err, undefined, "Unexpected err in deleteUser: " + util.inspect(err)); 327 | }); 328 | }); 329 | }); 330 | }); 331 | } 332 | }; 333 | 334 | function testEnv(vmc, appName, callback) { 335 | vmc.addEnv(appName, "FOO", "BAR", function(err, data) { 336 | assert.equal(err, undefined, "Unexpected err in addEnv: " + util.inspect(err)); 337 | vmc.env(appName, function(err, environment) { 338 | assert.equal(err, undefined, "Unexpected err in env: " + util.inspect(err)); 339 | vmc.delEnv(appName, "FOO", function(err, data){ 340 | assert.equal(err, undefined, "Unexpected err in delEnv: " + util.inspect(err)); 341 | callback(undefined); 342 | }); 343 | }); 344 | }); 345 | }; 346 | 347 | function testAppOk(vmc, appName, service, callback) { 348 | vmc.appInfo(appName, function(err, app){ 349 | assert.equal(err, undefined, "Unexpected err in appInfo: " + util.inspect(err)); 350 | assert.equal(app.state, 'STARTED', 'App not started: ' + util.inspect(app)); 351 | assert.notEqual(app.services.indexOf(service), -1, "App doesn't have service: " + service + " " + util.inspect(app)); 352 | vmc.appStats(appName, function(err, app){ 353 | assert.equal(err, undefined, "Unexpected err in appStats: " + util.inspect(err)); 354 | callback(undefined); 355 | }); 356 | }); 357 | }; 358 | 359 | // run a bunch of calls in series.. test will fail if any return an error 360 | function createApp(vmc, appName, appDir, cb) { 361 | async.series([ 362 | function(callback) { vmc.login(callback);}, 363 | function(callback) { vmc.deleteApp(appName, function(err, data){ callback();});}, // ignore any error from delete 364 | function(callback) { vmc.push(appName, appDir, callback);}, 365 | function(callback) { vmc.deleteService('redis-' + appName, function(err, data){ callback();});}, // ignore any error from delete 366 | function(callback) { vmc.createService('redis-' + appName, 'redis', callback);}, 367 | function(callback) { vmc.bindService('redis-' + appName, appName, callback);}, 368 | function(callback) { vmc.start(appName, callback);}, 369 | function(callback) { vmc.addEnv(appName, 'variable1', 'value1', callback);}, 370 | function(callback) { vmc.delEnv(appName, 'variable1', callback);} 371 | ], cb); 372 | }; 373 | --------------------------------------------------------------------------------