├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── getServer.js ├── listApps.js └── listServers.js ├── index.js ├── lib └── serverpilot.js ├── package.json └── test ├── apps.test.js ├── databases.test.js ├── dummy.cert ├── dummy.key ├── serverpilot.test.js ├── servers.test.js └── sysusers.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Joshua P. Larson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Node.JS Wrapper for the ServerPilot API 2 | ================ 3 | 4 | [![Build Status](https://travis-ci.org/jplhomer/serverpilot-node.svg?branch=master)](https://travis-ci.org/jplhomer/serverpilot-node) 5 | 6 | This is a simple Node.JS wrapper to communicate with the [ServerPilot](http://serverpilot.io) API. 7 | 8 | Check out the [official ServerPilot API documentation](https://github.com/ServerPilot/API) for more information on using the ServerPilot API. 9 | 10 | ## Installation 11 | 12 | To start making calls to ServerPilot from Node.JS, install it with [NPM](http://npmjs.org): 13 | 14 | ```sh 15 | $ npm install serverpilot --save 16 | ``` 17 | 18 | ## Getting Started 19 | 20 | Log into your [ServerPilot](http://serverpilot.io) account, navigate to the Account section, and click API. Generate an API Key. 21 | 22 | Create the ServerPilot client as follows: 23 | 24 | ```js 25 | var ServerPilot = require('../lib/serverpilot'); 26 | var sp = new ServerPilot({ 27 | clientId: , 28 | apiKey: 29 | }); 30 | ``` 31 | 32 | ## Usage 33 | 34 | ### Servers 35 | 36 | Servers are Ubuntu 12.04 or 14.04 machines that are managed by ServerPilot. 37 | 38 | Every App, Database, and System User is related to a Server. By default, all 39 | Servers will have a `serverpilot` System User. 40 | 41 | [Learn more about what is involved in creating a server](https://github.com/ServerPilot/API#connect-a-new-server). 42 | 43 | ```js 44 | // List servers 45 | sp.getServers( function(err, data) { 46 | if ( err ) { console.log( err.message ); } 47 | 48 | // This is where you would do something with the data 49 | console.log(data.data); 50 | }); 51 | 52 | // Get a single server 53 | var serverId = '123456'; 54 | sp.getServer(serverId, function(err, data) {}); 55 | 56 | // Create a server 57 | var serverName = 'newserver'; 58 | sp.createServer(serverName, function(err, data){}); 59 | 60 | // Update a server 61 | sp.updateServer({ 62 | serverId: serverId, 63 | firewall: firewall, // Boolean 64 | autoupdates: autoupdates // Boolean 65 | },function(err, data) {}); 66 | 67 | // Delete a server 68 | sp.deleteServer(serverId, function(err, data) {} ); 69 | ``` 70 | 71 | ### System Users 72 | 73 | System Users are Linux user accounts that ServerPilot creates on your Server. 74 | Every App belongs to and runs as one of these System Users. You can log in to 75 | your Server as a System User via SSH to deploy an App or view an App's log 76 | files. 77 | 78 | By default, ServerPilot creates the `serverpilot` System User. 79 | 80 | The home directory of each System User created by ServerPilot is 81 | 82 | ``` 83 | /srv/users/USERNAME 84 | ``` 85 | 86 | Under the System User's home directory are additional directories: 87 | 88 | * `apps` — each app has its own directory under here. 89 | * `log` — each app has its log files here. 90 | 91 | **You must be a paid user** to add/edit system users. An error will be 92 | thrown if you are on the free plan. 93 | 94 | ```js 95 | // List system users 96 | sp.getSysUsers(function(err, data) {}); 97 | 98 | // Get a single system user 99 | var sysUserId = 'test123' 100 | sp.getSysUser(sysUserId, function(err, data) {} ); 101 | 102 | // Create a system user 103 | sp.createSysUser({ 104 | serverid: serverId, 105 | name: name, 106 | password: password 107 | }, function(err, data) {} ); 108 | 109 | // Update a system user's password 110 | sp.updateSysUser({ 111 | sysUserId: sysUserId, 112 | password: 'test45678' // Must be at least 8 characters 113 | }, function(err, data) {}); 114 | 115 | // Delete a system user 116 | sp.deleteSysUser(sysUserId, function(err, data) {}); 117 | ``` 118 | 119 | ### Apps 120 | 121 | Apps are your web applications. Sometimes people call apps "websites". 122 | 123 | ServerPilot currently supports PHP 5.4, 5.5 and 5.6 apps. 124 | 125 | ServerPilot configures your servers with Nginx as the public-facing webserver. 126 | Nginx serves static files and all other requests are proxied to Apache 2.4 so 127 | that you can use .htaccess files. PHP is configured to run via FPM. Each app 128 | can have multiple MySQL databases. 129 | 130 | The web root directory for each app is: 131 | 132 | ``` 133 | /srv/users/serverpilot/apps/{APPNAME}/public 134 | ``` 135 | 136 | ServerPilot does not manage DNS for you. In order for you to access your apps 137 | by their domain name, you must make the appropriate changes in your domain's 138 | DNS zone so that your domain name resolves to your server's IP address. You can 139 | do this where you currently manage DNS for your domain. 140 | 141 | ```js 142 | // List apps 143 | sp.getApps(function(err, data) {}); 144 | 145 | // Get a single app 146 | var appId = 'test123'; 147 | sp.getApp(appId, function(err, data) {}); 148 | 149 | // Create an app 150 | sp.createApp({ 151 | name: 'testapp', 152 | sysuserid: 'abc123', 153 | runtime: 'php5.5', 154 | domains: ['testapp.com','www.testapp.com'] 155 | }, function(err, data) {}); 156 | 157 | // Update an app's runtime or domains 158 | sp.updateApp({ 159 | appId: appId, 160 | runtime: 'php5.6', 161 | domains: ['mail.google.com','google.com'] 162 | }, function(err, data) {}); 163 | 164 | // Delete an app 165 | sp.deleteApp(appId, function(err, data) {}); 166 | 167 | // Add SSL to an app 168 | sp.addSSL({ 169 | appId: appId, 170 | key: sslKey, // Text version of your key 171 | cert: sslCert, // Text version of your cert 172 | cacerts: null // Any CA Certs. Null is OK. 173 | }, function(err, data) {}); 174 | 175 | // Delete SSL from an app 176 | sp.deleteSSL(appId, function(err, data) {}); 177 | ``` 178 | 179 | ### Databases 180 | 181 | Databases are MySQL databases. Each Database is associated with an App. 182 | 183 | There is only one Database User for each Database. 184 | 185 | ```js 186 | // List databases 187 | sp.getDatabases(function(err, data) {}); 188 | 189 | // Get a single database 190 | var databaseId = 'test1234'; 191 | sp.getDatabase(databaseId, function(err, data) {}); 192 | 193 | // Create a database 194 | sp.createDatabase({ 195 | appid: appId, 196 | name: databaseName, 197 | user: { 198 | name: databaseUserName, 199 | password: databaseUserPass 200 | } 201 | }, function(err, data) {}); 202 | 203 | // Update a database user's password 204 | sp.updateDatabase({ 205 | databaseId: databaseId, 206 | user: { 207 | id: databaseUserId, 208 | password: 'mynewpassword' 209 | } 210 | }, function(err, data) {}); 211 | 212 | // Delete a database 213 | sp.deleteDatabase(databaseId, function(err, data) {}); 214 | ``` 215 | 216 | ### Actions 217 | 218 | Actions are a record of work done on ServerPilot resources. These can be things 219 | like the creation of an App, deploying SSL, deleting an old Database, etc. 220 | 221 | All methods that modify a resource will have an `actionid` top-level key in the 222 | JSON response. The `actionid` can be used to track the `status` of the Action. 223 | 224 | **Possible values of Action `status`** 225 | 226 | | Status | Description 227 | | --------- | ----------- 228 | | `success` | Action was completed successfully. 229 | | `open` | Action has not completed yet. 230 | | `error` | Action has completed but there were errors. 231 | 232 | ```js 233 | var actionId = 'test1234'; 234 | sp.getActionStatus(actionId, function(err, data) {}); 235 | ``` 236 | 237 | 238 | ## Unit Tests 239 | 240 | To run tests, ensure you have Mocha installed on your system. 241 | 242 | ```sh 243 | $ npm install -g mocha 244 | ``` 245 | 246 | Next, set your environmental variables like this: 247 | 248 | ```sh 249 | $ export SP_CLIENT_ID=YOURCLIENTID SP_API_KEY=YOURAPIKEY 250 | ``` 251 | 252 | Then run all the tests using this handy shortcut: 253 | 254 | ```sh 255 | $ npm test 256 | ``` 257 | 258 | Or run an individual test like this: 259 | 260 | ```sh 261 | $ mocha test/apps.test.js -t 15000 262 | ``` 263 | 264 | ## License 265 | 266 | MIT. See the License file for more info. 267 | -------------------------------------------------------------------------------- /examples/getServer.js: -------------------------------------------------------------------------------- 1 | var ServerPilot = require('../lib/serverpilot'); 2 | var sp = new ServerPilot({ 3 | clientId: process.env.SP_CLIENT_ID, 4 | apiKey: process.env.SP_API_KEY 5 | }); 6 | var serverId = 'DIAo3w871vFkwHPy'; 7 | 8 | // List out servers 9 | var server = sp.getServer( serverId, function(err, data) { 10 | if ( err ) { console.log( err.message ); } 11 | 12 | console.log(data.data); 13 | }); -------------------------------------------------------------------------------- /examples/listApps.js: -------------------------------------------------------------------------------- 1 | var ServerPilot = require('../lib/serverpilot'); 2 | var sp = new ServerPilot({ 3 | clientId: process.env.SP_CLIENT_ID, 4 | apiKey: process.env.SP_API_KEY 5 | }); 6 | 7 | // List out servers 8 | var apps = sp.getApps( function(err, data) { 9 | if ( err ) { console.log( err.message ); } 10 | 11 | console.log(data.data); 12 | }); -------------------------------------------------------------------------------- /examples/listServers.js: -------------------------------------------------------------------------------- 1 | var ServerPilot = require('../lib/serverpilot'); 2 | var sp = new ServerPilot({ 3 | clientId: process.env.SP_CLIENT_ID, 4 | apiKey: process.env.SP_API_KEY 5 | }); 6 | 7 | // List out servers 8 | var servers = sp.getServers( function(err, data) { 9 | if ( err ) { console.log( err.message ); } 10 | 11 | console.log(data.data); 12 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/serverpilot'); -------------------------------------------------------------------------------- /lib/serverpilot.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var rest = require('restler'); 4 | var pkg = require('../package.json'); 5 | 6 | function isEmptyObject(obj) { 7 | return !Object.keys(obj).length; 8 | } 9 | 10 | var ServerPilot = function( options ) { 11 | if ( ! options || isEmptyObject(options) ) { 12 | throw new Error('You must send an options object'); 13 | } 14 | 15 | if ( ! options.clientId || ! options.apiKey ) { 16 | throw new Error('You must define a Client ID and API Key'); 17 | } 18 | 19 | this.VERSION = pkg.version; 20 | this.options = options; 21 | this.baseURL = 'https://api.serverpilot.io/v1'; 22 | }; 23 | 24 | module.exports = ServerPilot; 25 | 26 | /** 27 | * UTILITIES 28 | */ 29 | 30 | /** 31 | * Utility delete function 32 | * @param {string} url URL 33 | * @param {Function} callback Callback 34 | * @return {void} 35 | */ 36 | ServerPilot.prototype._del = function( url, callback ) { 37 | if ( typeof callback !== 'function' ) { 38 | throw new Error('Valid callback function required'); 39 | } 40 | 41 | if ( url.charAt(0) == '/' ) { 42 | url = this.baseURL + url; 43 | } 44 | 45 | rest.del( url, { 46 | username: this.options.clientId, 47 | password: this.options.apiKey 48 | }).on('success', function(result, response) { 49 | callback(null, result); 50 | }).on('fail', function(err, response) { 51 | var error = new Error(err.error.message); 52 | callback(error, null); 53 | }); 54 | }; 55 | 56 | /** 57 | * Utility GET function 58 | * @param {string} url URL endpoint to hit 59 | * @param {Function} callback Callback 60 | * @return {void} 61 | */ 62 | ServerPilot.prototype._get = function( url, callback ) { 63 | 64 | if ( typeof callback !== 'function' ) { 65 | throw new Error('Valid callback function required'); 66 | } 67 | 68 | if ( url.charAt(0) == '/' ) { 69 | url = this.baseURL + url; 70 | } 71 | 72 | rest.get( url, { 73 | username: this.options.clientId, 74 | password: this.options.apiKey 75 | }).on('success', function(result, response) { 76 | callback(null, result); 77 | }).on('fail', function(err, response) { 78 | var error = new Error(err.error.message); 79 | callback(error, null); 80 | }); 81 | }; 82 | 83 | /** 84 | * Utility function to handle responses 85 | * @param {mixed} err Possible error object 86 | * @param {mixed} response Possible response 87 | * @param {Function} callback Callback 88 | * @return {void} 89 | */ 90 | ServerPilot.prototype._handleResponse = function( err, response, callback ) { 91 | if ( err ) { 92 | callback(err, null); 93 | } else { 94 | callback(null, response); 95 | } 96 | }; 97 | 98 | /** 99 | * Utility POST function 100 | * @param {string} url URL endpoint to hit 101 | * @param {Function} callback Callback 102 | * @return {void} 103 | */ 104 | ServerPilot.prototype._post = function( url, data, callback ) { 105 | 106 | if ( typeof callback !== 'function' ) { 107 | throw new Error('Valid callback function required'); 108 | } 109 | 110 | if ( url.charAt(0) == '/' ) { 111 | url = this.baseURL + url; 112 | } 113 | 114 | rest.post( url, { 115 | username: this.options.clientId, 116 | password: this.options.apiKey, 117 | headers: { 118 | 'Content-Type': 'application/json' 119 | }, 120 | data: JSON.stringify(data) 121 | }).on('success', function(result, response) { 122 | callback(null, result); 123 | }).on('fail', function(err, response) { 124 | var error = new Error(err.error.message); 125 | callback(error, null); 126 | }); 127 | }; 128 | 129 | /** 130 | * ACTIONS 131 | */ 132 | 133 | /** 134 | * Get the status of an action, like a database create or app create 135 | * @param {string} id ID of the action 136 | * @param {Function} callback Callback 137 | * @return {void} 138 | */ 139 | ServerPilot.prototype.getActionStatus = function( id, callback ) { 140 | if ( ! id ) { 141 | throw new Error('You must provide an ID for the action'); 142 | } 143 | 144 | this._get('/actions/' + id, function(err, response) { 145 | this._handleResponse(err, response, callback); 146 | }.bind(this)); 147 | }; 148 | 149 | /** 150 | * APPS 151 | */ 152 | 153 | /** 154 | * Add an SSL Certificate to an app 155 | * @param {object} options Options 156 | * @param {Function} callback Callback 157 | */ 158 | ServerPilot.prototype.addSSL = function( options, callback ) { 159 | if ( ! options || isEmptyObject(options) ) { 160 | throw new Error('You must provide an SSL Key, Certificate, and CA Certificate'); 161 | } 162 | 163 | if ( ! options.appId ) { 164 | throw new Error('You must provide an App ID'); 165 | } 166 | 167 | if ( ! options.key ) { 168 | throw new Error('You must provide an SSL Key'); 169 | } 170 | 171 | if ( ! options.cert ) { 172 | throw new Error('You must provide an SSL Certificate'); 173 | } 174 | 175 | // Make sure it's set to at least null 176 | if ( ! options.cacerts ) { 177 | options.cacerts = null; 178 | } 179 | 180 | var id = options.appId; 181 | delete(options.appId); 182 | 183 | this._post('/apps/' + id + '/ssl', options, function(err, response) { 184 | this._handleResponse(err, response, callback); 185 | }.bind(this)); 186 | }; 187 | 188 | /** 189 | * Create an app 190 | * @param {object} options Options 191 | * @param {Function} callback Callback 192 | * @return {void} 193 | */ 194 | ServerPilot.prototype.createApp = function( options, callback ) { 195 | if ( ! options || isEmptyObject(options) ) { 196 | throw new Error('You must pass options for: name, runtime, sysuserid'); 197 | } 198 | 199 | if ( ! ('name' in options) 200 | || ! ('sysuserid' in options) 201 | || ! ('runtime' in options) ) { 202 | throw new Error('You must pass options for: name, runtime, sysuserid'); 203 | } 204 | 205 | this._post('/apps', options, function(err, response) { 206 | this._handleResponse(err, response, callback); 207 | }.bind(this)); 208 | }; 209 | 210 | /** 211 | * Delete an app 212 | * @param {string} id App ID 213 | * @param {Function} callback Callback 214 | * @return {void} 215 | */ 216 | ServerPilot.prototype.deleteApp = function( id, callback ) { 217 | if ( ! id || ! id.length ) { 218 | throw new Error('You must pass an app id'); 219 | } 220 | 221 | this._del('/apps/' + id, function(err, response) { 222 | this._handleResponse(err, response, callback); 223 | }.bind(this)); 224 | }; 225 | 226 | /** 227 | * Delete SSL from an app 228 | * @param {string} id App ID 229 | * @param {Function} callback Callback 230 | * @return {void} 231 | */ 232 | ServerPilot.prototype.deleteSSL = function( id, callback ) { 233 | if ( ! id ) { 234 | throw new Error('You must pass an app id'); 235 | } 236 | 237 | this._del('/apps/' + id + '/ssl', function(err, response) { 238 | this._handleResponse(err, response, callback); 239 | }.bind(this)); 240 | }; 241 | 242 | /** 243 | * Get a single app 244 | * @param {string} id Server ID 245 | * @param {Function} callback Callback 246 | * @return {void} 247 | */ 248 | ServerPilot.prototype.getApp = function( id, callback ) { 249 | this._get('/apps/' + id, function(err, response) { 250 | this._handleResponse(err, response, callback); 251 | }.bind(this)); 252 | }; 253 | 254 | /** 255 | * Get all apps 256 | * @param {Function} callback Callback 257 | * @return {void} 258 | */ 259 | ServerPilot.prototype.getApps = function( callback ) { 260 | this._get('/apps', function(err, response) { 261 | this._handleResponse(err, response, callback); 262 | }.bind(this)); 263 | }; 264 | 265 | /** 266 | * Update an app 267 | * @param {object} options Options 268 | * @param {Function} callback Callback 269 | * @return {void} 270 | */ 271 | ServerPilot.prototype.updateApp = function( options, callback ) { 272 | if ( ! options || isEmptyObject(options) ) { 273 | throw new Error('You must pass an options object'); 274 | } 275 | 276 | if ( ! ('appId' in options) ) { 277 | throw new Error('You must pass a valid App ID'); 278 | } 279 | 280 | if ( ! ('runtime' in options) && ! ('domains' in options) ) { 281 | throw new Error('You must pass at least one of the following: runtime, domains'); 282 | } 283 | 284 | var appId = options.appId; 285 | delete(options.appId); 286 | 287 | this._post('/apps/' + appId, options, function(err, response) { 288 | this._handleResponse(err, response, callback); 289 | }.bind(this)); 290 | } 291 | 292 | /** 293 | * SERVERS 294 | */ 295 | 296 | /** 297 | * Create a server 298 | * @param {string} name Name of the server 299 | * @param {Function} callback Callback 300 | * @return {void} 301 | */ 302 | ServerPilot.prototype.createServer = function( name, callback ) { 303 | if ( ! name || ! name.length ) { 304 | throw new Error('You must specify a name for the server'); 305 | } 306 | 307 | // TODO: Validate name of server 308 | // The nickname of the Server. Length must be between 1 and 255 characters. Characters can be of lowercase ascii letters, digits, a period, or a dash ('abcdefghijklmnopqrstuvwxyz0123456789-'), but must start with a lowercase ascii letter and end with either a lowercase ascii letter or digit. www.store2 is a valid name, while .org.company nor www.blog- are. 309 | 310 | this._post('/servers', { 311 | name: name 312 | }, function(err, response) { 313 | this._handleResponse(err, response, callback); 314 | }.bind(this)); 315 | }; 316 | 317 | /** 318 | * Detete a server 319 | * @param {string} id Server ID 320 | * @param {Function} callback Callback 321 | * @return {void} 322 | */ 323 | ServerPilot.prototype.deleteServer = function( id, callback ) { 324 | if ( ! id || ! id.length ) { 325 | throw new Error('You must pass a valid Server ID'); 326 | } 327 | 328 | this._del('/servers/' + id, function(err, response) { 329 | this._handleResponse(err, response, callback); 330 | }.bind(this)); 331 | }; 332 | 333 | /** 334 | * Get a single server 335 | * @param {string} id Server ID 336 | * @param {Function} callback Callback 337 | * @return {void} 338 | */ 339 | ServerPilot.prototype.getServer = function( id, callback ) { 340 | this._get('/servers/' + id, function(err, response) { 341 | this._handleResponse(err, response, callback); 342 | }.bind(this)); 343 | }; 344 | 345 | /** 346 | * Get all servers 347 | * @param {Function} callback Callback 348 | * @return {void} 349 | */ 350 | ServerPilot.prototype.getServers = function( callback ) { 351 | this._get('/servers', function(err, response) { 352 | this._handleResponse(err, response, callback); 353 | }.bind(this)); 354 | }; 355 | 356 | /** 357 | * Update a server 358 | * @param {object} options Options object 359 | * @param {Function} callback Callback 360 | * @return {void} 361 | */ 362 | ServerPilot.prototype.updateServer = function( options, callback ) { 363 | if ( ! options || isEmptyObject(options) ) { 364 | throw new Error('You must pass a Server ID and one of: autoupdates, firewall'); 365 | } 366 | 367 | if ( ! ('serverId' in options) || ! options.serverId.length ) { 368 | throw new Error('You must pass a valid Server ID'); 369 | } 370 | 371 | if ( ! ('autoupdates' in options) && ! ('firewall' in options) ) { 372 | throw new Error('You must pass in arguments for at least autoupdates or firewall'); 373 | } 374 | 375 | if ( ! ( ('autoupdates' in options) && typeof options.autoupdates === 'boolean' ) 376 | && ! ( ('firewall' in options) && typeof options.firewall === 'boolean' ) ) { 377 | throw new Error('Arguments for autoupdates and firewall must be boolean'); 378 | } 379 | 380 | var serverId = options.serverId; 381 | delete(options.serverId); 382 | 383 | this._post('/servers/' + serverId, options, function(err, response) { 384 | this._handleResponse(err, response, callback); 385 | }.bind(this)); 386 | }; 387 | 388 | /** 389 | * SYSUSERS 390 | */ 391 | 392 | /** 393 | * Delete a system user 394 | * @param {string} id System User ID 395 | * @param {Function} callback Callback 396 | * @return {void} 397 | */ 398 | ServerPilot.prototype.deleteSysUser = function( id, callback ) { 399 | if ( ! id ) { 400 | throw new Error('You must provide a System User ID'); 401 | } 402 | 403 | this._del('/sysusers/' + id, function(err, response) { 404 | this._handleResponse(err, response, callback); 405 | }.bind(this)); 406 | }; 407 | 408 | /** 409 | * Add a system user 410 | * @param {object} options Options 411 | * @param {Function} callback Callback 412 | */ 413 | ServerPilot.prototype.createSysUser = function( options, callback ) { 414 | if ( ! options || isEmptyObject(options) ) { 415 | throw new Error('You must pass valid options'); 416 | } 417 | 418 | if ( ! ('name' in options) ) { 419 | throw new Error('You must specify a Name'); 420 | } 421 | 422 | if ( ! ('serverid' in options) ) { 423 | throw new Error('You must specify a Server ID'); 424 | } 425 | 426 | // TODO: 427 | // Name can only contain the characters abcdefghijklmnopqrstuvwxyz0123456789- 428 | 429 | this._post('/sysusers', options, function(err, response) { 430 | this._handleResponse(err, response, callback); 431 | }.bind(this)); 432 | }; 433 | 434 | /** 435 | * Get a single system user 436 | * @param {string} id SysUser ID 437 | * @param {Function} callback Callback 438 | * @return {ServerPilot} 439 | */ 440 | ServerPilot.prototype.getSysUser = function( id, callback ) { 441 | if ( ! id ) { 442 | throw new Error('You must provide a System User ID'); 443 | } 444 | 445 | this._get('/sysusers/' + id, function(err, response) { 446 | this._handleResponse(err, response, callback); 447 | }.bind(this)); 448 | }; 449 | 450 | /** 451 | * Get system users 452 | * @param {Function} callback callback 453 | * @return {void} 454 | */ 455 | ServerPilot.prototype.getSysUsers = function( callback ) { 456 | this._get('/sysusers', function(err, response) { 457 | this._handleResponse(err, response, callback); 458 | }.bind(this)); 459 | }; 460 | 461 | /** 462 | * Update a system user 463 | * @param {object} options System User ID and password 464 | * @param {Function} callback Callback 465 | * @return {void} 466 | */ 467 | ServerPilot.prototype.updateSysUser = function( options, callback ) { 468 | if ( ! options ) { 469 | throw new Error('You must provide a password and a System User ID to update'); 470 | } 471 | 472 | if ( isEmptyObject(options) ) { 473 | throw new Error('You must provide a password and a System User ID to update'); 474 | } 475 | 476 | if ( ! options.password ) { 477 | throw new Error('You must provide a password to update'); 478 | } 479 | 480 | if ( options.password.length < 8 ) { 481 | throw new Error('Password must be at least 8 characters'); 482 | } 483 | 484 | if ( ! options.sysUserId ) { 485 | throw new Error('You must provide a System User ID to update'); 486 | } 487 | 488 | var id = options.sysUserId; 489 | delete(options.sysUserId); 490 | 491 | this._post('/sysusers/' + id, options, function(err, response) { 492 | this._handleResponse(err, response, callback); 493 | }.bind(this)); 494 | }; 495 | 496 | /** 497 | * DATABASES 498 | */ 499 | 500 | /** 501 | * Create a database 502 | * @param {object} options Options 503 | * @param {Function} callback Callback 504 | * @return {void} 505 | */ 506 | ServerPilot.prototype.createDatabase = function( options, callback ) { 507 | if ( ! options ) { 508 | throw new Error('You must provide options'); 509 | } 510 | 511 | if ( ! options.appid ) { 512 | throw new Error('You must provide an App ID'); 513 | } 514 | 515 | if ( ! options.name ) { 516 | throw new Error('You must provide a Database Name'); 517 | } 518 | 519 | if ( ! options.user ) { 520 | throw new Error('You must provide database user information'); 521 | } 522 | 523 | if ( ! options.user.name ) { 524 | throw new Error('You must provide a database user name'); 525 | } 526 | 527 | if ( ! options.user.password ) { 528 | throw new Error('You must provide a database user password'); 529 | } 530 | 531 | this._post('/dbs', options, function(err, response) { 532 | this._handleResponse(err, response, callback); 533 | }.bind(this)); 534 | }; 535 | 536 | /** 537 | * Delete a database 538 | * @param {string} id Database ID 539 | * @param {Function} callback Callback 540 | * @return {void} 541 | */ 542 | ServerPilot.prototype.deleteDatabase = function( id, callback ) { 543 | if ( ! id ) { 544 | throw new Error('You must provide a database ID'); 545 | } 546 | 547 | this._del('/dbs/' + id, function(err, response) { 548 | this._handleResponse(err, response, callback); 549 | }.bind(this)); 550 | }; 551 | 552 | /** 553 | * Get a database 554 | * @param {string} id Database ID 555 | * @param {Function} callback Callback 556 | * @return {void} 557 | */ 558 | ServerPilot.prototype.getDatabase = function( id, callback ) { 559 | if ( ! id ) { 560 | throw new Error('You must pass a database ID'); 561 | } 562 | 563 | this._get('/dbs/' + id, function(err, response) { 564 | this._handleResponse(err, response, callback); 565 | }.bind(this)); 566 | }; 567 | 568 | /** 569 | * Get all databases 570 | * @param {Function} callback Callback 571 | * @return {void} 572 | */ 573 | ServerPilot.prototype.getDatabases = function( callback ) { 574 | this._get('/dbs', function(err, response) { 575 | this._handleResponse(err, response, callback); 576 | }.bind(this)); 577 | }; 578 | 579 | /** 580 | * Update a database with a new password for a user 581 | * @param {object} options Options 582 | * @param {Function} callback Callback 583 | * @return {void} 584 | */ 585 | ServerPilot.prototype.updateDatabase = function( options, callback ) { 586 | if ( ! options ) { 587 | throw new Error('You must pass options'); 588 | } 589 | 590 | if ( ! options.databaseId ) { 591 | throw new Error('You must provide a database ID'); 592 | } 593 | 594 | if ( ! options.user ) { 595 | throw new Error('You must provide a database user name and a new password'); 596 | } 597 | 598 | if ( ! options.user.id ) { 599 | throw new Error('You must provide a database user ID'); 600 | } 601 | 602 | if ( ! options.user.password ) { 603 | throw new Error('You must provide a new password for the database user'); 604 | } 605 | 606 | var id = options.databaseId; 607 | delete(options.databaseId); 608 | 609 | this._post('/dbs/' + id, options, function(err, response) { 610 | this._handleResponse(err, response, callback); 611 | }.bind(this)); 612 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverpilot", 3 | "version": "0.1.2", 4 | "description": "A Node wrapper to communicate with the ServerPilot API", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test/*.js -t 15000" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/jplhomer/serverpilot-node.git" 12 | }, 13 | "keywords": [ 14 | "api", 15 | "serverpilot", 16 | "wrapper" 17 | ], 18 | "author": "Joshua P. Larson (http://jplhomer.org)", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/jplhomer/serverpilot-node/issues" 22 | }, 23 | "homepage": "https://github.com/jplhomer/serverpilot-node", 24 | "dependencies": { 25 | "mocha": "^2.0.1", 26 | "restler": "^3.2.2", 27 | "should": "^4.3.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/apps.test.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var fs = require('fs'); 3 | var ServerPilot = require('..'); 4 | var sp; 5 | var appId, serverId; 6 | var options = { 7 | name: 'testapp', 8 | sysuserid: '', 9 | runtime: 'php5.5', 10 | domains: ['testapp.com'] 11 | }; 12 | var sslKey, sslCert; 13 | 14 | function catchCreateAppException(opts) { 15 | return function() { 16 | try { 17 | sp.createApp(opts, function() {} ); 18 | } catch (e) { 19 | return; 20 | } 21 | throw new Error('No error throw by class with options: ' + JSON.stringify(opts)); 22 | }; 23 | } 24 | 25 | function catchUpdateAppException(opts) { 26 | return function() { 27 | try { 28 | sp.updateApp(opts, function() {} ); 29 | } catch(e) { 30 | return; 31 | } 32 | throw new Error('No error throw by class with options: ' + JSON.stringify(opts)); 33 | }; 34 | } 35 | 36 | function catchDeleteAppException(id) { 37 | return function() { 38 | try { 39 | sp.deleteApp(id, function() {} ); 40 | } catch(e) { 41 | return; 42 | } 43 | throw new Error('No error throw by class with id: ' + id); 44 | }; 45 | } 46 | 47 | function catchAddSSLException(opts) { 48 | return function() { 49 | try { 50 | sp.addSSL(opts, function() {} ); 51 | } catch(e) { 52 | return; 53 | } 54 | throw new Error('No error throw by class with options: ' + JSON.stringify(opts)); 55 | }; 56 | } 57 | 58 | function catchDeleteSSLException(id) { 59 | return function() { 60 | try { 61 | sp.deleteSSL(id, function() {} ); 62 | } catch(e) { 63 | return; 64 | } 65 | throw new Error('No error throw by class with id: ' + id); 66 | }; 67 | } 68 | 69 | describe('Apps', function() { 70 | 71 | before(function(done) { 72 | sp = new ServerPilot({ 73 | clientId: process.env.SP_CLIENT_ID, 74 | apiKey: process.env.SP_API_KEY 75 | }); 76 | 77 | // Create a dummy server 78 | sp.createServer('testserver' + process.version, function(err, data) { 79 | if (err) { console.log(err.message); } 80 | 81 | serverId = data.data.id; 82 | 83 | // Get the sysuserid of that server 84 | sp.getSysUsers(function(err, data) { 85 | var sysusers = data.data; 86 | 87 | for (var i = sysusers.length - 1; i >= 0; i--) { 88 | if (sysusers[i].serverid === serverId) { 89 | options.sysuserid = sysusers[i].id; 90 | 91 | // Load in the contents of the SSL Key and Cert 92 | fs.readFile(__dirname + '/dummy.key', 'ascii', function(err, data) { 93 | if (err) { return done(err); } 94 | 95 | sslKey = data; 96 | 97 | fs.readFile(__dirname + '/dummy.cert', 'ascii', function(err, data) { 98 | if (err) { return done(err); } 99 | 100 | sslCert = data; 101 | 102 | done(); 103 | }) 104 | }); 105 | } 106 | } 107 | }); 108 | }); 109 | }); 110 | 111 | describe('.getApps()', function() { 112 | it('should get all apps', function(done) { 113 | sp.getApps(function(err, data) { 114 | if (err) { return done(err); } 115 | data.should.be.an.Object; 116 | data.data.should.be.an.Object; 117 | done(); 118 | }); 119 | }); 120 | }); 121 | 122 | describe('.createApp(options)', function() { 123 | it('should throw when no options passed', catchCreateAppException()); 124 | it('should throw when empty options passed', catchCreateAppException({})); 125 | it('should throw when no name passed', catchCreateAppException({ sysuserid: options.sysuserid, runtime: options.runtime })); 126 | it('should throw when no sysuserid passed', catchCreateAppException({ name: options.name, runtime: options.runtime })); 127 | it('should throw when no runtime passed', catchCreateAppException({ sysuserid: options.sysuserid, name: options.name })); 128 | it('should create an app', function(done) { 129 | sp.createApp(options, function(err, data) { 130 | if (err) { return done(err); } 131 | 132 | data.data.name.should.eql(options.name); 133 | data.data.runtime.should.eql(options.runtime); 134 | data.data.domains.should.eql(options.domains); 135 | 136 | // Set stuff to use later 137 | appId = data.data.id; 138 | 139 | done(); 140 | }); 141 | }); 142 | }); 143 | 144 | describe('.getApp(id)', function() { 145 | it('should get an app', function(done) { 146 | sp.getApp( appId, function(err, data) { 147 | if (err) { return done(err); } 148 | data.should.be.an.Object; 149 | data.data.should.be.an.Object; 150 | done(); 151 | }); 152 | }); 153 | }); 154 | 155 | describe('.updateApp(options)', function() { 156 | it('should throw when no options passed', catchUpdateAppException()); 157 | it('should throw when empty options passed', catchUpdateAppException({})); 158 | it('should throw when no appId passed', catchUpdateAppException({ runtime: 'php5.6' })); 159 | it('should throw when neither are passed: runtime, domains', catchUpdateAppException({ appId: appId })); 160 | it('should update app', function(done) { 161 | var opts = { 162 | appId: appId, 163 | runtime: 'php5.6', 164 | domains: ['mail.google.com','google.com'] 165 | }; 166 | sp.updateApp(opts, function(err, data) { 167 | if (err) { return done(err); } 168 | 169 | data.data.runtime.should.eql(opts.runtime); 170 | // data.data.domains.should.eql(opts.domains); // ServerPilot switches 171 | done(); 172 | }) 173 | }) 174 | }); 175 | 176 | describe.skip('.addSSL(options)', function() { 177 | it('should throw when no options passed', catchAddSSLException()); 178 | it('should throw when no appId passed', catchAddSSLException({ key: sslKey, cert: sslCert, cacerts: null })); 179 | it('should throw when no key passed', catchAddSSLException({ appId: appId, cert: sslCert, cacerts: null })); 180 | it('should throw when no cert passed', catchAddSSLException({ appId: appId, key: sslKey, cacerts: null })); 181 | it('should add SSL to the app', function(done) { 182 | sp.addSSL({ 183 | appId: appId, 184 | key: sslKey, 185 | cert: sslCert, 186 | cacerts: null 187 | }, function(err, data) { 188 | if (err) { return done(err); } 189 | 190 | data.data.key.should.eql(sslKey); 191 | data.data.cert.should.eql(sslCert); 192 | should(data.data.cacerts).not.be.ok; 193 | done(); 194 | }) 195 | }); 196 | }); 197 | 198 | describe.skip('.deleteSSL(id)', function() { 199 | it('should throw when no appId passed', catchDeleteSSLException()); 200 | it('should delete SSL from an app', function(done) { 201 | sp.deleteSSL(appId, function(err, data) { 202 | if (err) { return done(err); } 203 | 204 | data.data.should.not.have.a.length; 205 | 206 | done(); 207 | }); 208 | }); 209 | }); 210 | 211 | describe('.deleteApp(id)', function() { 212 | it('should throw when no id passed', catchDeleteAppException()); 213 | it('should delete the app', function(done) { 214 | sp.deleteApp( appId, function(err, data) { 215 | if (err) { return done(err); } 216 | 217 | sp.getApp(appId, function(err, data) { 218 | if ( err ) { 219 | done(); 220 | } 221 | }); 222 | }); 223 | }) 224 | }); 225 | 226 | after(function(done) { 227 | // Destroy the server 228 | sp.deleteServer(serverId, function(err, data) { 229 | done(); 230 | }); 231 | }); 232 | 233 | }); 234 | -------------------------------------------------------------------------------- /test/databases.test.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var ServerPilot = require('..'); 3 | var sp, appId, serverId, sysUserId, databaseId, databaseUserId; 4 | var databaseName = 'testdatabase', 5 | databaseUserName = 'testdatauser' 6 | databaseUserPass = 'testdatauserpass'; 7 | 8 | function catchCreateDatabaseException(options) { 9 | return function() { 10 | try { 11 | sp.createDatabase(options, function() {} ); 12 | } catch(e) { 13 | return; 14 | } 15 | throw new Error('No error throw from class with options:' + JSON.stringify(options)); 16 | }; 17 | } 18 | 19 | function catchGetDatabaseException(id) { 20 | return function() { 21 | try { 22 | sp.getDatabase(id, function() {} ); 23 | } catch(e) { 24 | return; 25 | } 26 | throw new Error('No error throw from class with ID:' + id); 27 | }; 28 | } 29 | 30 | function catchUpdateDatabaseException(options) { 31 | return function() { 32 | try { 33 | sp.updateDatabase(options, function() {} ); 34 | } catch(e) { 35 | return; 36 | } 37 | throw new Error('No error throw from class with options:' + JSON.stringify(options)); 38 | }; 39 | } 40 | 41 | function catchDeleteDatabaseException(id) { 42 | return function() { 43 | try { 44 | sp.deleteDatabase(id, function() {} ); 45 | } catch(e) { 46 | return; 47 | } 48 | throw new Error('No error throw from class with id:' + id); 49 | }; 50 | } 51 | 52 | describe('Databases', function() { 53 | 54 | before(function(done) { 55 | sp = new ServerPilot({ 56 | clientId: process.env.SP_CLIENT_ID, 57 | apiKey: process.env.SP_API_KEY 58 | }); 59 | 60 | // Create a dummy server 61 | sp.createServer('testserver' + process.version, function(err, data) { 62 | if (err) { console.log(err.message); } 63 | 64 | serverId = data.data.id; 65 | 66 | // Get the sysuserid of that server 67 | sp.getSysUsers(function(err, data) { 68 | var sysusers = data.data; 69 | 70 | for (var i = sysusers.length - 1; i >= 0; i--) { 71 | if (sysusers[i].serverid === serverId) { 72 | sysUserId = sysusers[i].id; 73 | } 74 | } 75 | 76 | // Create a dummy app 77 | sp.createApp({ 78 | name: 'testapp', 79 | sysuserid: sysUserId, 80 | runtime: 'php5.5', 81 | domains: ['testapp.com'] 82 | }, function(err, data) { 83 | appId = data.data.id; 84 | 85 | done(); 86 | }); 87 | }); 88 | }); 89 | }); 90 | 91 | describe('.getDatabases()', function() { 92 | it('should get all databases`', function(done) { 93 | sp.getDatabases(function(err, data) { 94 | if (err) { return done(err); } 95 | data.should.be.an.Object; 96 | data.data.should.be.an.Object; 97 | done(); 98 | }); 99 | }); 100 | }); 101 | 102 | describe('.createDatabase(options)', function() { 103 | it('should throw when no options passed', catchCreateDatabaseException()); 104 | it('should throw when no appId passed', catchCreateDatabaseException({ 105 | name: databaseName, 106 | user: { 107 | name: databaseUserName, 108 | password: databaseUserPass 109 | } 110 | })); 111 | it('should throw when no database name passed', catchCreateDatabaseException({ 112 | appid: appId, 113 | user: { 114 | name: databaseUserName, 115 | password: databaseUserPass 116 | } 117 | })); 118 | it('should throw when no user.name passed', catchCreateDatabaseException({ 119 | appid: appId, 120 | name: databaseName, 121 | user: { 122 | password: databaseUserPass 123 | } 124 | })); 125 | it('should throw when no user.password passed', catchCreateDatabaseException({ 126 | appid: appId, 127 | name: databaseName, 128 | user: { 129 | name: databaseUserName 130 | } 131 | })); 132 | it('should create a database', function(done) { 133 | sp.createDatabase({ 134 | appid: appId, 135 | name: databaseName, 136 | user: { 137 | name: databaseUserName, 138 | password: databaseUserPass 139 | } 140 | }, function(err, data) { 141 | if (err) { return done(err); } 142 | 143 | data.data.name.should.eql(databaseName); 144 | data.data.user.name.should.eql(databaseUserName); 145 | 146 | // Set id for future use 147 | databaseId = data.data.id; 148 | databaseUserId = data.data.user.id; 149 | 150 | done(); 151 | }); 152 | }); 153 | }); 154 | 155 | describe('.getDatabase(id)', function() { 156 | it('should throw when no id passed', catchGetDatabaseException()); 157 | it('should get a database', function(done) { 158 | sp.getDatabase(databaseId, function(err, data) { 159 | if (err) { return done(err); } 160 | 161 | data.data.id.should.eql(databaseId); 162 | 163 | done(); 164 | }); 165 | }); 166 | }); 167 | 168 | describe('.updateDatabase(options)', function() { 169 | it('should throw when no options passed', catchUpdateDatabaseException()); 170 | it('should throw when no user.id passed', catchUpdateDatabaseException({ 171 | databaseId: databaseId, 172 | name: databaseName, 173 | user: { 174 | password: 'mynewpassword' 175 | } 176 | })); 177 | it('should throw when no user.password passed', catchUpdateDatabaseException({ 178 | databaseId: databaseId, 179 | name: databaseName, 180 | user: { 181 | id: databaseUserId 182 | } 183 | })); 184 | it('should update database user password', function(done) { 185 | sp.updateDatabase({ 186 | databaseId: databaseId, 187 | user: { 188 | id: databaseUserId, 189 | password: 'mynewpassword' 190 | } 191 | }, function(err, data) { 192 | if (err) { return done(err); } 193 | 194 | // No way to verify password change 195 | done(); 196 | }); 197 | }); 198 | }); 199 | 200 | describe('.deleteDatabase(id)', function() { 201 | it('should throw when no id passed', catchDeleteDatabaseException()); 202 | it('should delete the database', function(done) { 203 | sp.deleteDatabase(databaseId, function(err, data) { 204 | if (err) { return done(err); } 205 | 206 | sp.getDatabase(databaseId, function(err, data) { 207 | if ( err ) { 208 | done(); 209 | } 210 | }); 211 | }); 212 | }); 213 | }); 214 | 215 | after(function(done) { 216 | sp.deleteServer(serverId, function(err, data) { 217 | done(); 218 | }); 219 | }); 220 | 221 | }); -------------------------------------------------------------------------------- /test/dummy.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDBTCCAe2gAwIBAgIJAJA2aM4DZqVTMA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNV 3 | BAMMDnNlcnZlcnBpbG90LmlvMB4XDTE0MTIwMTA1MDkyOVoXDTI0MTEyODA1MDky 4 | OVowGTEXMBUGA1UEAwwOc2VydmVycGlsb3QuaW8wggEiMA0GCSqGSIb3DQEBAQUA 5 | A4IBDwAwggEKAoIBAQDCetlOt2Cn3Gs2yL6VDtw2ScVLcsz7yxljdl2FWIfnhJkt 6 | Vf13QYGs2cTBPmWeDPJZgF55sLmtnT/qNTe/Bsc8l8U4TjowGzT8cxZGL9smXjPL 7 | Xs/fXdfIVkI/jELDT5ULKwUTKHhuKZwjrgc2MFiYCQ4h+wEq74WIkQGWAIEmXzir 8 | wtj/1o6j5CeuIEIRq0hACkQSlgHS9ySVXMWizo4AflqyH7tW06a9pwJmPUZ0NzXx 9 | j+zuPChGBpxyBvjL3SVpuwKMQ0VNZoXDCuld5tpc0/4qNOrZAaF3cT0+b4zKMliR 10 | Axk3L+AWcS9d5H1ykcG9nsmCOD27/bytWfznUDHJAgMBAAGjUDBOMB0GA1UdDgQW 11 | BBQUBIkjZfh+nR+9jWV5eiz10A8UmzAfBgNVHSMEGDAWgBQUBIkjZfh+nR+9jWV5 12 | eiz10A8UmzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA6OAmPBXCC 13 | 9gbl1Eud8wbROWbAw7MwRle0ZLAkG3mhALXzCQLzgEYffmNe4LYXvF0QuGP5x/ig 14 | yq6Qjfi9JSthrm992vTmbrPvPNZxfrMa6esNGRrSu/HxqaiJHbSNvZmsy/izYMcC 15 | F+TK6CwUb/d7EYSRS5WFJOQ/6E/5koQWh7hbk4a/k+WB0FLWNDtHpbd4ets+QaY2 16 | YOk7YUUdZxiXEyTAB4Y08PILOa+RLWaRrZExcROvbOl7JnWXwGb62UQPJYaQfaBr 17 | wjMcKl3czneE2Er3Tur92MKd9JUbhAfjeg8BbH10U3XiYnWv4XPCxvLuciaI6Ent 18 | Ruo7OLZCUdBX 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /test/dummy.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAwnrZTrdgp9xrNsi+lQ7cNknFS3LM+8sZY3ZdhViH54SZLVX9 3 | d0GBrNnEwT5lngzyWYBeebC5rZ0/6jU3vwbHPJfFOE46MBs0/HMWRi/bJl4zy17P 4 | 313XyFZCP4xCw0+VCysFEyh4bimcI64HNjBYmAkOIfsBKu+FiJEBlgCBJl84q8LY 5 | /9aOo+QnriBCEatIQApEEpYB0vcklVzFos6OAH5ash+7VtOmvacCZj1GdDc18Y/s 6 | 7jwoRgaccgb4y90labsCjENFTWaFwwrpXebaXNP+KjTq2QGhd3E9Pm+MyjJYkQMZ 7 | Ny/gFnEvXeR9cpHBvZ7Jgjg9u/28rVn851AxyQIDAQABAoIBAHk3ONrnUUdPC0RK 8 | Ov+NE5SCxoYQN9N484oa7I+AbrTO4Opw/lqmabzivs/zpEKurx8586jFjfW4XW4N 9 | XYGjJ+uMbVB68amqS4xN70Qv79gqPv4zSj1esHAd04zoT3SM1cjwN2mJgAapyLg0 10 | cCpGV/HrMvPbDw9D22cTenmUNutCuDLfxM5v5DicqZyLzCUsjcLF2hVGHwTwpqoq 11 | bySW3MGwwJOTeCTj7uBy8wzvFkeHROddBrn31SS6f4uP5ipqd6z8cXpScrpXMzJc 12 | 0bKZDMycOlm2gcvcEqL9XBE4vgW/dK8V/3TYO8qadFWbfuKApgmwqmcS807Kt5Na 13 | uRexcAECgYEA5pv0PbhXTf2HkqPyd60zSCO9ckR5mxteVljWltCFVyIg7+qgiXbw 14 | moXo19nUMC7+tHdlaQFBUTt6AdTCWtfAK4xymFogdvB2WCE7YS08t/ho6UtZXCPK 15 | g33z7imNtdlcrSR5nm1uKagyAf0Fz+pS9ITTSoTiVcbymq+dC5U8R2ECgYEA1+SJ 16 | +IunciHLK7G0ANXTB++nybalP6QlUTocVdWOsib6ogDUDbk0pbXGquZxfWk8Cah7 17 | DCeMi0FmxhhogRw4CXXf1qZt7hHe8k9im9j1JKnAXwlErEYf9pKbsqmosCTki4vB 18 | cRgFy3hvhB18lOCW/UnmWAjiOZHIz8dGVy8dy2kCgYATHENuRfN+NA9jGJEEV+cT 19 | CFsvt/r33pm/wBbwYpFaOirOlvv8yE2CEKzODnAmGg7jd62AuSjniW0/75i3nH+B 20 | XF82vulH0f+MqBxanjwU5tIS2zqphxHGOc7YIrtqoXuroYUumtTxQM38GjK8GygK 21 | uWMlHsP+5cC/DuSNzC01oQKBgFKADwlftLk0mMM1wSegjmg1+NRoa3WKcIg9wJ48 22 | Ya9KnfRShLZJ1AiPEkA0l6HpGPxH2d2nL1n3wCJrKWRNOEzFMqiAKPoU8jF+jJwp 23 | qGQ+SjkG/asnkQdUtxLrKn9FWeDJoz5og65hc5v0+KqbuJdKKi/yFSESyL491G90 24 | ndohAoGAJJ4rTl3zBiBMXiIaNw2TpiOo3o9FoVHHoKUE1lhtlYE3QOJMypXpfJ3M 25 | E4qGK5ITBEqcX69WnSIYo8oCVXx8fBYOwGkoqk9pZEttPzkE0mOlh+a3H/tEL/2k 26 | GR57PYc2LLXkPYEnqklgjvPhcPUtHI+AJwJhZEcc4duEEqFTSXs= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/serverpilot.test.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var ServerPilot = require('..'); 3 | 4 | function catchSPException(options) { 5 | return function () { 6 | try { 7 | new ServerPilot(options); 8 | } catch (e) { 9 | return; 10 | } 11 | throw new Error('No error throw by class with options: ' + JSON.stringify(options)); 12 | }; 13 | } 14 | 15 | describe('ServerPilot', function() { 16 | 17 | it('should throw when no options passed', catchSPException()); 18 | it('should throw when empty options passed', catchSPException({})); 19 | it('should throw when options with no clientId or apiKey', catchSPException({clientId: '', apiKey: ''})); 20 | it('should create an instance', function() { 21 | var options = { 22 | clientId: process.env.SP_CLIENT_ID, 23 | apiKey: process.env.SP_API_KEY 24 | }; 25 | var sp = new ServerPilot(options); 26 | sp.options.clientId.should.eql( process.env.SP_CLIENT_ID ); 27 | sp.options.apiKey.should.eql( process.env.SP_API_KEY ); 28 | }); 29 | }); -------------------------------------------------------------------------------- /test/servers.test.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var ServerPilot = require('..'); 3 | var sp; 4 | var serverId, apikey, actionid; 5 | var serverName = 'testserver' + process.version; 6 | var firewall = true; 7 | var autoupdates = true; 8 | 9 | function catchServerException(name) { 10 | return function() { 11 | try { 12 | sp.createServer(name, function(){} ); 13 | } catch (e) { 14 | return; 15 | } 16 | throw new Error('No error throw by class with name: ' + name); 17 | }; 18 | } 19 | 20 | function catchUpdateServerException(options) { 21 | return function() { 22 | try { 23 | sp.updateServer(options, function() {} ); 24 | } catch (e) { 25 | return; 26 | } 27 | throw new Error('No error throw by class with options: ' + JSON.stringify(options)); 28 | }; 29 | } 30 | 31 | function catchDeleteServerException(id) { 32 | return function() { 33 | try { 34 | sp.deleteServer(id, function() {} ); 35 | } catch (e) { 36 | return; 37 | } 38 | throw new Error('No error throw by class with id: ' + id); 39 | }; 40 | } 41 | 42 | describe('Servers', function() { 43 | 44 | before(function() { 45 | sp = new ServerPilot({ 46 | clientId: process.env.SP_CLIENT_ID, 47 | apiKey: process.env.SP_API_KEY 48 | }); 49 | }); 50 | 51 | describe('.getServers()', function() { 52 | it('should get all servers', function(done) { 53 | sp.getServers(function(err, data) { 54 | if ( err ) { return done(err); } 55 | data.should.be.an.Object; 56 | data.data.should.be.an.Object; 57 | done(); 58 | }); 59 | }); 60 | }); 61 | 62 | describe('.createServer(name)', function() { 63 | it('should throw when no options passed', catchServerException()); 64 | it('should throw when empty options', catchServerException('')); 65 | it('should create a server', function(done) { 66 | sp.createServer(serverName, function(err, data) { 67 | if ( err ) { return done(err); } 68 | 69 | data.actionid.should.be.a.String; 70 | data.data.should.be.an.Object; 71 | data.data.name.should.eql(serverName); 72 | 73 | // Set data for later tasks 74 | actionId = data.actionid; 75 | serverId = data.data.id; 76 | apiKey = data.data.apikey; 77 | 78 | done(); 79 | }) 80 | }) 81 | }) 82 | 83 | describe('.getServer(id)', function() { 84 | it('should get a server', function(done) { 85 | sp.getServer( serverId, function(err, data) { 86 | if ( err ) { done(err); } 87 | data.should.be.an.Object; 88 | data.data.should.be.an.Object; 89 | done(); 90 | }); 91 | }); 92 | }); 93 | 94 | describe('.updateServer(options)', function() { 95 | it('should throw when no options are passed', catchUpdateServerException()); 96 | it('should throw when empty options object passed', catchUpdateServerException({})); 97 | it('should throw when options object does not contain: serverId', catchUpdateServerException({ firewall: true })); 98 | it('should throw when option values are not boolean: firewall or autoupdates', catchUpdateServerException({ serverId: serverId, firewall: '', autoupdates: '' })); 99 | it('should update the server', function(done) { 100 | var updateOptions = { 101 | serverId: serverId, 102 | firewall: firewall, 103 | autoupdates: autoupdates 104 | }; 105 | 106 | sp.updateServer(updateOptions, function(err, data) { 107 | if (err) { return done(err); } 108 | 109 | // Match up known things about our server 110 | data.data.id.should.eql(serverId); 111 | data.data.autoupdates.should.eql(autoupdates); 112 | data.data.firewall.should.eql(firewall); 113 | 114 | done(); 115 | }); 116 | }); 117 | }); 118 | 119 | describe('.deleteServer(id)', function() { 120 | it('should throw when no id is passed', catchDeleteServerException()); 121 | it('should delete server', function(done) { 122 | sp.deleteServer(serverId, function(err, data) { 123 | if (err) { return done(err); } 124 | 125 | // Check to see that the server doesn't exist anymore 126 | sp.getServers(function(err, data) { 127 | if (err) { return done(err); } 128 | 129 | var servers = data.data; 130 | servers.should.not.containDeep({id: serverId}); 131 | done(); 132 | }) 133 | }) 134 | }) 135 | }) 136 | 137 | }); -------------------------------------------------------------------------------- /test/sysusers.test.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var ServerPilot = require('..'); 3 | var sp; 4 | var serverId; 5 | var sysUserId; 6 | var name = 'testuser'; 7 | var password = '#1password'; 8 | 9 | function catchCreateSysUser(opts) { 10 | return function() { 11 | try { 12 | sp.addSysUser(opts, function() {} ); 13 | } catch(e) { 14 | return; 15 | } 16 | throw new Error('No error throw by class for options: ' + JSON.stringify(opts)); 17 | }; 18 | } 19 | 20 | function catchGetSysUser(id) { 21 | return function() { 22 | try { 23 | sp.getSysUser(id, function() {} ); 24 | } catch(e) { 25 | return; 26 | } 27 | throw new Error('No error throw by class for id: ' + id); 28 | }; 29 | } 30 | 31 | function catchDeleteSysUser(id) { 32 | return function() { 33 | try { 34 | sp.deleteSysUser(id, function() {} ); 35 | } catch(e) { 36 | return; 37 | } 38 | throw new Error('No error throw by class for id: ' + id); 39 | }; 40 | } 41 | 42 | function catchUpdateSysUser(options) { 43 | return function() { 44 | try { 45 | sp.updateSysUser(options, function() {} ); 46 | } catch(e) { 47 | return; 48 | } 49 | throw new Error('No error throw by class for options: ' + JSON.stringify(options)); 50 | }; 51 | } 52 | 53 | describe('System Users', function() { 54 | 55 | before(function(done) { 56 | sp = new ServerPilot({ 57 | clientId: process.env.SP_CLIENT_ID, 58 | apiKey: process.env.SP_API_KEY 59 | }); 60 | 61 | // Create a dummy server 62 | sp.createServer('testserver' + process.version, function(err, data) { 63 | if (err) { done(err); } 64 | 65 | serverId = data.data.id; 66 | done(); 67 | }) 68 | }); 69 | 70 | describe('.getSysUsers()', function() { 71 | it('should get all sysusers', function(done) { 72 | sp.getSysUsers(function(err, data) { 73 | if ( err ) { return done(err); } 74 | data.should.be.an.Object; 75 | data.data.should.be.an.Object; 76 | done(); 77 | }); 78 | }); 79 | }); 80 | 81 | describe.skip('.createSysUser(options)', function() { 82 | it('should throw when no options passed', catchCreateSysUser()); 83 | it('should throw when empty options passed', catchCreateSysUser({})); 84 | it('should throw when no serverid passed', catchCreateSysUser({ name: name })); 85 | it('should throw when no name passed', catchCreateSysUser({ serverid: serverId })); 86 | // it('should throw when invalid name is passed', catchCreateSysUser({ name: 'Test User', serverid: serverId })); 87 | it('should add sysuser', function(done) { 88 | var opts = { 89 | serverid: serverId, 90 | name: name, 91 | password: password 92 | }; 93 | 94 | sp.createSysUser(opts, function(err, data) { 95 | if (err) { return done(err); } 96 | 97 | data.data.name.should.eql(opts.name); 98 | data.data.serverid.should.eql(opts.serverid); 99 | 100 | sysUserId = data.data.id; 101 | done(); 102 | }); 103 | }); 104 | }); 105 | 106 | describe.skip('.getSysUser(id)', function() { 107 | it('should throw when no id passed', catchGetSysUser()); 108 | it('should get the sysuser', function(done) { 109 | sp.getSysUser(sysUserId, function(err, data) { 110 | if ( err ) { return done(err); } 111 | data.should.be.an.Object; 112 | data.data.should.be.an.Object; 113 | done(); 114 | }); 115 | }); 116 | }); 117 | 118 | describe.skip('.updateSysUser(options)', function() { 119 | it('should throw when no options passed', catchUpdateSysUser()); 120 | it('should throw when no password passed', catchUpdateSysUser({ sysUserId: sysUserId })); 121 | it('should throw when password less than 8 characters', catchUpdateSysUser({ password: 'test456', sysUserId: sysUserId })); 122 | it('should throw when no sysUserId passed', catchUpdateSysUser({ password: 'test45678', sysUserId: sysUserId })); 123 | it('should update sysuser', function(done) { 124 | var updateOpts = { 125 | sysUserId: sysUserId, 126 | password: 'test45678' 127 | }; 128 | 129 | sp.updateSysUser(updateOpts, function(err, data) { 130 | if (err) { return done(err); } 131 | 132 | // Yeah, really no way to verify that this worked. 133 | done(); 134 | }); 135 | }); 136 | }); 137 | 138 | describe.skip('.deleteSysUser(id)', function() { 139 | it('should throw when no id passed', catchDeleteSysUser()); 140 | it('should delete the sysuser', function(done) { 141 | sp.deleteSysUser(sysUserId, function(err, data) { 142 | if (err) { return done(err); } 143 | 144 | // Make sure the user is gone 145 | sp.getSysUser(sysUserId, function(err, data) { 146 | if (err) { 147 | done(); 148 | } 149 | }); 150 | }); 151 | }); 152 | }); 153 | 154 | after(function(done) { 155 | // Kill dummy server 156 | sp.deleteServer(serverId, function(err, data) { 157 | done(); 158 | }); 159 | }); 160 | }); 161 | --------------------------------------------------------------------------------