├── .gitignore ├── LICENSE ├── README.md ├── bin └── scw ├── index.js ├── interactive-cli.js ├── package.json └── pres.png /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | *.tar.gz 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Alexandre Strzelewicz, Inc. 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # scaleway-commander 3 | 4 | ![Presentation commands](https://raw.githubusercontent.com/Unitech/scaleway-commander/master/pres.png) 5 | 6 | ## Install 7 | 8 | ``` 9 | $ npm install scaleway-commander -g 10 | ``` 11 | 12 | ## Configure 13 | 14 | Put access token into **~/.scaleway** (https://cloud.scaleway.com/#/credentials) 15 | 16 | ## Use 17 | 18 | ``` 19 | $ scw -h 20 | ``` 21 | 22 | ## Commands 23 | 24 | ``` 25 | # List host 26 | $ scw ls 27 | 28 | # Constantly refresh server list and clear screen 29 | $ scw ls --watch 30 | 31 | # Interactively stop host 32 | $ scw stop 33 | 34 | # Interactively start host 35 | $ scw start 36 | 37 | # Interactively terminate host 38 | $ scw start 39 | 40 | # Interactively ssh to host 41 | $ scw ssh 42 | 43 | # SSH to all machine and execute command 44 | $ scw sshall 45 | ``` 46 | -------------------------------------------------------------------------------- /bin/scw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // https://developer.scaleway.com/#snapshots-snapshots 4 | 5 | var program = require('commander'); 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | var package = require('../package.json'); 9 | var Scaleway = require('../index.js'); 10 | var sshexec = require('ssh-exec'); 11 | var Icli = require('../interactive-cli.js'); 12 | var chalk = require('chalk'); 13 | var cliSpinners = require('cli-spinners'); 14 | var async = require('async'); 15 | var multissh = require('multissh'); 16 | 17 | var conf_file = path.join(process.env.HOME, '.scaleway'); 18 | var token = ''; 19 | 20 | try { 21 | token = fs.readFileSync(conf_file).toString().trim(); 22 | } catch(e) { 23 | console.error('Please put access token (from https://cloud.scaleway.com/#/credentials) in file in %s', conf_file); 24 | process.exit(1); 25 | } 26 | 27 | var scaleway = new Scaleway(token); 28 | var icli = new Icli(scaleway); 29 | 30 | // CLI opts 31 | program 32 | .version(package.version); 33 | 34 | program 35 | .description('list-servers') 36 | .option('--watch') 37 | .command('server-list') 38 | .alias('ls') 39 | .action(function() { 40 | if (program.watch === true) 41 | icli.watchListServers(); 42 | else 43 | listServers(); 44 | }); 45 | 46 | program 47 | .command('stop [hostname]') 48 | .description('stop hostname') 49 | .action(function(hostname) { 50 | 51 | function act(hostname) { 52 | scaleway.actionServer(hostname, 'poweroff', function(err, data) { 53 | if (err) return exitError(err); 54 | return success(); 55 | }); 56 | } 57 | 58 | if (!hostname) { 59 | icli.chooseHostname(act); 60 | return false; 61 | } 62 | return act(hostname); 63 | }); 64 | 65 | program 66 | .command('start [hostname]') 67 | .description('start [hostname]') 68 | .action(function(hostname) { 69 | function act(hostname) { 70 | scaleway.actionServer(hostname, 'poweron', function(err, data) { 71 | if (err) return exitError(err); 72 | return success(); 73 | }); 74 | } 75 | 76 | if (!hostname) { 77 | icli.chooseHostname(act); 78 | return false; 79 | } 80 | return act(hostname); 81 | }); 82 | 83 | program 84 | .command('ssh') 85 | .description('ssh to hostname') 86 | .action(function(hostname) { 87 | function act(hostname) { 88 | const spawn = require('child_process').spawn; 89 | 90 | scaleway.getServerMetaFromHostname(hostname, function(err, meta) { 91 | var con = 'root@' + meta.public_ip.address; 92 | console.log('Executing command: ssh %s', con); 93 | spawn('ssh', [con], { 94 | stdio : 'inherit' 95 | }); 96 | }); 97 | } 98 | 99 | icli.chooseHostname(act); 100 | }); 101 | 102 | program 103 | .command('multissh [parallel]') 104 | .description('ssh to each online host and execute ') 105 | .action(function(cmd, parallel) { 106 | 107 | var server_list = []; 108 | 109 | scaleway.server_list.forEach(function(server) { 110 | if (server.state != 'running') return; 111 | server_list.push({ 112 | ip : server.public_ip.address, 113 | hostname : server.hostname, 114 | user : 'root' 115 | }); 116 | }); 117 | 118 | multissh.start(cmd, server_list); 119 | }); 120 | 121 | program 122 | .command('terminate ') 123 | .description('terminate ') 124 | .action(function(hostname) { 125 | function act(hostname) { 126 | scaleway.actionServer(hostname, 'terminate', function(err, data) { 127 | if (err) return exitError(err); 128 | return success(); 129 | }); 130 | } 131 | 132 | if (!hostname) { 133 | icli.chooseHostname(act); 134 | return false; 135 | } 136 | return act(hostname); 137 | }); 138 | 139 | program 140 | .command('snapshot-list') 141 | .alias('snap') 142 | .action(function() { 143 | scaleway.listSnapshots(function(err, data) { 144 | listSnap(data); 145 | }); 146 | }); 147 | 148 | program 149 | .command('snapshot-list') 150 | .alias('snap') 151 | .action(function() { 152 | scaleway.listSnapshots(function(err, data) { 153 | listSnap(data); 154 | }); 155 | }); 156 | 157 | program 158 | .command('*') 159 | .action(function(env){ 160 | console.log('Enter a Valid command'); 161 | program.outputHelp(); 162 | process.exit(0); 163 | }); 164 | 165 | if (!process.argv.slice(2).length) { 166 | program.outputHelp(); 167 | } 168 | 169 | var ora = require('ora'); 170 | var spinner = ora({ 171 | text : chalk.bold('Loading server list'), 172 | spinner: 'arrow3' 173 | }); 174 | 175 | // connecting= earth 176 | //arrow3 177 | 178 | spinner.start(); 179 | scaleway.init(function() { 180 | spinner.stop(); 181 | program.parse(process.argv); 182 | }); 183 | 184 | /** 185 | * CLI commands 186 | */ 187 | function exitError(err) { 188 | console.error(err); 189 | process.exit(1); 190 | } 191 | 192 | function formatResponse(data) { 193 | console.log(data); 194 | } 195 | 196 | function success() { 197 | console.log(chalk.bold.green('Action successfully executed')); 198 | process.exit(0); 199 | } 200 | 201 | function listSnap(snapshots) { 202 | console.log(snapshots); 203 | } 204 | 205 | function listServers(servers) { 206 | icli.listServers(); 207 | process.exit(0); 208 | } 209 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | // curl -H 'X-Auth-Token: 8fad00b4-ab78-4363-b5ab-f995cf97ee4a' -H 'Content-Type: application/json' https://api.scaleway.com/server 3 | 4 | var request = require('request'); 5 | 6 | var Scaleway = function(token) { 7 | var that = this; 8 | 9 | this.server_list = []; 10 | 11 | this.request = request.defaults({ 12 | headers : { 13 | 'X-Auth-Token' : token 14 | } 15 | }); 16 | 17 | this.base_url = 'https://api.scaleway.com'; 18 | 19 | this.doGet = function(path, cb) { 20 | var url = this.base_url + path; 21 | 22 | this.request.get(url, function(err, res, body) { 23 | if (err) return cb(err); 24 | return cb(null, JSON.parse(body)); 25 | }); 26 | }; 27 | 28 | this.doPost = function(path, data, cb) { 29 | var url = this.base_url + path; 30 | 31 | this.request.post({ 32 | url : url, 33 | json : data 34 | }, function(err, res, body) { 35 | if (err) return cb(err); 36 | return cb(null, body); 37 | }); 38 | }; 39 | }; 40 | 41 | Scaleway.prototype.init= function(fn) { 42 | var that = this; 43 | this.doGet('/servers', function(err, data) { 44 | if (err) throw new Error(err); 45 | that.server_list = data.servers; 46 | fn(); 47 | }); 48 | }; 49 | 50 | Scaleway.prototype.listServers = function(cb) { 51 | return cb(null, this.server_list); 52 | }; 53 | 54 | Scaleway.prototype.listActions = function(hostname, cb) { 55 | var that = this; 56 | 57 | this.getServerMetaFromHostname(hostname, function(err, server) { 58 | that.doGet('/servers/' + server.id + '/action', cb); 59 | }); 60 | }; 61 | 62 | Scaleway.prototype.actionServer = function(hostname, action, cb) { 63 | var that = this; 64 | 65 | this.getServerMetaFromHostname(hostname, function(err, server) { 66 | that.doPost('/servers/' + server.id + '/action', { 67 | action : action 68 | }, cb); 69 | }); 70 | }; 71 | 72 | Scaleway.prototype.listSnapshots = function(cb) { 73 | this.doGet('/snapshots', cb); 74 | }; 75 | 76 | Scaleway.prototype.getServerMetaFromHostname = function(hostname, cb) { 77 | var t_server = null; 78 | this.server_list.forEach(function(server) { 79 | if (server.hostname == hostname) 80 | t_server = server; 81 | }); 82 | return cb(null, t_server); 83 | }; 84 | 85 | module.exports = Scaleway; 86 | -------------------------------------------------------------------------------- /interactive-cli.js: -------------------------------------------------------------------------------- 1 | 2 | var inquirer = require('inquirer'); 3 | var Table = require('cli-table2'); 4 | var chalk = require('chalk'); 5 | 6 | var ICLI = function(scaleway) { 7 | this.scaleway = scaleway; 8 | }; 9 | 10 | module.exports = ICLI; 11 | 12 | function colorStatus(state) { 13 | if (state == 'running') 14 | return chalk.bold.green(state); 15 | if (state == 'stopped') 16 | return chalk.bold.red(state); 17 | if (state == 'starting') 18 | return chalk.bold.blue(state); 19 | if (state == 'stopping') 20 | return chalk.bold.yellow(state); 21 | return state; 22 | } 23 | 24 | ICLI.prototype.chooseHostname = function(cb) { 25 | var hostnames = []; 26 | 27 | this.scaleway.server_list.forEach(function(server) { 28 | hostnames.push(server.hostname + ' '); 29 | }); 30 | 31 | inquirer.prompt({ 32 | type: 'list', 33 | name: 'server', 34 | message: 'Which server?', 35 | choices: hostnames, 36 | filter : function(server) { 37 | // Return server hostname only 38 | return server.split(' ')[0]; 39 | } 40 | }).then(function (answers) { 41 | cb(answers.server); 42 | }); 43 | }; 44 | 45 | ICLI.prototype.watchListServers = function() { 46 | var clear = require('clear'); 47 | var that = this; 48 | 49 | function display() { 50 | clear(); 51 | that.listServers(); 52 | var time = new Date(); 53 | console.log('Refresh time %s', time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds()); 54 | } 55 | 56 | (function rec() { 57 | that.scaleway.init(function() { 58 | display(); 59 | setTimeout(rec, 500); 60 | }); 61 | })(); 62 | 63 | display(); 64 | }; 65 | 66 | ICLI.prototype.listServers = function() { 67 | // instantiate 68 | var table = new Table({ 69 | head: ['hostname', 'state', 'public ip', 'private ip', 'image', 'ssh'], 70 | style : { 71 | head : ['cyan', 'bold'], 72 | compact : true 73 | } 74 | }); 75 | 76 | this.scaleway.server_list.forEach(function(server) { 77 | table.push([ 78 | server.hostname, 79 | colorStatus(server.state), 80 | server.public_ip.address, 81 | server.private_ip, 82 | server.image.name, 83 | 'ssh root@' + server.public_ip.address 84 | ]); 85 | }); 86 | 87 | console.log(table.toString()); 88 | }; 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scaleway-commander", 3 | "version": "0.4.2", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "scw": "./bin/scw" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "dependencies": { 13 | "request": "*", 14 | "commander": "*", 15 | "cli-table2": "*", 16 | "inquirer": "*", 17 | "chalk": "*", 18 | "ssh-exec": "*", 19 | "ora": "*", 20 | "clear": "*", 21 | "async": "*", 22 | "cli-spinners": "*", 23 | "moment" : "*", 24 | "multissh" : "^1" 25 | }, 26 | "author": "", 27 | "license": "ISC" 28 | } 29 | -------------------------------------------------------------------------------- /pres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unitech/scaleway-commander/2aa6310ef3521d7fa30386f9ce473df3cbf126ff/pres.png --------------------------------------------------------------------------------