├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE.md ├── Makefile ├── README.md ├── browser ├── bundle.js └── bundle.map.js ├── examples ├── create_server.js ├── create_server_cb.js ├── simple.coffee └── simple.js ├── lib ├── client.js ├── config.js └── index.js ├── package.json └── test └── all.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 | 3 | node_js: 4 | - "0.11" 5 | - "0.10" 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:onbuild 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | =============== 3 | 4 | Copyright (c) **2014-2015 Manfred Touron ([@moul](https://twitter.com/moul))** 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUNDLE ?= browser/bundle.js 2 | MAP ?= browser/bundle.map.js 3 | NPM ?= npm 4 | BROWSERIFY ?= node_modules/.bin/browserify 5 | EXORCIST ?= node_modules/.bin/exorcist 6 | 7 | BROWSER_ENTRY = lib/index.js 8 | SOURCES = $(wildcard lib/*.js) 9 | 10 | 11 | all: $(BUNDLE) 12 | 13 | 14 | build: 15 | $(NPM) build 16 | 17 | 18 | $(BUNDLE): $(BROWSER_ENTRY) $(SOURCES) Makefile 19 | rm -f $(MAP) $@.tmp 20 | $(BROWSERIFY) \ 21 | --debug \ 22 | -x lodash \ 23 | -x debug \ 24 | -x rc \ 25 | -x request-promise \ 26 | $(BROWSER_ENTRY) | $(EXORCIST) $(MAP) > $@.tmp 27 | @test -s $@.tmp 28 | mv $@.tmp $@ 29 | 30 | 31 | clean: 32 | rm -f $(BUNDLE) $(MAP) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-scaleway 2 | 3 | [![Build Status (Travis)](https://travis-ci.org/moul/node-scaleway.svg?branch=master)](https://travis-ci.org/moul/node-scaleway) 4 | [![Dependency Status](https://david-dm.org/moul/node-scaleway.svg?theme=shields.io)](https://david-dm.org/moul/node-scaleway) 5 | [![Total views](https://sourcegraph.com/api/repos/github.com/moul/node-scaleway/counters/views.svg)](https://sourcegraph.com/github.com/moul/node-scaleway) 6 | [![Views in the last 24 hours](https://sourcegraph.com/api/repos/github.com/moul/node-scaleway/counters/views-24h.svg)](https://sourcegraph.com/github.com/moul/node-scaleway) 7 | [![Code Climate](https://codeclimate.com/github/moul/node-scaleway/badges/gpa.svg)](https://codeclimate.com/github/moul/node-scaleway) 8 | 9 | [![NPM Badge](https://nodei.co/npm/scaleway.png)](https://npmjs.org/package/scaleway) 10 | 11 | [Scaleway](https://www.scaleway.com/) API Node.js client. 12 | It wraps the HTTP api library described [here](https://github.com/node-gitlab/gitlabhq/tree/master/doc/api). 13 | 14 | Maintained by [Manfred Touron](https://github.com/moul) 15 | 16 | --- 17 | 18 | Install 19 | ------- 20 | 21 | # Install from npm 22 | npm install scaleway 23 | 24 | Examples 25 | -------- 26 | 27 | Create a server with Node.js 28 | 29 | ```js 30 | var Api = require('scaleway'), 31 | client = new Api({token: ''}); 32 | 33 | var data = { 34 | name: 'c1', 35 | organization: '', 36 | image: '', 37 | tags: ['test', 'demo'] 38 | }; 39 | 40 | client.post('/servers', data, function(err, res) { 41 | console.log(res.server); 42 | }); 43 | ``` 44 | 45 | Create a server with Coffee-Script 46 | 47 | ```coffee 48 | client = new require('scaleway')() 49 | 50 | var data = 51 | name: 'c1' 52 | organization: '' 53 | image: '' 54 | tags: ['test', 'demo'] 55 | 56 | client.post '/servers', data, (err, res) -> 57 | console.log res.server 58 | ``` 59 | 60 | See [./examples](https://github.com/moul/node-scaleway/tree/master/examples) directory for more examples 61 | 62 | Documentation 63 | ------------- 64 | 65 | Even if this SDK is designed to be developer-friendly and aim for self-service discovery, it is still recommended to read the official [API documentation](https://developer.scaleway.com). 66 | 67 | Alternative SDKs 68 | ---------------- 69 | 70 | - Official Python SDK: [scaleway/python-scaleway](https://github.com/scaleway/python-scaleway) 71 | - Cloudformation plugin, with API client in Node.js: [resin-io/scaleway-cloudformation](https://github.com/resin-io/scaleway-cloudformation) 72 | 73 | 74 | License 75 | ------- 76 | 77 | [MIT](https://github.com/moul/node-scaleway/blob/master/LICENSE.md) 78 | -------------------------------------------------------------------------------- /browser/bundle.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o' 5 | 6 | data = 7 | name: 'c1' 8 | organization: '' 9 | image: '' 10 | tags: ['test', 'demo'] 11 | 12 | client.post '/servers', data, (err, res) -> 13 | console.log res.server 14 | -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | var Api = require('scaleway'), 2 | client = new Api({token: ''}); 3 | 4 | var data = { 5 | name: 'c1', 6 | organization: '', 7 | image: '', 8 | tags: ['test', 'demo'] 9 | }; 10 | 11 | client.post('/servers', data, function(err, res) { 12 | console.log(res.body.server); 13 | }); 14 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | var request = require('superagent-bluebird-promise'), 2 | debug = require('debug')('node-scaleway:lib'), 3 | nodeify = require('nodeify'), 4 | _ = require('lodash'); 5 | 6 | 7 | var RawClient = module.exports = function(options) { 8 | this.config = options || {}; 9 | }; 10 | 11 | 12 | (function() { 13 | var client; 14 | 15 | this.setConfig = function(config) { 16 | this.config = _.defaults(this.config, config); 17 | }; 18 | 19 | this.getConfig = function() { 20 | return this.config; 21 | }; 22 | 23 | this.request = function(path, method, input, options, cb) { 24 | client = this; 25 | 26 | // options parameter is optional 27 | if (typeof options === 'function') { 28 | cb = options; 29 | options = {}; 30 | } 31 | options = options || {}; 32 | 33 | var url = client.config.api_endpoint + path.replace(/^\//, ''); 34 | 35 | // build options 36 | _.defaults(options, { 37 | method: method, 38 | url: url, 39 | headers: {} 40 | }); 41 | // default headers 42 | _.defaults(options.headers, { 43 | 'Accept': 'application/json', 44 | 'User-Agent': 'node-scaleway' // FIXME: append version dynamically 45 | }); 46 | // token-based authentication 47 | if (client.config.token) { 48 | _.defaults(options.headers, { 49 | 'X-Auth-Token': client.config.token 50 | }); 51 | } 52 | var req = request(options.method, options.url); 53 | _.forEach(options.headers, function(val, key) { 54 | req.set(key, val); 55 | }); 56 | 57 | // input 58 | if (input) { 59 | req.send(input); 60 | } 61 | 62 | debug(method + ' ' + url, options); 63 | 64 | // display response from server 65 | if (debug.enabled) { 66 | options.transform = function(data, response) { 67 | debug(response.statusCode, { 68 | headers: response.headers, 69 | body: data 70 | }); 71 | return response; 72 | }; 73 | } 74 | 75 | if (client.config.dry_run) { 76 | console.error('dry-run, should call "' + options.method 77 | + ' ' + options.url + '", exiting...'); 78 | process.exit(1); 79 | } 80 | return req.promise().nodeify(cb); 81 | }; 82 | 83 | // HTTP verbs 84 | this.get = function(path, options, cb) { 85 | return this.request(path, 'GET', null, options, cb); 86 | }; 87 | this.post = function(path, input, options, cb) { 88 | return this.request(path, 'POST', input, options, cb); 89 | }; 90 | this.patch = function(path, input, options, cb) { 91 | return this.request(path, 'PATCH', input, options, cb); 92 | }; 93 | this.put = function(path, input, options, cb) { 94 | return this.request(path, 'PUT', input, options, cb); 95 | }; 96 | this.delete = function(path, options, cb) { 97 | return this.request(path, 'DELETE', null, options, cb); 98 | }; 99 | 100 | // Helpers 101 | /** 102 | * Create a Server. 103 | * You must have a root volume, it can be an image, or a volume. 104 | * 105 | * @param {Object} data 106 | * - {String} name (optional) The name of the new server 107 | * - {String} bootscript (optional) UUID of the bootscript to use 108 | * - {Array} tags (optional) List of tags to add 109 | * - {String} root_volume (optional) UUID of the volume to use for NBD0 110 | * - {String} image (optional) UUID of the image to use for NBD0 111 | * - {Array} volumes (optional) List of UUID to use for NBD1 -> NBD16 112 | * @param {Object} options Request options 113 | * @param {FUnction} fn Callback 114 | */ 115 | this.createServer = function(data, options, fn) { 116 | var postData = { 117 | organization: data.organization || this.config.organization, 118 | name: data.name 119 | }; 120 | if (data.image) { 121 | postData.image = data.image; 122 | } 123 | if (data.bootscript) { 124 | postData.bootscript = data.bootscript; 125 | } 126 | if (data.root_volume) { 127 | postData.volumes = postData.volumes || {}; 128 | postData.volumes['0'] = data.root_volume; 129 | } 130 | if (data.volumes && data.volumes.length) { 131 | postData.volumes = postData.volumes || {}; 132 | data.volumes.forEach(function(volume, idx) { 133 | postData.volumes[(idx + 1).toString()] = volume; 134 | }); 135 | } 136 | if (data.env) { 137 | postData.tags = data.env; 138 | } 139 | 140 | return this.post('/servers', postData, options, fn); 141 | }; 142 | 143 | /** 144 | * Create a Volume. 145 | * 146 | * @param {Object} data 147 | * - {String} name (optional) The name of the new volume 148 | * - {Integer} size The size of the new volume 149 | * - {String} [l_ssd] volume_type The type of the new volume 150 | * @param {Object} options Request options 151 | * @param {FUnction} fn Callback 152 | */ 153 | this.createVolume = function(data, options, fn) { 154 | var postData = { 155 | organization: data.organization || this.config.organization, 156 | name: data.name || data.size, 157 | size: parseInt(data.size), 158 | volume_type: data.volume_type || 'l_ssd' 159 | }; 160 | return this.post('/volumes', postData, options, fn); 161 | }; 162 | 163 | }).call(RawClient.prototype); 164 | -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | var config = require('rc')('node_scaleway', { 2 | api_endpoint: 'https://api.scaleway.com/', 3 | 4 | dry_run: false, 5 | 6 | organization: null, 7 | token: null 8 | }); 9 | if (config['dry-run']) { 10 | config.dry_run = config['dry-run']; 11 | } 12 | 13 | // FIXME: data validation 14 | 15 | module.exports = config; 16 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var RawClient = require('./client'), 2 | _ = require('lodash'); 3 | 4 | 5 | var Client = module.exports = function(options) { 6 | var client = new RawClient(options); 7 | var rc = require('./config'); 8 | client.setConfig(rc); 9 | return client; 10 | }; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scaleway", 3 | "version": "0.8.0", 4 | "description": "Scaleway.com API client", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "mocha --reporter spec --ui tdd --bail --check-leaks ./test/all.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git@github.com:moul/node-scaleway" 12 | }, 13 | "keywords": [ 14 | "Scaleway", 15 | "OnlineLabs", 16 | "API", 17 | "client" 18 | ], 19 | "author": "Manfred Touron ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/moul/node-scaleway/issues" 23 | }, 24 | "homepage": "https://github.com/moul/node-scaleway", 25 | "dependencies": { 26 | "bluebird": "^2.9.34", 27 | "debug": "^2.1.3", 28 | "lodash": "^3.5.0", 29 | "nodeify": "^1.0.0", 30 | "rc": "^1.0.0", 31 | "superagent": "^1.3.0", 32 | "superagent-bluebird-promise": "^2.1.0" 33 | }, 34 | "devDependencies": { 35 | "browserify": "^9.0.3", 36 | "chai": "^2.1.2", 37 | "exorcist": "^0.1.6", 38 | "minimist": "^1.1.1", 39 | "mocha": "^2.1.0", 40 | "uglifyify": "^3.0.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/all.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var chai = require('chai'), 4 | debug = require('debug')('tests'), 5 | Client = require('..'), 6 | util = require('util'); 7 | 8 | 9 | // Initialize chai.should() 10 | chai.should(); 11 | 12 | 13 | var inspect = function(name, obj) { 14 | debug(name, util.inspect(obj, {showHidden: false, depth: null})); 15 | }; 16 | 17 | 18 | suite('#config', function() { 19 | test('should have a minimal configuration', function() { 20 | var client = new Client(); 21 | inspect('client', client); 22 | client.config.should.have.property('api_endpoint'); 23 | client.config.should.have.property('organization'); 24 | client.config.should.have.property('token'); 25 | }); 26 | test('should have an url for api_endpoint', function() { 27 | var client = new Client(); 28 | client.config.api_endpoint.should.contains('http'); 29 | client.config.api_endpoint.should.contains('://'); 30 | }); 31 | }); 32 | 33 | 34 | suite("[client]", function() { 35 | var client; 36 | 37 | setup(function() { 38 | var options = { 39 | token: null 40 | }; 41 | client = new Client(options); 42 | }); 43 | 44 | teardown(function() { 45 | client = null; 46 | }); 47 | 48 | suite("#get", function() { 49 | test("should successfully execute GET /", function(done) { 50 | this.timeout(5000); 51 | client.get("/").then(function(res) { 52 | inspect('res', res); 53 | try { 54 | (res.statusCode).should.equal(200); 55 | (res.headers['content-type']).should.equal('application/json'); 56 | (res.body.api).should.equal('api-compute'); 57 | done(); 58 | } catch (e) { 59 | done(e); 60 | } 61 | }).catch(function(err) { 62 | inspect('err', err); 63 | done(err); 64 | }); 65 | }); 66 | 67 | test("should raise an authentication error", function(done) { 68 | client.get("/servers").then(function(res) { 69 | inspect('res', res); 70 | done(res); 71 | }).catch(function(err) { 72 | inspect('err', err); 73 | try { 74 | (err.res.statusCode).should.equal(401); 75 | (err.res.headers['content-type']).should.equal('application/json'); 76 | (err.body.type).should.equal('invalid_auth'); 77 | done(); 78 | } catch (e) { 79 | done(e); 80 | } 81 | }); 82 | }); 83 | }); 84 | }); 85 | --------------------------------------------------------------------------------