1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | if (!process.env.PRECOMPILE_STREAMLINE_FILES) {
|
21 | require('streamline').register({ cache: true });
|
22 | }
|
23 |
|
24 | var fs = require('fs');
|
25 | var path = require('path');
|
26 | var util = require('util');
|
27 |
|
28 | var _ = require('underscore');
|
29 | var callerId = require('caller-id');
|
30 |
|
31 | var CmdLoader = require('./cmdLoader');
|
32 | var ExtendedCommand = require('./util/extendedcommand');
|
33 | var log = require('./util/logging');
|
34 |
|
35 | var utilsCore = require('./util/utilsCore');
|
36 | var Interactor = require('./util/interaction');
|
37 |
|
38 |
|
39 | function AzureCli(name, parent, genMode) {
|
40 | this.parent = parent;
|
41 | this.output = log;
|
42 | this.interaction = new Interactor(this);
|
43 |
|
44 | AzureCli['super_'].call(this, name);
|
45 |
|
46 | if (parent) {
|
47 | this._mode = parent._mode;
|
48 | }
|
49 | else {
|
50 | this.initSetup();
|
51 |
|
52 | this.enableNestedCommands(this);
|
53 |
|
54 |
|
55 |
|
56 | this.checkVersion();
|
57 |
|
58 | this._mode = genMode;
|
59 | if (!this._mode) {
|
60 | this._mode = utilsCore.getMode();
|
61 | }
|
62 | var loader = new CmdLoader(this, this._mode);
|
63 | if (genMode) {
|
64 | log.info('Generating command metadata file: ' + loader.cmdMetadataFile);
|
65 | loader.harvestPlugins();
|
66 | loader.harvestModules();
|
67 | loader.saveCmdMetadata();
|
68 | log.info('Done');
|
69 | return;
|
70 | } else if (loader.cmdMetadataExists()) {
|
71 | loader.initFromCmdMetadata(AzureCli);
|
72 | } else {
|
73 | log.warn('No existing command metadata files. Command will run slow.');
|
74 | loader.harvestPlugins();
|
75 | loader.harvestModules();
|
76 | }
|
77 | }
|
78 | }
|
79 |
|
80 | util.inherits(AzureCli, ExtendedCommand);
|
81 |
|
82 | _.extend(AzureCli.prototype, {
|
83 | initSetup: function () {
|
84 | var self = this;
|
85 |
|
86 | self.debug = process.env.AZURE_DEBUG === '1';
|
87 |
|
88 |
|
89 | if (!self.debug && process.listeners('uncaughtException').length === 0) {
|
90 | self.uncaughExceptionHandler = function (err) {
|
91 | self.interaction.clearProgress();
|
92 |
|
93 |
|
94 | var noConsole = false;
|
95 | if (!log['default'].transports.console) {
|
96 | noConsole = true;
|
97 | self.output.add(self.output.transports.Console);
|
98 | }
|
99 |
|
100 | var loggedFullError = false;
|
101 | if (err.message) {
|
102 | log.error(err.message);
|
103 | } else if (err.Message) {
|
104 | log.error(err.Message);
|
105 | } else {
|
106 | log.json('error', err);
|
107 | loggedFullError = true;
|
108 | }
|
109 |
|
110 | if (!loggedFullError) {
|
111 | if (err.stack) {
|
112 | log.verbose('stack', err.stack);
|
113 | }
|
114 |
|
115 | log.json('silly', err);
|
116 | }
|
117 |
|
118 | self.recordError(err);
|
119 |
|
120 | if (noConsole) {
|
121 | self.output.remove(self.output.transports.Console);
|
122 | }
|
123 |
|
124 | self.exit('error', null, 1);
|
125 | };
|
126 |
|
127 | process.addListener('uncaughtException', self.uncaughExceptionHandler);
|
128 | }
|
129 | },
|
130 |
|
131 | getErrorFile: function () {
|
132 | return path.join(utilsCore.azureDir(), 'azure.err');
|
133 | },
|
134 |
|
135 | getSillyErrorFile: function () {
|
136 | return path.join(utilsCore.azureDir(), 'azure.details.err');
|
137 | },
|
138 |
|
139 | recordError: function (err) {
|
140 | if (err) {
|
141 | var errorFile = this.getErrorFile();
|
142 | try {
|
143 | var writeFileFunction = process.env.AZURE_CLI_APPEND_LOGS ? fs.appendFileSync : fs.writeFileSync;
|
144 | writeFileFunction(errorFile, (new Date().toISOString()) + ':\n' +
|
145 | util.inspect(err) + '\n' + err.stack + '\n');
|
146 | (log.format().json ? log.error : log.info)('Error information has been recorded to ' + errorFile);
|
147 | } catch (err2) {
|
148 | log.warn('Cannot save error information :' + util.inspect(err2));
|
149 | }
|
150 |
|
151 | log.writeCapturedSillyLogs(this.getSillyErrorFile(), process.env.AZURE_CLI_APPEND_LOGS);
|
152 | }
|
153 | },
|
154 |
|
155 | exit: function (level, message, exitCode) {
|
156 | var self = this;
|
157 |
|
158 | self.interaction.clearProgress();
|
159 | if (message) {
|
160 | log.log(level, message);
|
161 | }
|
162 |
|
163 | if (self.uncaughtExceptionHandler) {
|
164 | process.removeListener('uncaughtException', self.uncaughExceptionHandler);
|
165 | }
|
166 |
|
167 | if (log.shouldWaitForStdoutDrained) {
|
168 | process.on('exit', function () { process.exit(exitCode); });
|
169 | }
|
170 | else {
|
171 | process.exit(exitCode);
|
172 | }
|
173 | },
|
174 |
|
175 | normalizeAuthorizationError: function (msg) {
|
176 | var regex = /.*The \'Authorization\' header is not present or provided in an invalid format.*/ig;
|
177 | if (msg.match(regex)) {
|
178 | msg = 'Certificate based Authentication is not supported in current mode: \'' + this._mode +
|
179 | '\'. Please authenticate using an organizational account via \'azure login\' command.';
|
180 | }
|
181 | return msg;
|
182 | },
|
183 |
|
184 | execute: function (fn) {
|
185 | var self = this;
|
186 |
|
187 | return self.action(function () {
|
188 | self.setupCommandOutput();
|
189 |
|
190 | if (log.format().json) {
|
191 | log.verbose('Executing command ' + self.fullName().bold);
|
192 | } else {
|
193 | log.info('Executing command ' + self.fullName().bold);
|
194 | }
|
195 |
|
196 | try {
|
197 |
|
198 | var argsCount = fn.length <= 1 ? self.args.length + 2 : fn.length;
|
199 | var args = new Array(argsCount);
|
200 |
|
201 | var optionIndex = arguments.length - 1;
|
202 | for (var i = 0; i < arguments.length; i++) {
|
203 | if (typeof arguments[i] === 'object') {
|
204 | optionIndex = i;
|
205 | break;
|
206 | }
|
207 | }
|
208 |
|
209 |
|
210 | var options = arguments[optionIndex].optionValues;
|
211 |
|
212 | args[args.length - 2] = options;
|
213 | args[args.length - 1] = callback;
|
214 |
|
215 |
|
216 | var freeArguments = 0;
|
217 | for (var j = 0; j < self.args.length; j++) {
|
218 | var optionName = utilsCore.camelcase(self.args[j].name);
|
219 | if (options[optionName]) {
|
220 | args[j] = options[optionName];
|
221 | delete options[optionName];
|
222 | } else if (freeArguments < arguments.length) {
|
223 | args[j] = arguments[freeArguments];
|
224 | freeArguments++;
|
225 | }
|
226 | }
|
227 |
|
228 | fn.apply(this, args);
|
229 | } catch (err) {
|
230 | callback(err);
|
231 | }
|
232 |
|
233 | function callback(err) {
|
234 | if (err) {
|
235 |
|
236 | var noConsole = false;
|
237 | if (!process.env.AZURE_NO_ERROR_ON_CONSOLE && !log['default'].transports.console) {
|
238 | noConsole = true;
|
239 | self.output.add(self.output.transports.Console);
|
240 | }
|
241 |
|
242 | if (err.message) {
|
243 | log.error(err.message);
|
244 | log.json('silly', err);
|
245 | } else if (err.Message) {
|
246 | if (typeof err.Message === 'object' && typeof err.Message['#'] === 'string') {
|
247 | var innerError;
|
248 | try {
|
249 | innerError = JSON.parse(err.Message['#']);
|
250 | } catch (e) {
|
251 |
|
252 | }
|
253 |
|
254 | if (innerError) {
|
255 | if (noConsole) {
|
256 | self.output.remove(self.output.transports.Console);
|
257 | }
|
258 |
|
259 | return callback(innerError);
|
260 | }
|
261 | }
|
262 |
|
263 | err.message = self.normalizeAuthorizationError(err.message);
|
264 | log.error(err.Message);
|
265 | log.json('verbose', err);
|
266 | } else {
|
267 | log.error(err);
|
268 | }
|
269 |
|
270 | self.recordError(err);
|
271 | if (err.stack) {
|
272 | (self.debug ? log.error : log.verbose)(err.stack);
|
273 | }
|
274 |
|
275 | if (noConsole) {
|
276 | self.output.remove(self.output.transports.Console);
|
277 | }
|
278 |
|
279 | self.exit('error', self.fullName().bold + ' command ' + 'failed\n'.red.bold, 1);
|
280 | } else {
|
281 | if (log.format().json) {
|
282 | self.exit('verbose', self.fullName().bold + ' command ' + 'OK'.green.bold, 0);
|
283 | }
|
284 | else {
|
285 | self.exit('info', self.fullName().bold + ' command ' + 'OK'.green.bold, 0);
|
286 | }
|
287 | }
|
288 | }
|
289 | });
|
290 | },
|
291 |
|
292 | |
293 |
|
294 |
|
295 | parseOptions: function (argv) {
|
296 | var args = [];
|
297 | var len = argv.length;
|
298 | var literal = false;
|
299 | var option;
|
300 | var arg;
|
301 |
|
302 | var unknownOptions = [];
|
303 |
|
304 |
|
305 | for (var i = 0; i < len; ++i) {
|
306 | arg = argv[i];
|
307 |
|
308 |
|
309 | if ('--' == arg) {
|
310 | literal = true;
|
311 | continue;
|
312 | }
|
313 |
|
314 | if (literal) {
|
315 | args.push(arg);
|
316 | continue;
|
317 | }
|
318 |
|
319 |
|
320 | option = this.optionFor(arg);
|
321 |
|
322 |
|
323 | var commandOption = null;
|
324 |
|
325 | if (!option && arg[0] === '-') {
|
326 | var command = this;
|
327 | var arga = null;
|
328 | for (var a = 0; a < args.length && command && !commandOption; ++a) {
|
329 | arga = args[a];
|
330 | if (command.categories && (arga in command.categories)) {
|
331 | command = command.categories[arga];
|
332 | commandOption = command.optionFor(arg);
|
333 | continue;
|
334 | }
|
335 | break;
|
336 | }
|
337 | if (!commandOption && arga && command && command.commands) {
|
338 | for (var j in command.commands) {
|
339 | if (command.commands[j].name === arga) {
|
340 | commandOption = command.commands[j].optionFor(arg);
|
341 | break;
|
342 | }
|
343 | }
|
344 | }
|
345 | }
|
346 |
|
347 |
|
348 |
|
349 | if (option) {
|
350 |
|
351 | if (option.required) {
|
352 | arg = argv[++i];
|
353 | if (!arg) {
|
354 | return this.optionMissingArgument(option);
|
355 | }
|
356 |
|
357 | if ('-' === arg[0]) {
|
358 | return this.optionMissingArgument(option, arg);
|
359 | }
|
360 |
|
361 | this.emit(option.name(), arg);
|
362 | } else if (option.optional) {
|
363 |
|
364 | arg = argv[i + 1];
|
365 | if (!arg || '-' === arg[0]) {
|
366 | arg = null;
|
367 | } else {
|
368 | ++i;
|
369 | }
|
370 |
|
371 | this.emit(option.name(), arg);
|
372 |
|
373 | } else {
|
374 | this.emit(option.name());
|
375 | }
|
376 | continue;
|
377 | }
|
378 |
|
379 |
|
380 | if (arg.length > 1 && '-' == arg[0]) {
|
381 | unknownOptions.push(arg);
|
382 |
|
383 |
|
384 |
|
385 |
|
386 |
|
387 | commandOption = commandOption || { optional : 1 };
|
388 | if (commandOption.required || (commandOption.optional && argv[i + 1] && '-' != argv[i + 1][0])) {
|
389 | unknownOptions.push(argv[++i]);
|
390 | }
|
391 | continue;
|
392 | }
|
393 |
|
394 |
|
395 | args.push(arg);
|
396 | }
|
397 |
|
398 | return { args: args, unknown: unknownOptions };
|
399 | },
|
400 |
|
401 | setupCommandLogFormat: function (topMost) {
|
402 | if (topMost) {
|
403 | var opts = {
|
404 | json: false,
|
405 | level: 'info',
|
406 | logo: 'on'
|
407 | };
|
408 |
|
409 | log.format(opts);
|
410 | }
|
411 | },
|
412 |
|
413 | setupCommandOutput: function (raw) {
|
414 | var self = this;
|
415 | var verbose = 0;
|
416 | var json = 0;
|
417 |
|
418 | if (!raw) {
|
419 | raw = self.normalize(self.parent.rawArgs.slice(2));
|
420 | }
|
421 |
|
422 | function hasOption(optionName) {
|
423 | return self.options.some(function (o) { return o.long === optionName; });
|
424 | }
|
425 |
|
426 | for (var i = 0, len = raw.length; i < len; ++i) {
|
427 | if (hasOption('--json') &&
|
428 | raw[i] === '--json') {
|
429 | ++json;
|
430 | } else if (hasOption('--verbose') &&
|
431 | (raw[i] === '-v' || raw[i] === '--verbose')) {
|
432 | ++verbose;
|
433 | }
|
434 | }
|
435 |
|
436 | var opts = {};
|
437 | if (verbose || json) {
|
438 | if (json) {
|
439 | opts.json = true;
|
440 | opts.level = 'data';
|
441 | }
|
442 |
|
443 | if (verbose == 1) {
|
444 | opts.json = false;
|
445 | opts.level = 'verbose';
|
446 | }
|
447 |
|
448 | if (verbose >= 2) {
|
449 | opts.json = false;
|
450 | opts.level = 'silly';
|
451 | }
|
452 | } else {
|
453 | opts.level = 'info';
|
454 | }
|
455 | log.format(opts);
|
456 | },
|
457 |
|
458 | enableNestedCommands: function (command) {
|
459 | if (!command.parent) {
|
460 | command.option('-v, --version', 'output the application version');
|
461 | }
|
462 |
|
463 | if (!command.categories) {
|
464 | command.categories = {};
|
465 | }
|
466 |
|
467 | command.category = function (name) {
|
468 | var category = command.categories[name];
|
469 | if (!command.categories[name] || (command.categories[name]).stub && this.executingCmd) {
|
470 | category = command.categories[name] = new AzureCli(name, this);
|
471 | command.categories[name].stub = false;
|
472 | category.helpInformation = command.categoryHelpInformation;
|
473 | command.enableNestedCommands(category);
|
474 | }
|
475 |
|
476 | return category;
|
477 | };
|
478 |
|
479 | command.on('*', function () {
|
480 | var args = command.rawArgs.slice(0, 2);
|
481 | var raw = command.normalize(command.rawArgs.slice(2));
|
482 |
|
483 | var category = '*';
|
484 | if (raw.length > 0) {
|
485 | category = raw[0];
|
486 | args = args.concat(raw.slice(1));
|
487 | }
|
488 |
|
489 | var i, index;
|
490 | var targetCmd;
|
491 | var cat = command.categories[category];
|
492 |
|
493 | if (!cat){
|
494 | index = command.searchCommand(category, command.commands);
|
495 | if (index !== -1){
|
496 | targetCmd = require(command.commands[index].filePath);
|
497 | targetCmd.init.apply(command, [command]);
|
498 |
|
499 | return this.parse(command.rawArgs);
|
500 | }
|
501 | }
|
502 |
|
503 |
|
504 | for (i = 2; cat && i < args.length && args[i] !== '-h' && args[i] !== '--help'; i++) {
|
505 | index = command.searchCommand(args[i], cat.commands);
|
506 | if (index !== -1) {
|
507 | targetCmd = cat.commands[index];
|
508 | break;
|
509 | } else {
|
510 | cat = cat.categories[args[i]];
|
511 | }
|
512 | }
|
513 |
|
514 |
|
515 | if (targetCmd) {
|
516 |
|
517 | if (i+1 < args.length && (args[i+1] === '-h' || args[i+1] === '--help')) {
|
518 | return targetCmd.commandHelpInformation();
|
519 | }
|
520 | this.executingCmd = true;
|
521 | if (!this.workaroundOnAsmSiteCommands(targetCmd, command)) {
|
522 | targetCmd = require(targetCmd.filePath);
|
523 | targetCmd.init(command);
|
524 | }
|
525 | cat = command.categories[category];
|
526 | return cat.parse(args);
|
527 | }
|
528 |
|
529 | if (!cat) {
|
530 | var toBlame = (i>2) ? args[i-1] : category;
|
531 | log.error('\'' + toBlame + '\' is not an azure command. See \'azure help\'.');
|
532 | } else {
|
533 |
|
534 | command.categoryHelpInformation.apply(cat,[]);
|
535 | }
|
536 | });
|
537 | },
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 | workaroundOnAsmSiteCommands: function (targetCmd, command) {
|
544 | if (path.basename(targetCmd.filePath).indexOf('site.') !== -1) {
|
545 | var siteCmdDir = path.dirname(targetCmd.filePath);
|
546 | var siteCmdFiles = utilsCore.getFiles(siteCmdDir, false);
|
547 | var filesToLoad = {};
|
548 | var sitePlugins = [];
|
549 |
|
550 |
|
551 |
|
552 | siteCmdFiles.forEach(function (f) {
|
553 | var basename = path.basename(f);
|
554 | if (basename.indexOf('site.') === 0) {
|
555 | var nameWithoutExt = basename.substring(0, basename.lastIndexOf('.'));
|
556 | var ext = path.extname(basename);
|
557 | if (filesToLoad[nameWithoutExt]) {
|
558 | if (ext === '.js') {
|
559 | filesToLoad[nameWithoutExt] = f;
|
560 | }
|
561 | } else {
|
562 | filesToLoad[nameWithoutExt] = f;
|
563 | }
|
564 | }
|
565 | });
|
566 | Object.keys(filesToLoad).forEach(function (f) {
|
567 | sitePlugins.push(require(filesToLoad[f]));
|
568 | });
|
569 | sitePlugins.forEach(function (plugin) {
|
570 | if (plugin.init) {
|
571 | plugin.init(command);
|
572 | }
|
573 | });
|
574 | return true;
|
575 | } else {
|
576 | return false;
|
577 | }
|
578 | },
|
579 |
|
580 | command: function (name) {
|
581 | var args = name.split(/ +/);
|
582 | var cmd = new AzureCli(args.shift(), this);
|
583 | cmd.option('-v, --verbose', 'use verbose output');
|
584 | cmd.option('-vv', 'more verbose with debug output');
|
585 | cmd.option('--json', 'use json output');
|
586 |
|
587 | var caller = callerId.getData();
|
588 | cmd.filePath = caller.filePath;
|
589 | cmd.helpInformation = cmd.commandHelpInformation;
|
590 | var index = this.searchCommand(cmd.name, this.commands);
|
591 | if (index !== -1) {
|
592 | this.commands[index] = cmd;
|
593 | } else {
|
594 | this.commands.push(cmd);
|
595 | }
|
596 | cmd.parseExpectedArgs(args);
|
597 | return cmd;
|
598 | },
|
599 |
|
600 | searchCommand: function(name, commands) {
|
601 | if ( !commands || !name ) return -1;
|
602 | for (var i = 0; i < commands.length; i++) {
|
603 | if (commands[i].name === name) {
|
604 | return i;
|
605 | }
|
606 | }
|
607 | return -1;
|
608 | },
|
609 |
|
610 | deprecatedDescription: function (text, newCommand) {
|
611 | return this.description(util.format('%s (deprecated. This command is deprecated and will be removed in a future version. Please use \"%s\" instead', text, newCommand));
|
612 | },
|
613 |
|
614 | detailedDescription: function (str) {
|
615 | if (0 === arguments.length) return this._detailedDescription;
|
616 | this._detailedDescription = str;
|
617 | return this;
|
618 | },
|
619 |
|
620 | getMode: function () {
|
621 | return this._mode;
|
622 | },
|
623 |
|
624 | isAsmMode: function () {
|
625 | return utilsCore.ignoreCaseEquals(this._mode, 'asm');
|
626 | },
|
627 |
|
628 | isArmMode: function () {
|
629 | return utilsCore.ignoreCaseEquals(this._mode, 'arm');
|
630 | },
|
631 |
|
632 | checkVersion: function () {
|
633 |
|
634 | var version = process.version;
|
635 | var ver = version.split('.');
|
636 | var ver1num = parseInt(ver[1], 10);
|
637 | var ver2num = parseInt(ver[2], 10);
|
638 | if (ver[0] === 'v0') {
|
639 | if (ver1num < 6 || (ver1num === 6 && ver2num < 15)) {
|
640 | throw new Error('You need node.js v0.6.15 or higher to run this code. Your version: ' +
|
641 | version);
|
642 | }
|
643 | if (ver1num === 7 && ver2num <= 7) {
|
644 | throw new Error('You need node.js v0.6.15 or higher to run this code. Your version ' +
|
645 | version + ' won\'t work either.');
|
646 | }
|
647 | }
|
648 | }
|
649 | });
|
650 |
|
651 | exports = module.exports = AzureCli;
|