UNPKG

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