1 | #!/usr/bin/env node
|
2 |
|
3 |
|
4 |
|
5 | const help=`Usage: skale [options] <command> [<args>]
|
6 |
|
7 | Create, run, deploy clustered node applications
|
8 |
|
9 | Commands:
|
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 |
|
16 | Options:
|
17 | -h, --help Show help
|
18 | -V, --version Show version
|
19 | `;
|
20 |
|
21 | const child_process = require('child_process');
|
22 | const fs = require('fs');
|
23 | const net = require('net');
|
24 |
|
25 | const 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 |
|
30 | var skale_port = 12346;
|
31 |
|
32 | if (argv.h || argv.help) {
|
33 | console.log(help);
|
34 | process.exit();
|
35 | }
|
36 | if (argv.V || argv.version) {
|
37 | var pkg = require('./package');
|
38 | console.log(pkg.name + '-' + pkg.version);
|
39 | process.exit();
|
40 | }
|
41 |
|
42 | const config = load(argv);
|
43 | const proto = config.ssl ? require('https') : require('http');
|
44 |
|
45 | switch (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 |
|
66 | function 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 |
|
86 | var sc = require('skale-engine').context();
|
87 |
|
88 | sc.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.
|
97 | Pleas change directory to ${name}: "cd ${name}"
|
98 | To run your app: "skale run"
|
99 | To modify your app: edit ${name}.js`)
|
100 | }
|
101 |
|
102 | function die(err) {
|
103 | console.error(help);
|
104 | console.error(err);
|
105 | process.exit(1);
|
106 | }
|
107 |
|
108 | function 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 |
|
120 | function 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 |
|
131 | function 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 |
|
143 | function 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 |
|
150 | function 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 |
|
158 | function 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 |
|
169 | function 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 |
|
198 | function 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 | }
|