UNPKG

5.73 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3// Copyright 2016 Luca-SAS, licensed under the Apache License 2.0
4
5const help=`Usage: skale [options] <command> [<args>]
6
7Create, run, deploy clustered node applications
8
9Commands:
10 create <app> Create a new application
11 run [<args>...] Run application
12 deploy [<args>...] Deploy application
13 status print status of local skale cluster
14 stop Stop local skale cluster
15
16Options:
17 -h, --help Show help
18 -V, --version Show version
19`;
20
21const child_process = require('child_process');
22const fs = require('fs');
23const net = require('net');
24
25const argv = require('minimist')(process.argv.slice(2), {
26 string: ['c', 'config', 'H', 'host', 'k', 'key', 'p', 'port'],
27 boolean: ['h', 'help', 'r', 'remote', 'V', 'version', 's', 'ssl'],
28});
29
30var skale_port = 12346;
31
32if (argv.h || argv.help) {
33 console.log(help);
34 process.exit();
35}
36if (argv.V || argv.version) {
37 var pkg = require('./package');
38 console.log(pkg.name + '-' + pkg.version);
39 process.exit();
40}
41
42const config = load(argv);
43const proto = config.ssl ? require('https') : require('http');
44
45switch (argv._[0]) {
46 case 'create':
47 create(argv._[1]);
48 break;
49 case 'deploy':
50 deploy(argv._.splice(1));
51 break;
52 case 'run':
53 if (argv.r || argv.remote) run_remote(argv._.splice(1));
54 else run_local(argv._.splice(1));
55 break;
56 case 'status':
57 status_local();
58 break;
59 case 'stop':
60 stop_local_server();
61 break;
62 default:
63 die('Error: invalid command: ' + argv._[0]);
64}
65
66function create(name) {
67 console.log('create application ' + name);
68 try {
69 fs.mkdirSync(name);
70 } catch (error) {
71 die('skale create error: ' + error.message);
72 }
73 process.chdir(name);
74 const pkg = {
75 name: name,
76 version: '0.0.0',
77 private: true,
78 keywords: [ 'skale' ],
79 dependencies: {
80 'skale-engine': '^0.4.0'
81 }
82 };
83 fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
84 var src = `#!/usr/bin/env node
85
86var sc = require('skale-engine').context();
87
88sc.parallelize(['Hello world'])
89 .collect()
90 .on('data', console.log)
91 .on('end', sc.end);
92`;
93 fs.writeFileSync(name + '.js', src);
94 const npm = child_process.spawnSync('npm', ['install'], {stdio: 'inherit'});
95 if (npm.status) die('skale create error: npm install failed');
96 console.log(`Project ${name} is now ready.
97Pleas change directory to ${name}: "cd ${name}"
98To run your app: "skale run"
99To modify your app: edit ${name}.js`)
100}
101
102function die(err) {
103 console.error(help);
104 console.error(err);
105 process.exit(1);
106}
107
108function load(argv) {
109 var conf = {}, save = false;
110 var path = argv.c || argv.config || process.env.SKALE_CONFIG || process.env.HOME + '/.skalerc';
111 try { conf = JSON.parse(fs.readFileSync(path)); } catch (error) { save = true; }
112 conf.host = argv.H || argv.host || process.env.SKALE_HOST || conf.host;
113 conf.port = argv.p || argv.port || process.env.SKALE_PORT || conf.port;
114 conf.key = argv.k || argv.key || conf.key;
115 conf.ssl = argv.s || argv.ssl || (conf.ssl ? true : false);
116 if (save || argv._[0] == 'init') fs.writeFileSync(path, JSON.stringify(conf, null, 2));
117 return conf;
118}
119
120function start_skale(done) {
121 const out = fs.openSync('skale-server.log', 'a');
122 const err = fs.openSync('skale-server.log', 'a');
123 const child = child_process.spawn('node_modules/skale-engine/bin/server.js', ['-l', '2'], {
124 detached: true,
125 stdio: ['ignore', out, err]
126 });
127 child.unref();
128 try_connect(5, 1000, done);
129}
130
131function try_connect(nb_try, timeout, done) {
132 const sock = net.connect(skale_port);
133 sock.on('connect', function () {
134 sock.end();
135 done(null);
136 });
137 sock.on('error', function (err) {
138 if (--nb_try <= 0) return done('skale-server not ok');
139 setTimeout(function () { try_connect(nb_try, timeout, done); }, timeout);
140 });
141}
142
143function stop_local_server() {
144 const child = child_process.execFile('/usr/bin/pgrep', ['skale-server ' + skale_port], function (err, pid) {
145 if (! pid) return;
146 process.kill(pid.trim());
147 });
148}
149
150function status_local() {
151 const child = child_process.execFile('/bin/ps', ['ux'], function (err, out) {
152 const lines = out.split(/\r\n|\r|\n/);
153 for (var i = 0; i < lines.length; i++)
154 if (i == 0 || lines[i].match(/ skale-/)) console.log(lines[i]);
155 });
156}
157
158function run_local(args) {
159 const pkg = JSON.parse(fs.readFileSync('package.json'));
160 const cmd = pkg.name + '.js';
161 args.splice(0, 0, cmd);
162 try_connect(0, 0, function (err) {
163 if (!err) return run_app();
164 start_skale(run_app);
165 });
166 function run_app() { child = child_process.spawn('node', args, {stdio: 'inherit'}); }
167}
168
169function deploy(args) {
170 const pkg = JSON.parse(fs.readFileSync('package.json'));
171 fs.readFile(pkg.name + '.js', {encoding: 'utf8'}, function (err, data) {
172 if (err) throw err;
173
174 const postdata = JSON.stringify({pkg: pkg, src: data, args: args});
175
176 const options = {
177 hostname: config.host,
178 port: config.port,
179 path: '/deploy',
180 method: 'POST',
181 headers: {
182 'X-Auth': config.key,
183 'Content-Type': 'application/json',
184 'Content-Length': Buffer.byteLength(postdata)
185 }
186 };
187
188 const req = proto.request(options, function (res) {
189 res.setEncoding('utf8');
190 res.pipe(process.stdout);
191 });
192
193 req.on('error', function (err) {throw err;});
194 req.end(postdata);
195 });
196}
197
198function run_remote(args) {
199 const name = process.cwd().split('/').pop();
200 const postdata = JSON.stringify({name: name, args: args});
201
202 const options = {
203 hostname: config.host,
204 port: config.port,
205 path: '/run',
206 method: 'POST',
207 headers: {
208 'X-Auth': config.key,
209 'Content-Type': 'application/json',
210 'Content-Length': Buffer.byteLength(postdata)
211 }
212 };
213
214 const req = proto.request(options, function (res) {
215 res.setEncoding('utf8');
216 res.pipe(process.stdout);
217 });
218
219 req.on('error', function (err) {throw err;});
220 req.end(postdata);
221}