diff --git a/.changelogrc b/.changelogrc index 14a1a689..83c18d09 100644 --- a/.changelogrc +++ b/.changelogrc @@ -2,10 +2,10 @@ "app_name": "", "logo": "", "intro": "", - "branch" : "master", + "branch" : "development", "repo_url": "https://github.com/Unitech/pm2", - "version_name" : "2.10.4", - "tag": "2.10.3", + "version_name" : "3.0.0", + "tag": "2.10.4", "file": "currentTagChangelog.md", "template": "changelogTemplate.md", "sections": [ diff --git a/.gitignore b/.gitignore index 31e1bf66..e6576745 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ package-lock.json *.swp *.swo currentTagChangelog.md +joblog-X +test/fixtures/path-check*.txt diff --git a/.travis.yml b/.travis.yml index 1425f6b0..0c217f31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,14 +4,16 @@ node_js: - "4" - "6" - "8" - - "0.12" +git: + depth: 5 os: - linux before_install: - sudo apt-get -qq update + - sudo apt-get install parallel - sudo apt-get install python3 - sudo apt-get install php5-cli services: - docker -notifications: - slack: pm2-nodejs:5Lolyw2LMnwy8fziqOGILQxG +install: + - npm install diff --git a/ADVANCED_README.md b/ADVANCED_README.md index 8c6931d9..1de13ee0 100644 --- a/ADVANCED_README.md +++ b/ADVANCED_README.md @@ -32,7 +32,7 @@ - [Without Keymetrics](#without-keymetrics) - [With Keymetrics](#with-keymetrics) -### Deployment - ecosystem.json +### Deployment - ecosystem.config.js - [Getting started with deployment](#deployment) - [Deployment options](#deployment-help) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd96fd2..fcb607ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,312 @@ +## 3.0.0 ( Wed Jun 20 2018 11:06:21 GMT+0200 (CEST) ) + + +## Breaking changes + - merge_logs is now activated by default if not in cluster mode. Logs will not be suffixed by the pm_id if only one app is started + ([ae02adf6](https://github.com/Unitech/pm2/commit/ae02adf63f70ceb3bf101be968996ca68d9ce277)) + - Drop support for node 0.12 + - Drop gracefulReload command + - Remove Interactor from PM2 source code + - Replace pmx with [pm2-io-apm](https://github.com/keymetrics/pm2-io-apm) + + +## Bug Fixes + - return the configuration and allow custom conf to override default values + ([37dc7de1](https://github.com/Unitech/pm2/commit/37dc7de11e930aa4fce6a485e892f11ee714acd6)) + - add use strict for node 4 compatibility + ([ba2ee3b1](https://github.com/Unitech/pm2/commit/ba2ee3b1ea9aa5fa665e706b3d49a205eac44d53)) + - #3605 fix parameters definition, don't use camelcase for properties + ([c8616276](https://github.com/Unitech/pm2/commit/c8616276e4e08b4d90a742e219372e775bb81098)) + - #3695 change version check method in order to make it work with alpha/beta versions + ([052d6c55](https://github.com/Unitech/pm2/commit/052d6c55df0e941e1dd11430bbcbcaa34061a06e)) + - deprecated warning on isbinaryfile + ([db09275f](https://github.com/Unitech/pm2/commit/db09275f8e353e257c89e12fed754236b15cee74)) + - #3688 test adaptation + pm2 serve --port option + ([f0249684](https://github.com/Unitech/pm2/commit/f0249684bcbfdb75749a516f447c8e8d32020709)) + - startup script issue 18.04 #3645 + ([ff1a7f31](https://github.com/Unitech/pm2/commit/ff1a7f315bfee38eb9fd9cdd63efcc0d971585f8)) + - that this - uncache node_modules + ([294038d7](https://github.com/Unitech/pm2/commit/294038d76272a915e3addc67d3694717a9f7d704)) + - verify default conf variable via package.json on public module + ([157b106d](https://github.com/Unitech/pm2/commit/157b106df78af1d28d37bbea069b926de4dceca5)) + - bug because of const + ([56f05a90](https://github.com/Unitech/pm2/commit/56f05a900b03fb0c8dd635aede666c7d2f213271)) + - do not run two pm2 para cmds + ([3274132b](https://github.com/Unitech/pm2/commit/3274132b866ba5c93d5786e755acbada922f5f1e)) + - version + ([3ec178e5](https://github.com/Unitech/pm2/commit/3ec178e577e79730aae02c913301cd905ea8ce52)) + - re-enable agent tests + ([e6febcd7](https://github.com/Unitech/pm2/commit/e6febcd70dd0f1e68b74df8563d3046ee3b32b89)) + - test/display summary + ([b075e6d0](https://github.com/Unitech/pm2/commit/b075e6d09b09ff371adf045dc5079bb8ef82f1cf)) + - skip interactor tests + ([36c4d6bc](https://github.com/Unitech/pm2/commit/36c4d6bca7445b46afc1236dc8ab4b8bf921148b)) + - remove unused tests + ([234c6314](https://github.com/Unitech/pm2/commit/234c63143e723a508796bc1d323c7241979bf4c2)) + - add missing libraries in travis + ([88fbb845](https://github.com/Unitech/pm2/commit/88fbb84597cee7029ce33f5b7e20e45f5a815b4b)) + - remove unused variable when trying to use tracing + ([3aeeba02](https://github.com/Unitech/pm2/commit/3aeeba02f628bf4f19e8d5b93657fd94a6ef0ec7)) + - remove useless tests from .sh + ([e0be81c8](https://github.com/Unitech/pm2/commit/e0be81c86c7defb5e7a271edd5cc37f960c6aa69)) + - conflict + ([e13f39c9](https://github.com/Unitech/pm2/commit/e13f39c90b6a5e803c59c5424332520564703f5c)) + - fix bug with interpreter args + ([b26efa0d](https://github.com/Unitech/pm2/commit/b26efa0d4cd72cf04762df7b7d2eaddc4f4117d2)) + - improve error message if action has failed + ([d9f44f17](https://github.com/Unitech/pm2/commit/d9f44f170f115c2d6dfb6a7fe71dc31bd7fb66fb)) + - use polyfill module for copySync with node 4.x + ([bc07f43b](https://github.com/Unitech/pm2/commit/bc07f43b115066f6077606df8f59379777f2a917)) + - improve error message if action has failed + ([dacc6542](https://github.com/Unitech/pm2/commit/dacc654207cbe494af0d12a3f9f27c3b16541802)) + - solve empty list when no process and try to update pm2 + ([89511846](https://github.com/Unitech/pm2/commit/8951184688c720ded5b4b46bd5b393c3793f9b03)) + - #3485 fix issue when there is empty dump file + ([f2523f6a](https://github.com/Unitech/pm2/commit/f2523f6a6b9d8b61ba6ace7b89a0353bee76360b)) + - #3456 use homedir() instead of process.env.HOME, make module installation work on windows + ([1e001732](https://github.com/Unitech/pm2/commit/1e0017325fc8cf658263fb4e02c7bf8912f422b3)) + + + + +## Features + - add support for openbsd rc.d init scripts + ([fdeb0c32](https://github.com/Unitech/pm2/commit/fdeb0c327afd91b113b214c4c4de187848f9f1cb)) + - add kill_retry_time argument + ([b2cc0031](https://github.com/Unitech/pm2/commit/b2cc003114b44f1a9a31876ee4a2f4cb91e210b3)) + + - **bin/pm2** + - improve usage + ([2c310084](https://github.com/Unitech/pm2/commit/2c310084453dd7b1546957e59b1fc7ef964d425b)) + + + + +## Refactor + - use @pm2/js-api for login/register on pm2.io via CLI + ([cb6521ac](https://github.com/Unitech/pm2/commit/cb6521ac32f4737c42fc97fef972960bfe16c829)) + - keymetrics examples + ([109b331d](https://github.com/Unitech/pm2/commit/109b331ddf37e061d1890ef952f4cd167ce53f64)) + - faster cli with less require + ([ee5e6a06](https://github.com/Unitech/pm2/commit/ee5e6a06cbf93f2d1fa7fa022d6bdcad55a39695)) + - replace fs-extra with node calls + ([4576b4c9](https://github.com/Unitech/pm2/commit/4576b4c97bc685c9d774018d6b29c918abd7cb8d)) + - centralize SECRET/PUBLIC/MACHINE_NAME + change some wordings + ([d0a2a30e](https://github.com/Unitech/pm2/commit/d0a2a30e4110496b178199fb33e026d6402dd00d)) + - remove test deported to keymetrics-agent + ([299a52a2](https://github.com/Unitech/pm2/commit/299a52a253d70edcde23cbd7e0c201d492984df4)) + - parallel test v1 + ([08612de5](https://github.com/Unitech/pm2/commit/08612de5b7893a004ae33ed77fcb2ee3ff7b2251)) + - e2e test rewrite + ([2b9ffd4e](https://github.com/Unitech/pm2/commit/2b9ffd4eb493f1ff32c979e3811f4f1fedfae97d)) + - drop gracefullreload + ([bb57c76d](https://github.com/Unitech/pm2/commit/bb57c76d4191343925013d4353299092d80732c9)) + - add node 4.x support + ([d322dd00](https://github.com/Unitech/pm2/commit/d322dd00de0f527224c027b4fec5e86f12fd69ed)) + - create alias method instead of modify prototype + ([6d8f0dfa](https://github.com/Unitech/pm2/commit/6d8f0dfae8106deb2fee0a7ae15b6ca9802a066d)) + - change safety var to const + ([047aa494](https://github.com/Unitech/pm2/commit/047aa494d5c4dd4342915766b54d673db0d5cdf1)) + - drop some 0.x patch + ([0cab8880](https://github.com/Unitech/pm2/commit/0cab8880ffa362cf27ab7d7b6a64d6b478dce7cd)) + - remove prototype from API and create method + ([9552bd61](https://github.com/Unitech/pm2/commit/9552bd61b72692beb620a91765ad440cdf6abefe)) + - transform API into class + ([e3831f95](https://github.com/Unitech/pm2/commit/e3831f95c8d71f98e8840da37f7e883727eccd59)) + - name tests well + ([c3ccc651](https://github.com/Unitech/pm2/commit/c3ccc651d09ed7291090f516637b75bda99ff71c)) + - refactor e2e one line parallel + ([93802711](https://github.com/Unitech/pm2/commit/938027117cdb2f300ee772ab27f008cbe22a4b19)) + - e2e rename + ([8a7db95a](https://github.com/Unitech/pm2/commit/8a7db95aabc8437f292af0316cec81ab80ec41f5)) + - change params + ([282186f2](https://github.com/Unitech/pm2/commit/282186f24b19b010999f7c7c49750935ef19c190)) + - parallelize bash test + ([d4b4375e](https://github.com/Unitech/pm2/commit/d4b4375e16fe7ac463b252702da662d3a21bf8b4)) + + + + +## Test + - adapt test to new api + ([7a275e27](https://github.com/Unitech/pm2/commit/7a275e279ea01b1239e9dd8b9cf8e088e407b96d)) + - refactor before/after + ([b85ca3ca](https://github.com/Unitech/pm2/commit/b85ca3caa3c68e18f7ce6954cc85e90a9d33efef)) + - 3 concurrent jobs + ([472aba34](https://github.com/Unitech/pm2/commit/472aba3499ff2d9d0eb834e819410026b1a44503)) + - move test + ([9c973324](https://github.com/Unitech/pm2/commit/9c9733246dbe6afff1b488bc3ba3b6fea3877ea5)) + - move test + ([952b7631](https://github.com/Unitech/pm2/commit/952b7631d19e1074ea73cc7a67bbaefe20950603)) + - fix test with km_link + ([23fd8ecf](https://github.com/Unitech/pm2/commit/23fd8ecfea9b2bf61359f62a8e6e1a582c3b0d6e)) + + + + +## Chore + - shorten ecosystem file + ([992a0452](https://github.com/Unitech/pm2/commit/992a045227aed559e708ac4e6bb3f54beabe48e0)) + - change motd wording + ([aa183ba1](https://github.com/Unitech/pm2/commit/aa183ba19d88777d82619aa40499c2661d67879e)) + - merge master in development + ([0e4453d9](https://github.com/Unitech/pm2/commit/0e4453d9cc789aa08ee778ff400572337e90d2e3)) + - keymetrics -> pm2 + ([2c8170c2](https://github.com/Unitech/pm2/commit/2c8170c25e231eb8827bb0944b76c2f4b041d84e)) + - upgrade all modules + keymetrics-agent -> pm2/agent + increase version enabling v8-compile-cache + ([53ca18c1](https://github.com/Unitech/pm2/commit/53ca18c12868ab177b60a4edff2ccaa8127e301f)) + - pm2.io -> @pm2/io + ([ae098962](https://github.com/Unitech/pm2/commit/ae098962df35eee7f482dc0a514fd29a02a5f4ad)) + - right names as pm2 maintainers + ([e8cd7131](https://github.com/Unitech/pm2/commit/e8cd7131a6b9c9d497a2079bcbfc03770a753a06)) + - add changelog generation into contributing.md + ([d77bfbc3](https://github.com/Unitech/pm2/commit/d77bfbc3c8929851ee19ea604b2a6481d03771e3)) + - cache node_modules + ([81627e94](https://github.com/Unitech/pm2/commit/81627e94c72efa1f4d726e20bbf67f0bbd5c116f)) + - clone last 5 commits + ([dad38ed1](https://github.com/Unitech/pm2/commit/dad38ed1bae849147f66e44186cd71c4b9cb022d)) + - delete old stagnating pmx inside test + ([36834c2c](https://github.com/Unitech/pm2/commit/36834c2c00d496e04c38abaca30202eb650015c4)) + - pmx -> pm2.io + ([adcbebc3](https://github.com/Unitech/pm2/commit/adcbebc3f6419cd97c5ea99f3c3a6789585bda66)) + - updgrade pmx-2 + ([eeeb2988](https://github.com/Unitech/pm2/commit/eeeb2988f8886e405aea107db3b888fc1fc929f8)) + - disable legacy test + ([13723bd9](https://github.com/Unitech/pm2/commit/13723bd938d0e6fb1cbf35f15eabe91c52d87b58)) + - remove test for pmx alert system + ([c43414a6](https://github.com/Unitech/pm2/commit/c43414a63438d724b8099eb531ec72bab23b8ca2)) + - sync from master + ([3424ee27](https://github.com/Unitech/pm2/commit/3424ee27870feaf62fdf4509cce9015f8b1a8a2e)) + - add unique id for each process + ([85a5ee0f](https://github.com/Unitech/pm2/commit/85a5ee0f1fd16da9635fb4b16ddcd8d53aca8224)) + - use npm install for CI as yarn has issue with npm + ([52902186](https://github.com/Unitech/pm2/commit/5290218626af815f6cae8173bc78d21881a4dda8)) + - remove unused dependency + ([830fc15f](https://github.com/Unitech/pm2/commit/830fc15fad1aee95e65b2681482b03369f1f97d7)) + - upgrade PM2 to 3.0 + ([4bc2eb4c](https://github.com/Unitech/pm2/commit/4bc2eb4c9a8179b9ae38438e98ce7650a91b64db)) + - remove unused console.log + ([33db5084](https://github.com/Unitech/pm2/commit/33db5084814ae7940c90b7f933f9514d28008b78)) + - wording on error message + ([c251c8c9](https://github.com/Unitech/pm2/commit/c251c8c97e6f18aae584cac6b7f3c83cf4f2de9c)) + - revert PR #3496 + ([aae1d55e](https://github.com/Unitech/pm2/commit/aae1d55e410c4dcfbbca83eaabbdf1a65d55f3aa)) + - fix issue with snapshot command + remove command forceGc + ([97fd1010](https://github.com/Unitech/pm2/commit/97fd1010d005e59f2411042fa95891f9717fa8b7)) + - wording on error message + ([5f78ecbf](https://github.com/Unitech/pm2/commit/5f78ecbf90f9f46a7feb2a169968e86b0ecac91e)) + - drop 0.12 test on travis + ([beb6e487](https://github.com/Unitech/pm2/commit/beb6e48787c39c66569141d0fd8d090736114d23)) + - downgrade promptly + ([074a7a40](https://github.com/Unitech/pm2/commit/074a7a407a31b4d88442f5834d253d62f4e543b8)) + - remove coffee and livescript dependencies + ([13d6565c](https://github.com/Unitech/pm2/commit/13d6565c72e3596d05f87bfc8be15d3ee45fb279)) + - upgrade module version and engine version + ([84796956](https://github.com/Unitech/pm2/commit/84796956347ca638750fe89cb5545e2a90a0f2c2)) + + + + +## Branchs merged + - Merge branch 'development' into chore/dev-cache-node-modules + ([146c4e11](https://github.com/Unitech/pm2/commit/146c4e113c88e8ade17c7558c8e14cf523a3b2d6)) + - Merge branch 'development' of https://github.com/Unitech/pm2 into new-agent + ([3514e7fa](https://github.com/Unitech/pm2/commit/3514e7fac624bb83b4cc22651ebc05385f9c284d)) + - Merge branch 'development' into master + ([f5668331](https://github.com/Unitech/pm2/commit/f5668331dbe7346304258317a3b84450f421ed03)) + - Merge branch 'development' into new-usage-cli + ([4ae27694](https://github.com/Unitech/pm2/commit/4ae27694e34c4bc6ed389566d71fc5ec48b69652)) + - Merge branch 'Eywek-improv/agent' into new-agent + ([3e259dd1](https://github.com/Unitech/pm2/commit/3e259dd1d6bb96ea41897c49f3a84557c00c7dad)) + - Merge branch 'ecosystem-documentation' of github.com:rmonnier/pm2 into ecosystem-documentation + ([98348955](https://github.com/Unitech/pm2/commit/98348955a6eb3a9cd524b991bd1dd6ed03d2c857)) + - Merge branch 'development' into ecosystem-documentation + ([40157784](https://github.com/Unitech/pm2/commit/40157784a63bcb0e744d4ed56f6c687e28379fdd)) + - Merge branch 'inspect_mode' of github.com:Unitech/pm2 into inspect_mode + ([7e1494c7](https://github.com/Unitech/pm2/commit/7e1494c7f7971aaf1f4d00d2ee691c3c41775001)) + - Merge branch 'development' of github.com:Unitech/pm2 into development + ([48f81a8b](https://github.com/Unitech/pm2/commit/48f81a8b2f6f0db39edd86083fb369b74845c387)) + - Merge branch 'development' into master + ([47e54109](https://github.com/Unitech/pm2/commit/47e5410987ab3d824a34c062d70c24ab686e57db)) + - Merge branch 'development' into module_install_windows + ([7b82fb91](https://github.com/Unitech/pm2/commit/7b82fb916ed453c1c263bae43c962f6a5294d810)) + - Merge branch 'development' into module_install_windows + ([80b0495f](https://github.com/Unitech/pm2/commit/80b0495f63d1224b850af4b14cdeb055e3fef50b)) + + + + +## Pull requests merged + - Merge pull request #3726 from soyuka/fix-list + ([0255c5a6](https://github.com/Unitech/pm2/commit/0255c5a6ab1b8a8f609d2183d998695b8c42838d)) + - Merge pull request #3725 from soyuka/fix-list + ([a39eb4f8](https://github.com/Unitech/pm2/commit/a39eb4f806e87565f53758a19f0ee289b6489b67)) + - Merge pull request #3718 from AaronM04/openbsd-init-script + ([85458261](https://github.com/Unitech/pm2/commit/85458261d2673c609cb252d64ad4dfbaa466d848)) + - Merge pull request #3721 from Unitech/io_conf + ([70ec1f81](https://github.com/Unitech/pm2/commit/70ec1f81eae089f75e82723fde7b0b3926d0a9bc)) + - Merge pull request #3716 from Unitech/io_conf + ([0bc000b9](https://github.com/Unitech/pm2/commit/0bc000b9aae7dd37b456bc2d4fbc9eb4a9f047ef)) + - Merge pull request #3714 from Unitech/definition + ([d8cff0de](https://github.com/Unitech/pm2/commit/d8cff0dec5160a620d1512ff56726c073368d1a4)) + - Merge pull request #3700 from Unitech/report_error + ([4b2cad40](https://github.com/Unitech/pm2/commit/4b2cad407b76994e978074a2a3825fe70656304d)) + - Merge pull request #3670 from Unitech/changelog + ([4bcbcce1](https://github.com/Unitech/pm2/commit/4bcbcce16ced596f6ca2bab2b77d608a174a7c1a)) + - Merge pull request #3662 from DanielRuf/chore/dev-cache-node-modules + ([540590ee](https://github.com/Unitech/pm2/commit/540590ee056b44eed3b688a7b0b16ca78ec82cd9)) + - Merge pull request #3663 from DanielRuf/chore/dev-clone-last-5-commits + ([bdf95fc9](https://github.com/Unitech/pm2/commit/bdf95fc997f9ab2995b23668f25f11b6e98b5c47)) + - Merge pull request #3584 from ngtmuzi/development + ([33984b64](https://github.com/Unitech/pm2/commit/33984b64a2969ca4a3a5913f0f7da0242b6c5ec1)) + - Merge pull request #3500 from Unitech/test-parallel + ([da56c7af](https://github.com/Unitech/pm2/commit/da56c7aff18d3a38b3ad068b22cd75b290bac9d0)) + - Merge pull request #3539 from KimSeongIl/master + ([1325704d](https://github.com/Unitech/pm2/commit/1325704d95d324e56b0ebc86aed8137e0d0aa450)) + - Merge pull request #3556 from N-Nagorny/logs-smart-app-name-cutting + ([bfddf4fd](https://github.com/Unitech/pm2/commit/bfddf4fdef5ec293119d850cc2532ac5d6490ae3)) + - Merge pull request #3553 from Unitech/fix_tracing_not_working + ([9d51fe08](https://github.com/Unitech/pm2/commit/9d51fe0819182339f3a6a4aee7ea603ea3f4dd76)) + - Merge pull request #3549 from Eywek/new-agent + ([2f04027b](https://github.com/Unitech/pm2/commit/2f04027b536094d192b399677b3a113102f06b8e)) + - Merge pull request #3548 from rmonnier/start-ecosystem-default + ([55412f26](https://github.com/Unitech/pm2/commit/55412f263250395de0085144932cfe06b8c7180d)) + - Merge pull request #3546 from soyuka/improve-monitor-perf + ([e4e29233](https://github.com/Unitech/pm2/commit/e4e29233f99db36462a6e8f48eb8ebd3d2fd9fa5)) + - Merge pull request #3534 from rmonnier/new-usage-cli + ([5dfba8a4](https://github.com/Unitech/pm2/commit/5dfba8a4491f0bb83f2879915f0c4b164be2552c)) + - Merge pull request #3542 from rmonnier/default-start-ecosystem + ([c65595f4](https://github.com/Unitech/pm2/commit/c65595f4a70659e1e0d753e6c28a1fcedf45a91a)) + - Merge pull request #3545 from rmonnier/default-ecosystem + ([b3718656](https://github.com/Unitech/pm2/commit/b3718656f630aa54880343d9742534a2a508daec)) + - Merge pull request #3543 from rmonnier/ecosystem-documentation + ([a60580a1](https://github.com/Unitech/pm2/commit/a60580a12b4a0066c8df6620317fbc8bf599b0b6)) + - Merge pull request #3541 from soyuka/development + ([67e7a015](https://github.com/Unitech/pm2/commit/67e7a015cabaa7b08206a3b1bf9c0399af88f76b)) + - Merge pull request #3511 from Unitech/inspect_mode + ([75fb87f8](https://github.com/Unitech/pm2/commit/75fb87f8a1c46a6db8e974b421e857175e69b535)) + - Merge pull request #3517 from Unitech/polyfill_fs_copy_node4 + ([524f5494](https://github.com/Unitech/pm2/commit/524f54948de5080632d43bb512038d7bd7271619)) + - Merge pull request #3516 from Unitech/drop_unused_feature + ([9436f11a](https://github.com/Unitech/pm2/commit/9436f11aeecfc07e77aa9d6b108df4478b43402e)) + - Merge pull request #3510 from Unitech/dump_refacto + ([674e4469](https://github.com/Unitech/pm2/commit/674e4469554e6a765bb3d57a3c083e6ab53b20cc)) + - Merge pull request #3501 from Unitech/refactor_api + ([9f2c4ca4](https://github.com/Unitech/pm2/commit/9f2c4ca4c9eadf6c7730e3889c72e908cd2d8f5d)) + - Merge pull request #3496 from rmonnier/master + ([829cc303](https://github.com/Unitech/pm2/commit/829cc3032b2d61e20f7a2e7d1d819c0ddc0845e8)) + - Merge pull request #3484 from Unitech/pull_by_name + ([24d29404](https://github.com/Unitech/pm2/commit/24d294049008a0d01b2bc407b9b2b880d5843fbd)) + - Merge pull request #3482 from Unitech/mjs_support + ([ebe7b048](https://github.com/Unitech/pm2/commit/ebe7b0487218557858aaa98527360eca1776b140)) + - Merge pull request #3495 from Unitech/module_install_windows + ([e9c625d3](https://github.com/Unitech/pm2/commit/e9c625d3088c71eef4237ecd866b806957c61815)) + - Merge pull request #3507 from cheapsteak/patch-1 + ([a49287d6](https://github.com/Unitech/pm2/commit/a49287d6a1d22b39270e2d05dee2a17c0ed55797)) + + + + ## 2.10.4 ( Thu May 17 2018 14:32:40 GMT+0200 (CEST) ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83fa8bea..21dda9d7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,3 +100,25 @@ This test files are located in test/bash/* - `$HOME/.pm2/pm2.pid` PM2 pid - `$HOME/.pm2/rpc.sock` Socket file for remote commands - `$HOME/.pm2/pub.sock` Socket file for publishable events + +## Generate changelog + +### requirements + +``` +npm install git-changelog -g +``` + +### usage + +Edit .changelogrc +Change "version_name" to the next version to release (example 1.1.2). +Change "tag" to the latest existing tag (example 1.1.1). + +Run the following command into pm2 directory +``` +git-changelog +``` + +It will generate currentTagChangelog.md file. +Just copy/paste the result into changelog.md diff --git a/README.md b/README.md index 6bee4318..f0a6a68b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ npm version - + NPM Downloads @@ -234,14 +234,14 @@ $ pm2 reloadLogs # Reload all logs PM2 can generates and configure a startup script to keep PM2 and your processes alive at every server restart. -Supports init systems like: **systemd** (Ubuntu 16, CentOS, Arch), **upstart** (Ubuntu 14/12), **launchd** (MacOSx, Darwin), **rc.d** (FreeBSD). +Supports init systems like: **systemd** (Ubuntu 16, CentOS, Arch), **upstart** (Ubuntu 14/12), **launchd** (MacOSx, Darwin), **rc.d** (FreeBSD, OpenBSD). ```bash # Auto detect init system + generate and setup PM2 boot at server startup $ pm2 startup # Manually specify the startup system -# Can be: systemd, upstart, launchd, rcd +# Can be: systemd, upstart, launchd, rcd, rcd-openbsd $ pm2 startup [platform] # Disable and remove PM2 boot at server startup @@ -294,7 +294,6 @@ $ pm2 reset [app-name] # Reset all counters $ pm2 stop all # Stop all apps $ pm2 stop 0 # Stop process with id 0 $ pm2 restart all # Restart all apps -$ pm2 gracefulReload all # Gracefully reload all apps in cluster mode $ pm2 delete all # Kill and delete all apps $ pm2 delete 0 # Delete app with id 0 diff --git a/bin/pm2 b/bin/pm2 index 05072f4b..002657e7 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -3,7 +3,7 @@ var semver = require('semver'); -if (semver.satisfies(process.versions.node, '>= 5.7.0')) +if (semver.satisfies(process.versions.node, '>= 6.0.0')) require('v8-compile-cache'); process.env.PM2_USAGE = 'CLI'; @@ -33,24 +33,29 @@ if (process.argv.indexOf('-v') > -1) { var pm2 = new PM2(); commander.version(pkg.version) - .option('-v --version', 'get version') + .option('-v --version', 'print pm2 version') .option('-s --silent', 'hide all messages', false) + .option('-n --name ', 'set a name for the process in the process list') .option('-m --mini-list', 'display a compacted list without formatting') + .option('--interpreter ', 'set a specific interpreter to use for executing app, default: node') + .option('--interpreter-args ', 'set arguments to pass to the interpreter (alias of --node-args)') + .option('--node-args ', 'space delimited arguments to pass to node') + .option('-o --output ', 'specify log file for stdout') + .option('-e --error ', 'specify log file for stderr') + .option('-l --log [path]', 'specify log file which gathers both stdout and stderr') + .option('--log-type ', 'specify log output style (raw by default, json optional)') + .option('--log-date-format ', 'add custom prefix timestamp to logs') + .option('--disable-logs', 'disable all logs storage') + .option('--env ', 'specify which set of environment variables from ecosystem file must be injected') + .option('-a --update-env', 'force an update of the environment with restart/reload (-a <=> apply)') .option('-f --force', 'force actions') - .option('--disable-logs', 'do not write logs') - .option('-n --name ', 'set a for script') .option('-i --instances ', 'launch [number] instances (for networked app)(load balanced)') .option('--parallel ', 'number of parallel actions (for restart/reload)') - .option('-l --log [path]', 'specify entire log file (error and out are both included)') - .option('-o --output ', 'specify out log file') - .option('-e --error ', 'specify error log file') .option('-p --pid ', 'specify pid file') .option('-k --kill-timeout ', 'delay before sending final SIGKILL signal to process') .option('--listen-timeout ', 'listen timeout on application reload') - .option('--max-memory-restart ', 'specify max memory amount used to autorestart (in octet or use syntax like 100M)') + .option('--max-memory-restart ', 'Restart the app if an amount of memory is exceeded (in bytes)') .option('--restart-delay ', 'specify a delay between restarts (in milliseconds)') - .option('--env ', 'specify environment to get specific env variables (for JSON declaration)') - .option('--log-type ', 'specify log output style (raw by default, json optional)') .option('-x --execute-command', 'execute a program using fork system') .option('--max-restarts [count]', 'only restart the script COUNT times') .option('-u --user ', 'define user when generating startup script') @@ -62,19 +67,14 @@ commander.version(pkg.version) .option('--service-name ', 'define service name when generating startup script') .option('-c --cron ', 'restart a running process based on a cron pattern') .option('-w --write', 'write configuration in local folder') - .option('--interpreter ', 'the interpreter pm2 should use for executing app (bash, python...)') - .option('--interpreter-args ', 'interpret options (alias of --node-args)') - .option('--log-date-format ', 'add custom prefix timestamp to logs') .option('--no-daemon', 'run pm2 daemon in the foreground if it doesn\'t exist already') - .option('-a --update-env', 'update environment on restart/reload (-a <=> apply)') .option('--source-map-support', 'force source map support') .option('--only ', 'with json declaration, allow to only act on one application') .option('--disable-source-map-support', 'force source map support') .option('--wait-ready', 'ask pm2 to wait for ready event from your app') .option('--merge-logs', 'merge logs from different instances but keep error and out separated') .option('--watch [paths]', 'watch application folder for changes', function(v, m) { m.push(v); return m;}, []) - .option('--ignore-watch ', 'folder/files to be ignored watching, should be a specific name or regex - e.g. --ignore-watch="test node_modules \"some scripts\""') - .option('--node-args ', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"') + .option('--ignore-watch ', 'List of paths to ignore (name or regex)') .option('--no-color', 'skip colors') .option('--no-vizion', 'start an app without vizion feature (versioning control)') .option('--no-autorestart', 'start an app without automatic restart') @@ -90,38 +90,50 @@ commander.version(pkg.version) .option('--deep-monitoring', 'enable all monitoring tools (equivalent to --v8 --event-loop-inspector --trace)') .usage('[cmd] app'); -commander.on('--help', function() { - console.log(' Basic Examples:'); - console.log(''); - console.log(' Start an app using all CPUs available + set a name :'); - console.log(' $ pm2 start app.js -i 0 --name "api"'); - console.log(''); - console.log(' Restart the previous app launched, by name :'); - console.log(' $ pm2 restart api'); - console.log(''); - console.log(' Stop the app :'); - console.log(' $ pm2 stop api'); - console.log(''); - console.log(' Restart the app that is stopped :'); - console.log(' $ pm2 restart api'); - console.log(''); - console.log(' Remove the app from the process list :'); - console.log(' $ pm2 delete api'); - console.log(''); - console.log(' Kill daemon pm2 :'); - console.log(' $ pm2 kill'); - console.log(''); - console.log(' Update pm2 :'); - console.log(' $ npm install pm2@latest -g ; pm2 update'); - console.log(''); - console.log(' More examples in https://github.com/Unitech/pm2#usagefeatures'); - console.log(''); - console.log(' Deployment help:'); - console.log(''); - console.log(' $ pm2 deploy help'); - console.log(''); - console.log(''); -}); + function displayUsage() { + console.log('usage: pm2 [options] ') + console.log(''); + console.log('pm2 -h, --help all available commands and options'); + console.log('pm2 examples display pm2 usage examples'); + console.log('pm2 -h help on a specific command'); + console.log(''); + console.log('Access pm2 files in ~/.pm2'); + } + + function displayExamples() { + console.log('- Start and add a process to the pm2 process list:') + console.log(''); + console.log(chalk.cyan(' $ pm2 start app.js --name app')); + console.log(''); + console.log('- Show the process list:'); + console.log(''); + console.log(chalk.cyan(' $ pm2 ls')); + console.log(''); + console.log('- Stop and delete a process from the pm2 process list:'); + console.log(''); + console.log(chalk.cyan(' $ pm2 delete app')); + console.log(''); + console.log('- Stop, start and restart a process from the process list:'); + console.log(''); + console.log(chalk.cyan(' $ pm2 stop app')); + console.log(chalk.cyan(' $ pm2 start app')); + console.log(chalk.cyan(' $ pm2 restart app')); + console.log(''); + console.log('- Clusterize an app to all CPU cores available:'); + console.log(''); + console.log(chalk.cyan(' $ pm2 start -i max')); + console.log(''); + console.log('- Update pm2 :'); + console.log(''); + console.log(chalk.cyan(' $ npm install pm2 -g && pm2 update')); + console.log(''); + console.log('- Install pm2 auto completion:') + console.log(''); + console.log(chalk.cyan(' $ pm2 completion install')) + console.log(''); + console.log('Check the full documentation on https://pm2.io/doc'); + console.log(''); + } if (process.argv.indexOf('-s') > -1) { for(var key in console){ @@ -155,7 +167,7 @@ function checkCompletion(){ return data.short; }), data); // array containing commands after which process name should be listed - var cmdProcess = ['stop', 'restart', 'scale', 'reload', 'gracefulReload', 'delete', 'reset', 'pull', 'forward', 'backward', 'logs', 'describe', 'desc', 'show']; + var cmdProcess = ['stop', 'restart', 'scale', 'reload', 'delete', 'reset', 'pull', 'forward', 'backward', 'logs', 'describe', 'desc', 'show']; if (cmdProcess.indexOf(data.prev) > -1) { pm2.list(function(err, list){ @@ -253,7 +265,7 @@ function patchCommanderArg(cmd) { // // Start command // -commander.command('start ') +commander.command('start [name|file|ecosystem|id...]') .option('--watch', 'Watch folder for changes') .option('--fresh', 'Rebuild Dockerfile') .option('--daemon', 'Run container in Daemon mode (debug purposes)') @@ -280,6 +292,9 @@ commander.command('start ') else { // Commander.js patch cmd = patchCommanderArg(cmd); + if (cmd.length === 0) { + cmd = [cst.APP_CONF_DEFAULT_FILE]; + } async.forEachLimit(cmd, 1, function(script, next) { pm2.start(script, commander, next); }, function(err) { @@ -329,7 +344,7 @@ commander.command('startOrGracefulReload ') // commander.command('stop ') .option('--watch', 'Stop watching folder for changes') - .description('stop a process (to start it again, do pm2 restart )') + .description('stop a process') .action(function(param) { async.forEachLimit(param, 1, function(script, next) { pm2.stop(script, next); @@ -390,25 +405,24 @@ commander.command('reload ') pm2.reload(pm2_id, commander); }); -// -// Reload process(es) -// -commander.command('gracefulReload ') - .description('gracefully reload a process. Send a "shutdown" message to close all connections.') - .action(function(pm2_id) { - pm2.gracefulReload(pm2_id, commander); - }); - commander.command('id ') .description('get process id by name') .action(function(name) { pm2.getProcessIdByName(name); }); +// Inspect a process +commander.command('inspect ') + .description('inspect a process') + .action(function(cmd) { + pm2.inspect(cmd, commander); + }); + // // Stop and delete a process by name from database // commander.command('delete ') + .alias('del') .description('stop and delete a process from pm2 process list') .action(function(name) { if (name == "-") { @@ -464,11 +478,11 @@ commander.command('update') /** * Module specifics */ -commander.command('install [module|git:// url|json]') +commander.command('install ') .alias('module:install') .option('--v1', 'install module in v1 manner (do not use it)') .option('--safe [time]', 'keep module backup, if new module fail = restore with previous') - .description('install or update a module (or a set of modules) and run it forever') + .description('install or update a module and run it forever') .action(function(plugin_name, opts) { if (opts.v1) commander.v1 = true; @@ -553,41 +567,42 @@ commander.command('report') commander.command('link [secret] [public] [name]') .alias('interact') .option('--info-node [url]', 'set url info node') - .description('linking action to keymetrics.io - command can be stop|info|delete|restart') + .option('--ws', 'websocket mode') + .description('link with the pm2 monitoring dashboard') .action(pm2._pre_interact.bind(pm2)); commander.command('unlink') - .description('linking action to keymetrics.io - command can be stop|info|delete|restart') + .description('unlink with the pm2 monitoring dashboard') .action(function() { pm2.unlink(); }); -commander.command('unmonitor [name]') - .description('unmonitor target process') - .action(function(name) { - pm2.monitorState('unmonitor', name); - }); - commander.command('monitor [name]') .description('monitor target process') .action(function(name) { pm2.monitorState('monitor', name); }); +commander.command('unmonitor [name]') + .description('unmonitor target process') + .action(function(name) { + pm2.monitorState('unmonitor', name); + }); + commander.command('open') - .description('open dashboard in browser') + .description('open the pm2 monitoring dashboard') .action(function(name) { pm2.openDashboard(); }); commander.command('register') - .description('create an account on keymetrics') + .description('register on pm2 monitoring') .action(function(name) { pm2.registerToKM(); }); commander.command('login') - .description('login to keymetrics and link current PM2') + .description('use login to link with the pm2 monitoring dashboard') .action(function(name) { pm2.loginToKM(); }); @@ -612,6 +627,15 @@ commander.command('dump') pm2.dump(); })); +// +// Delete dump file +// +commander.command('cleardump') + .description('Create empty dump file') + .action(failOnUnknown(function() { + pm2.clearDump(); + })); + // // Save processes to file // @@ -645,7 +669,7 @@ commander.command('resurrect') // Set pm2 to startup // commander.command('unstartup [platform]') - .description('disable and clear auto startup - [platform]=systemd,upstart,launchd,rcd') + .description('disable the pm2 startup hook') .action(function(platform) { pm2.uninstallStartup(platform, commander); }); @@ -654,7 +678,7 @@ commander.command('unstartup [platform]') // Set pm2 to startup // commander.command('startup [platform]') - .description('setup script for pm2 at boot - [platform]=systemd,upstart,launchd,rcd') + .description('enable the pm2 startup hook') .action(function(platform) { pm2.startup(platform, commander); }); @@ -891,15 +915,6 @@ commander.command('backward ') pm2.backward(pm2_name); }); -// -// Force PM2 to trigger garbage collection -// -commander.command('gc') - .description('force PM2 to trigger garbage collection') - .action(function() { - pm2.forceGc(); - }); - // // Perform a deep update of PM2 // @@ -914,18 +929,27 @@ commander.command('deepUpdate') // commander.command('serve [path] [port]') .alias('expose') + .option('--port [port]', 'specify port to listen to') .description('serve a directory over http via port') - .action(function (path, port) { - pm2.serve(path, port, commander); + .action(function (path, port, cmd) { + pm2.serve(path, port || cmd.port, commander); }); +commander.command('examples') + .description('display pm2 usage examples') + .action(() => { + console.log(cst.PREFIX_MSG + chalk.grey('pm2 usage examples:\n')); + displayExamples(); + process.exit(cst.SUCCESS_EXIT); + }) + // // Catch all // commander.command('*') .action(function() { - console.log(cst.PREFIX_MSG + '\nCommand not found'); - commander.outputHelp(); + console.log(cst.PREFIX_MSG + 'Command not found\n'); + displayUsage(); // Check if it does not forget to close fds from RPC process.exit(cst.ERROR_EXIT); }); @@ -935,7 +959,7 @@ commander.command('*') // if (process.argv.length == 2) { commander.parse(process.argv); - commander.outputHelp(); + displayUsage(); // Check if it does not forget to close fds from RPC process.exit(cst.ERROR_EXIT); } diff --git a/constants.js b/constants.js index c86f8772..127f5764 100644 --- a/constants.js +++ b/constants.js @@ -8,7 +8,6 @@ var debug = require('debug')('pm2:conf'); var p = require('path'); var util = require('util'); var chalk = require('chalk'); -var semver = require('semver'); /** * Get PM2 path structure @@ -28,7 +27,7 @@ var csts = { TEMPLATE_FOLDER : p.join(__dirname, 'lib/templates'), - APP_CONF_DEFAULT_FILE : 'ecosystem.json', + APP_CONF_DEFAULT_FILE : 'ecosystem.config.js', APP_CONF_TPL : 'ecosystem.tpl', APP_CONF_TPL_SIMPLE : 'ecosystem-simple.tpl', SAMPLE_CONF_FILE : 'sample-conf.js', @@ -55,7 +54,12 @@ var csts = { LOW_MEMORY_ENVIRONMENT : process.env.PM2_OPTIMIZE_MEMORY || false, - KEYMETRICS_ROOT_URL : process.env.KEYMETRICS_NODE || 'root.keymetrics.io', + MACHINE_NAME : process.env.INSTANCE_NAME || process.env.MACHINE_NAME, + SECRET_KEY : process.env.KEYMETRICS_SECRET || process.env.PM2_SECRET_KEY || process.env.SECRET_KEY, + PUBLIC_KEY : process.env.KEYMETRICS_PUBLIC || process.env.PM2_PUBLIC_KEY || process.env.PUBLIC_KEY, + KEYMETRICS_ROOT_URL : process.env.KEYMETRICS_NODE || process.env.ROOT_URL || process.env.INFO_NODE || 'root.keymetrics.io', + + KEYMETRICS_BANNER : '../lib/motd', KEYMETRICS_UPDATE : '../lib/motd.update', DEFAULT_MODULE_JSON : 'package.json', @@ -74,10 +78,7 @@ var csts = { // Concurrent actions when doing start/restart/reload CONCURRENT_ACTIONS : (function() { - var default_concurrent_actions = 1; - if (semver.satisfies(process.versions.node, '>= 4.0.0')) - default_concurrent_actions = 2; - var concurrent_actions = parseInt(process.env.PM2_CONCURRENT_ACTIONS) || default_concurrent_actions; + var concurrent_actions = parseInt(process.env.PM2_CONCURRENT_ACTIONS) || 2; debug('Using %d parallelism (CONCURRENT_ACTIONS)', concurrent_actions); return concurrent_actions; })(), @@ -91,7 +92,7 @@ var csts = { WORKER_INTERVAL : process.env.PM2_WORKER_INTERVAL || 30000, KILL_TIMEOUT : process.env.PM2_KILL_TIMEOUT || 1600, PM2_PROGRAMMATIC : typeof(process.env.pm_id) !== 'undefined' || process.env.PM2_PROGRAMMATIC, - PM2_LOG_DATE_FORMAT : process.env.PM2_LOG_DATE_FORMAT !== undefined ? process.env.PM2_LOG_DATE_FORMAT : 'YYYY-MM-DD HH:mm:ss' + PM2_LOG_DATE_FORMAT : process.env.PM2_LOG_DATE_FORMAT !== undefined ? process.env.PM2_LOG_DATE_FORMAT : 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' }; diff --git a/examples/misc-examples/graceful-exit.js b/examples/misc-examples/graceful-exit.js index df593d44..a35fc3e8 100644 --- a/examples/misc-examples/graceful-exit.js +++ b/examples/misc-examples/graceful-exit.js @@ -2,7 +2,7 @@ /* * Example of graceful exit * - * $ pm2 gracefulReload all + * $ pm2 reload all */ process.on('message', function(msg) { diff --git a/examples/sourcemap-auto-resolve/API.js b/examples/sourcemap-auto-resolve/API.js index ed62bdec..54413879 100644 --- a/examples/sourcemap-auto-resolve/API.js +++ b/examples/sourcemap-auto-resolve/API.js @@ -399,33 +399,6 @@ API.prototype.update = function(cb) { return false; }; -/** - * Graceful Reload an application - * - * @param {String} process_name Application Name or All - * @param {Object} opts Options - * @param {Function} cb Callback - */ -API.prototype.gracefulReload = function(process_name, opts, cb) { - var that = this; - - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Warning gracefulReload will be soon deprecated')); - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Use http://pm2.keymetrics.io/docs/usage/signals-clean-restart/ instead')); - - if (Common.isConfigFile(process_name)) - that._startJson(process_name, commander, 'softReloadProcessId'); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); - that._operate('softReloadProcessId', process_name, opts, cb); - } -}; - /** * Reload an application * diff --git a/examples/test-all-keymetrics-features/actions-fibonacci.js b/examples/test-all-keymetrics-features/actions-fibonacci.js index 3b230314..608753f6 100644 --- a/examples/test-all-keymetrics-features/actions-fibonacci.js +++ b/examples/test-all-keymetrics-features/actions-fibonacci.js @@ -47,7 +47,7 @@ function fib(n) { } -var axm = require('pmx'); +var axm = require('@pm2/io'); axm.action('load:start', function(reply) { fib(50000); diff --git a/examples/test-all-keymetrics-features/custom_action.js b/examples/test-all-keymetrics-features/custom_action.js index 5a01fc63..d11d8257 100644 --- a/examples/test-all-keymetrics-features/custom_action.js +++ b/examples/test-all-keymetrics-features/custom_action.js @@ -1,5 +1,5 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); axm.action('getEnv', function(reply) { reply(process.env); diff --git a/examples/test-all-keymetrics-features/custom_action_with_params.js b/examples/test-all-keymetrics-features/custom_action_with_params.js index 8d0b6630..023c75e7 100644 --- a/examples/test-all-keymetrics-features/custom_action_with_params.js +++ b/examples/test-all-keymetrics-features/custom_action_with_params.js @@ -1,5 +1,5 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); axm.action('refresh:db', { comment : 'Refresh the database' }, function(reply) { console.log('Refreshing'); diff --git a/examples/test-all-keymetrics-features/event.js b/examples/test-all-keymetrics-features/event.js index dca10d4f..3c092ac1 100644 --- a/examples/test-all-keymetrics-features/event.js +++ b/examples/test-all-keymetrics-features/event.js @@ -1,5 +1,5 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); setInterval(function() { diff --git a/examples/test-all-keymetrics-features/http_app.js b/examples/test-all-keymetrics-features/http_app.js index 38653c02..2e24737a 100644 --- a/examples/test-all-keymetrics-features/http_app.js +++ b/examples/test-all-keymetrics-features/http_app.js @@ -1,7 +1,7 @@ -var pmx = require('pmx').init({ http : true }); -var probe = pmx.probe(); +var io = require('@pm2/io').init({ http : true }); +var probe = io.probe(); var http = require('http'); diff --git a/examples/test-all-keymetrics-features/http_transaction.js b/examples/test-all-keymetrics-features/http_transaction.js index 87e12253..1334ea85 100644 --- a/examples/test-all-keymetrics-features/http_transaction.js +++ b/examples/test-all-keymetrics-features/http_transaction.js @@ -1,6 +1,6 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); var probe = axm.probe(); @@ -17,7 +17,7 @@ http.createServer(function(req, res) { setTimeout(function() { res.end('transaction'); }, 1000); -}).listen(9010); +}).listen(10010); setInterval(function() { request(['/user', '/bla', '/user/lol/delete', '/POST/POST'][Math.floor((Math.random() * 4))]); diff --git a/examples/test-all-keymetrics-features/pm2_probe.js b/examples/test-all-keymetrics-features/pm2_probe.js index bedab4c9..a8741ae3 100644 --- a/examples/test-all-keymetrics-features/pm2_probe.js +++ b/examples/test-all-keymetrics-features/pm2_probe.js @@ -1,16 +1,15 @@ -var pmx = require('pmx'); +var io = require('@pm2/io'); var pm2 = require('../..'); var fs = require('fs'); var path = require('path'); -var conf = pmx.initModule({ +var conf = io.initModule({ comment : 'This module monitors PM2', errors : true, latency : false, versioning : false, show_module_meta : false, module_type : 'database', - pid : pmx.getPID(path.join(process.env.HOME, '.pm2', 'pm2.pid')), widget : { theme : ['#111111', '#1B2228', '#807C7C', '#807C7C'], @@ -18,7 +17,7 @@ var conf = pmx.initModule({ } }); -var probe = pmx.probe(); +var probe = io.probe(); var pm2_procs = 0; diff --git a/examples/test-all-keymetrics-features/probes.js b/examples/test-all-keymetrics-features/probes.js index 51dc1f56..8cefdac2 100644 --- a/examples/test-all-keymetrics-features/probes.js +++ b/examples/test-all-keymetrics-features/probes.js @@ -1,6 +1,6 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); var probe = axm.probe(); @@ -84,7 +84,7 @@ setInterval(function() { }, 1500); -axm.catchAll(); +//axm.catchAll(); axm.action('throw error', function(reply) { setTimeout(function() { diff --git a/examples/test-all-keymetrics-features/process-transpose.js b/examples/test-all-keymetrics-features/process-transpose.js index a0f078da..d02ed7e8 100644 --- a/examples/test-all-keymetrics-features/process-transpose.js +++ b/examples/test-all-keymetrics-features/process-transpose.js @@ -1,21 +1,21 @@ -var Probe = require('pmx').probe(); +var Probe = require('@pm2/io').probe(); var counter = 0; -var metric = Probe.transpose({ - name : 'data-flow', - data : function() { - return { - a : { - b : { - data : 'textflow', - array : [ 'yes', 'it', 'is' ] - } - } - } - } -}); +// var metric = Probe.transpose({ +// name : 'data-flow', +// data : function() { +// return { +// a : { +// b : { +// data : 'textflow', +// array : [ 'yes', 'it', 'is' ] +// } +// } +// } +// } +// }); setInterval(function() { }, 100); diff --git a/examples/test-all-keymetrics-features/scoped-actions.js b/examples/test-all-keymetrics-features/scoped-actions.js index 5a096dd4..427dcc8c 100644 --- a/examples/test-all-keymetrics-features/scoped-actions.js +++ b/examples/test-all-keymetrics-features/scoped-actions.js @@ -1,7 +1,7 @@ -var pmx = require('pmx'); +var io = require('@pm2/io'); -pmx.scopedAction('simple test', function(data, emitter) { +io.scopedAction('simple test', function(data, emitter) { var i = setInterval(function() { emitter.send('output-stream'); }, 100); @@ -13,7 +13,7 @@ pmx.scopedAction('simple test', function(data, emitter) { }, 3000); }); -pmx.scopedAction('throwing error', function(data, emitter) { +io.scopedAction('throwing error', function(data, emitter) { var i = setInterval(function() { emitter.send('output-stream'); }, 100); diff --git a/examples/test-all-keymetrics-features/throw.js b/examples/test-all-keymetrics-features/throw.js index 7351209c..1c9dfd1f 100644 --- a/examples/test-all-keymetrics-features/throw.js +++ b/examples/test-all-keymetrics-features/throw.js @@ -1,7 +1,5 @@ -var axm = require('pmx'); - -axm.catchAll(); +var axm = require('@pm2/io'); setTimeout(function() { console.log('log message from echo auto kill'); diff --git a/lib/API.js b/lib/API.js index 59bc227f..fa147e6b 100644 --- a/lib/API.js +++ b/lib/API.js @@ -3,20 +3,21 @@ * Use of this source code is governed by a license that * can be found in the LICENSE file. */ +'use strict'; -var commander = require('commander'); -var fs = require('fs'); -var path = require('path'); -var async = require('async'); -var debug = require('debug')('pm2:cli'); -var util = require('util'); -var chalk = require('chalk'); -var fclone = require('fclone'); +const commander = require('commander'); +const fs = require('fs'); +const path = require('path'); +const async = require('async'); +const debug = require('debug')('pm2:cli'); +const util = require('util'); +const chalk = require('chalk'); +const fclone = require('fclone'); var conf = require('../constants.js'); var Client = require('./Client'); var Common = require('./Common'); -var KMDaemon = require('./Interactor/InteractorDaemonizer'); +var KMDaemon = require('@pm2/agent/src/InteractorClient'); var Config = require('./tools/Config'); var Modularizer = require('./API/Modules/Modularizer.js'); var path_structure = require('../paths.js'); @@ -41,89 +42,1605 @@ var IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment vari * @param {String} [opts.pm2_home=[]] pm2 directory for log, pids, socket files * @param {Boolean} [opts.independent=false] unique PM2 instance (random pm2_home) * @param {Boolean} [opts.daemon_mode=true] should be called in the same process or not - * @param {String} [opts.public_key=null] keymetrics bucket public key - * @param {String} [opts.secret_key=null] keymetrics bucket secret key - * @param {String} [opts.machine_name=null] keymetrics instance name + * @param {String} [opts.public_key=null] pm2 plus bucket public key + * @param {String} [opts.secret_key=null] pm2 plus bucket secret key + * @param {String} [opts.machine_name=null] pm2 plus instance name */ -var API = module.exports = function(opts) { - if (!opts) opts = {}; - var that = this; +class API { - this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode; - this.pm2_home = conf.PM2_ROOT_PATH; - this.public_key = process.env.KEYMETRICS_SECRET || opts.public_key || null; - this.secret_key = process.env.KEYMETRICS_PUBLIC || opts.secret_key || null; - this.machine_name = process.env.INSTANCE_NAME || opts.machine_name || null + constructor (opts) { + if (!opts) opts = {}; + var that = this; - /** - * CWD resolution - */ - this.cwd = process.cwd(); - if (opts.cwd) { - this.cwd = path.resolve(opts.cwd); + this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode; + this.pm2_home = conf.PM2_ROOT_PATH; + this.public_key = conf.PUBLIC_KEY || opts.public_key || null; + this.secret_key = conf.SECRET_KEY || opts.secret_key || null; + this.machine_name = conf.MACHINE_NAME || opts.machine_name || null + + /** + * CWD resolution + */ + this.cwd = process.cwd(); + if (opts.cwd) { + this.cwd = path.resolve(opts.cwd); + } + + /** + * PM2 HOME resolution + */ + if (opts.pm2_home && opts.independent == true) + throw new Error('You cannot set a pm2_home and independent instance in same time'); + + if (opts.pm2_home) { + // Override default conf file + this.pm2_home = opts.pm2_home; + conf = util._extend(conf, path_structure(this.pm2_home)); + } + else if (opts.independent == true && conf.IS_WINDOWS === false) { + // Create an unique pm2 instance + const crypto = require('crypto'); + var random_file = crypto.randomBytes(8).toString('hex'); + this.pm2_home = path.join('/tmp', random_file); + + // If we dont explicitly tell to have a daemon + // It will go as in proc + if (typeof(opts.daemon_mode) == 'undefined') + this.daemon_mode = false; + conf = util._extend(conf, path_structure(this.pm2_home)); + } + + this._conf = conf; + + if (conf.IS_WINDOWS) { + // Weird fix, may need to be dropped + // @todo windows connoisseur double check + if (process.stdout._handle && process.stdout._handle.setBlocking) + process.stdout._handle.setBlocking(true); + } + + this.Client = new Client({ + pm2_home: that.pm2_home, + conf: this._conf, + secret_key: this.secret_key, + public_key: this.public_key, + daemon_mode: this.daemon_mode, + machine_name: this.machine_name + }); + + this.gl_interact_infos = null; + this.gl_is_km_linked = false; + + try { + var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH); + pid = parseInt(pid.toString().trim()); + process.kill(pid, 0); + that.gl_is_km_linked = true; + } catch (e) { + that.gl_is_km_linked = false; + } + + // For testing purposes + if (this.secret_key && process.env.NODE_ENV == 'local_test') + that.gl_is_km_linked = true; + + KMDaemon.getInteractInfo(this._conf, function (i_err, interact) { + that.gl_interact_infos = interact; + }); + + this.gl_retry = 0; } /** - * PM2 HOME resolution + * Connect to PM2 + * Calling this command is now optional + * + * @param {Function} cb callback once pm2 is ready for commands */ - if (opts.pm2_home && opts.independent == true) - throw new Error('You cannot set a pm2_home and independent instance in same time'); + connect (noDaemon, cb) { + var that = this; + this.start_timer = new Date(); - if (opts.pm2_home) { - // Override default conf file - this.pm2_home = opts.pm2_home; - conf = util._extend(conf, path_structure(this.pm2_home)); - } - else if (opts.independent == true && conf.IS_WINDOWS === false) { - // Create an unique pm2 instance - var crypto = require('crypto'); - var random_file = crypto.randomBytes(8).toString('hex'); - this.pm2_home = path.join('/tmp', random_file); - - // If we dont explicitly tell to have a daemon - // It will go as in proc - if (typeof(opts.daemon_mode) == 'undefined') + if (typeof(cb) == 'undefined') { + cb = noDaemon; + noDaemon = false; + } else if (noDaemon === true) { + // Backward compatibility with PM2 1.x + this.Client.daemon_mode = false; this.daemon_mode = false; - conf = util._extend(conf, path_structure(this.pm2_home)); + } + + this.Client.start(function(err, meta) { + if (err) + return cb(err); + + if (meta.new_pm2_instance == false && that.daemon_mode === true) + return cb(err, meta); + + // If new pm2 instance has been popped + // Lauch all modules + Modularizer.launchAll(that, function(err_mod) { + return cb(err, meta); + }); + }); } - this._conf = conf; + /** + * Usefull when custom PM2 created with independent flag set to true + * This will cleanup the newly created instance + * by removing folder, killing PM2 and so on + * + * @param {Function} cb callback once cleanup is successfull + */ + destroy (cb) { + var exec = require('shelljs').exec; + var that = this; - if (conf.IS_WINDOWS) { - // Weird fix, may need to be dropped - // @todo windows connoisseur double check - if (process.stdout._handle && process.stdout._handle.setBlocking) - process.stdout._handle.setBlocking(true); + debug('Killing and deleting current deamon'); + + this.killDaemon(function() { + var cmd = 'rm -rf ' + that.pm2_home; + var test_path = path.join(that.pm2_home, 'module_conf.json'); + var test_path_2 = path.join(that.pm2_home, 'pm2.pid'); + + if (that.pm2_home.indexOf('.pm2') > -1) + return cb(new Error('Destroy is not a allowed method on .pm2')); + + fs.access(test_path, fs.R_OK, function(err) { + if (err) return cb(err); + debug('Deleting temporary folder %s', that.pm2_home); + exec(cmd, cb); + }); + }); } - this.Client = new Client({ - pm2_home : that.pm2_home, - conf : this._conf, - secret_key : this.secret_key, - public_key : this.public_key, - daemon_mode : this.daemon_mode, - machine_name : this.machine_name - }); + /** + * Disconnect from PM2 instance + * This will allow your software to exit by itself + * + * @param {Function} [cb] optional callback once connection closed + */ + disconnect (cb) { + var that = this; - this.gl_interact_infos = null; - this.gl_is_km_linked = false; + if (!cb) cb = function() {}; - try { - var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH); - pid = parseInt(pid.toString().trim()); - process.kill(pid, 0); - that.gl_is_km_linked = true; - } catch(e) { - that.gl_is_km_linked = false; + this.Client.close(function(err, data) { + debug('The session lasted %ds', (new Date() - that.start_timer) / 1000); + return cb(err, data); + }); + }; + + /** + * Alias on disconnect + * @param cb + */ + close (cb) { + this.disconnect(cb); } - // For testing purposes - if (this.secret_key && process.env.NODE_ENV == 'local_test') - that.gl_is_km_linked = true; + /** + * Launch modules + * + * @param {Function} cb callback once pm2 has launched modules + */ + launchModules (cb) { + Modularizer.launchAll(this, cb); + } - KMDaemon.getInteractInfo(this._conf, function(i_err, interact) { - that.gl_interact_infos = interact; - }); + /** + * Enable bus allowing to retrieve various process event + * like logs, restarts, reloads + * + * @param {Function} cb callback called with 1st param err and 2nb param the bus + */ + launchBus (cb) { + this.Client.launchBus(cb); + } + + /** + * Exit methods for API + * @param {Integer} code exit code for terminal + */ + exitCli (code) { + var that = this; + + // Do nothing if PM2 called programmatically (also in speedlist) + if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false; + + KMDaemon.disconnectRPC(function() { + that.Client.close(function() { + code = code || 0; + // Safe exits process after all streams are drained. + // file descriptor flag. + var fds = 0; + // exits process when stdout (1) and sdterr(2) are both drained. + function tryToExit() { + if ((fds & 1) && (fds & 2)) { + debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000); + process.exit(code); + } + } + + [process.stdout, process.stderr].forEach(function(std) { + var fd = std.fd; + if (!std.bufferSize) { + // bufferSize equals 0 means current stream is drained. + fds = fds | fd; + } else { + // Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`. + std.write && std.write('', function() { + fds = fds | fd; + tryToExit(); + }); + } + // Does not write anything more. + delete std.write; + }); + tryToExit(); + }); + }); + } + +//////////////////////////// +// Application management // +//////////////////////////// + + /** + * Start a file or json with configuration + * @param {Object||String} cmd script to start or json + * @param {Function} cb called when application has been started + */ + start (cmd, opts, cb) { + if (typeof(opts) == "function") { + cb = opts; + opts = {}; + } + if (!opts) + opts = {}; + + var that = this; + + if (util.isArray(opts.watch) && opts.watch.length === 0) + opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false; + + if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) + that._startJson(cmd, opts, 'restartProcessId', cb); + else { + that._startScript(cmd, opts, cb); + } + } + + /** + * Reset process counters + * + * @method resetMetaProcess + */ + reset (process_name, cb) { + var that = this; + + function processIds(ids, cb) { + async.eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) { + that.Client.executeRemote('resetMetaProcessId', id, function(err, res) { + if (err) console.error(err); + Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id); + return next(); + }); + }, function(err) { + if (err) return cb(Common.retErr(err)); + return cb ? cb(null, {success:true}) : that.speedList(); + }); + } + + if (process_name == 'all') { + that.Client.getAllProcessId(function(err, ids) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + return processIds(ids, cb); + }); + } + else if (isNaN(process_name)) { + that.Client.getProcessIdByName(process_name, function(err, ids) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + if (ids.length === 0) { + Common.printError('Unknown process name'); + return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT); + } + return processIds(ids, cb); + }); + } else { + processIds([process_name], cb); + } + } + + /** + * Update daemonized PM2 Daemon + * + * @param {Function} cb callback when pm2 has been upgraded + */ + update (cb) { + var that = this; + + Common.printOut('Be sure to have the latest version by doing `npm install pm2@latest -g` before doing this procedure.'); + + // Dump PM2 processes + that.Client.executeRemote('notifyKillPM2', {}, function() {}); + + that.getVersion(function(err, new_version) { + // If not linked to PM2 plus, and update PM2 to latest, display motd.update + if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) { + var dt = fs.readFileSync(path.join(__dirname, that._conf.KEYMETRICS_UPDATE)); + console.log(dt.toString()); + } + + that.dump(function(err) { + debug('Dumping successfull', err); + that.killDaemon(function() { + debug('------------------ Everything killed', arguments); + that.Client.launchDaemon({interactor:false}, function(err, child) { + that.Client.launchRPC(function() { + that.resurrect(function() { + Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); + Modularizer.launchAll(that, function() { + KMDaemon.launchAndInteract(that._conf, null, function(err, data, interactor_proc) { + // Interactor error can be skipped here + return cb ? cb(null, {success:true}) : that.speedList(); + }); + }); + }); + }); + }); + }); + }); + }); + + return false; + } + + /** + * Reload an application + * + * @param {String} process_name Application Name or All + * @param {Object} opts Options + * @param {Function} cb Callback + */ + reload (process_name, opts, cb) { + var that = this; + + if (typeof(opts) == "function") { + cb = opts; + opts = {}; + } + + var delay = Common.lockReload(); + + if (delay > 0 && opts.force != true) { + Common.printError(conf.PREFIX_MSG_ERR + 'Reload already in progress, please try again in ' + Math.floor((conf.RELOAD_LOCK_TIMEOUT - delay) / 1000) + ' seconds or use --force'); + return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT); + } + + if (Common.isConfigFile(process_name)) + that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) { + Common.unlockReload(); + if (err) + return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); + return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);; + }); + else { + if (opts && !opts.updateEnv) + Common.printOut(IMMUTABLE_MSG); + + that._operate('reloadProcessId', process_name, opts, function(err, apps) { + Common.unlockReload(); + + if (err) + return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); + return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);; + }); + } + } + + /** + * Restart process + * + * @param {String} cmd Application Name / Process id / JSON application file / 'all' + * @param {Object} opts Extra options to be updated + * @param {Function} cb Callback + */ + restart (cmd, opts, cb) { + if (typeof(opts) == "function") { + cb = opts; + opts = {}; + } + var that = this; + + if (typeof(cmd) === 'number') + cmd = cmd.toString(); + + if (cmd == "-") { + // Restart from PIPED JSON + process.stdin.resume(); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function (param) { + process.stdin.pause(); + that.actionFromJson('restartProcessId', param, opts, 'pipe', cb); + }); + } + else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object') + that._startJson(cmd, opts, 'restartProcessId', cb); + else { + if (opts && !opts.updateEnv) + Common.printOut(IMMUTABLE_MSG); + that._operate('restartProcessId', cmd, opts, cb); + } + } + + /** + * Delete process + * + * @param {String} process_name Application Name / Process id / Application file / 'all' + * @param {Function} cb Callback + */ + delete (process_name, jsonVia, cb) { + var that = this; + + if (typeof(jsonVia) === "function") { + cb = jsonVia; + jsonVia = null; + } + if (typeof(process_name) === "number") { + process_name = process_name.toString(); + } + + if (jsonVia == 'pipe') + return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', cb); + if (Common.isConfigFile(process_name)) + return that.actionFromJson('deleteProcessId', process_name, commander, 'file', cb); + else + that._operate('deleteProcessId', process_name, cb); + } + + /** + * Stop process + * + * @param {String} process_name Application Name / Process id / Application file / 'all' + * @param {Function} cb Callback + */ + stop (process_name, cb) { + var that = this; + + if (typeof(process_name) === 'number') + process_name = process_name.toString(); + + if (process_name == "-") { + process.stdin.resume(); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function (param) { + process.stdin.pause(); + that.actionFromJson('stopProcessId', param, commander, 'pipe', cb); + }); + } + else if (Common.isConfigFile(process_name)) + that.actionFromJson('stopProcessId', process_name, commander, 'file', cb); + else + that._operate('stopProcessId', process_name, cb); + } + + /** + * Get list of all processes managed + * + * @param {Function} cb Callback + */ + list (opts, cb) { + var that = this; + + if (typeof(opts) == 'function') { + cb = opts; + opts = null; + } + + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + + if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) { + var moment = require('moment'); + function show() { + process.stdout.write('\\033[2J'); + process.stdout.write('\\033[0f'); + console.log('Last refresh: ', moment().format('LTS')); + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + UX.dispAsTable(list, null); + }); + } + + show(); + setInterval(show, 900); + return false; + } + + return cb ? cb(null, list) : that.speedList(null, list); + }); + } + + /** + * Kill Daemon + * + * @param {Function} cb Callback + */ + killDaemon (cb) { + var that = this; + + var semver = require('semver'); + Common.printOut(conf.PREFIX_MSG + 'Stopping PM2...'); + + that.Client.executeRemote('notifyKillPM2', {}, function() {}); + + that.killAllModules(function() { + that._operate('deleteProcessId', 'all', function(err, list) { + Common.printOut(conf.PREFIX_MSG + 'All processes have been stopped and deleted'); + process.env.PM2_SILENT = 'false'; + + that.killInteract(function(err, data) { + that.Client.killDaemon(function(err, res) { + if (err) Common.printError(err); + Common.printOut(conf.PREFIX_MSG + 'PM2 stopped'); + return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT); + }); + }); + }); + }); + } + + kill (cb) { + this.killDaemon(cb); + } + + ///////////////////// + // Private methods // + ///////////////////// + + /** + * Method to START / RESTART a script + * + * @private + * @param {string} script script name (will be resolved according to location) + */ + _startScript (script, opts, cb) { + if (typeof opts == "function") { + cb = opts; + opts = {}; + } + var that = this; + + var app_conf = Config.transCMDToConf(opts); + var appConf = {}; + + if (!!opts.executeCommand) + app_conf.exec_mode = 'fork'; + else if (opts.instances !== undefined) + app_conf.exec_mode = 'cluster'; + else + app_conf.exec_mode = 'fork'; + + if (typeof app_conf.name == 'function') + delete app_conf.name; + + delete app_conf.args; + + var argsIndex; + + if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) + app_conf.args = opts.rawArgs.slice(argsIndex + 1); + else if (opts.scriptArgs) + app_conf.args = opts.scriptArgs; + + app_conf.script = script; + + if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) + return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT); + + app_conf = appConf[0]; + + /** + * If -w option, write configuration to configuration.json file + */ + if (appConf.write) { + var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-pm2.json'); + Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path)); + // pretty JSON + try { + fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2)); + } catch (e) { + console.error(e.stack || e); + } + } + + /** + * If start start/restart application + */ + function restartExistingProcessName(cb) { + if (!isNaN(script) || + (typeof script === 'string' && script.indexOf('/') != -1) || + (typeof script === 'string' && path.extname(script) !== '')) + return cb(null); + + if (script !== 'all') { + that.Client.getProcessIdByName(script, function(err, ids) { + if (err && cb) return cb(err); + if (ids.length > 0) { + that._operate('restartProcessId', script, opts, function(err, list) { + if (err) return cb(err); + Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); + return cb(true, list); + }); + } + else return cb(null); + }); + } + else { + that._operate('restartProcessId', 'all', function(err, list) { + if (err) return cb(err); + Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); + return cb(true, list); + }); + } + } + + function restartExistingProcessId(cb) { + if (isNaN(script)) return cb(null); + + that._operate('restartProcessId', script, opts, function(err, list) { + if (err) return cb(err); + Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); + return cb(true, list); + }); + } + + /** + * Restart a process with the same full path + * Or start it + */ + function restartExistingProcessPath(cb) { + that.Client.executeRemote('getMonitorData', {}, function(err, procs) { + if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT); + + var full_path = path.resolve(that.cwd, script); + var managed_script = null; + + procs.forEach(function(proc) { + if (proc.pm2_env.pm_exec_path == full_path && + proc.pm2_env.name == app_conf.name) + managed_script = proc; + }); + + if (managed_script && + (managed_script.pm2_env.status == conf.STOPPED_STATUS || + managed_script.pm2_env.status == conf.STOPPING_STATUS || + managed_script.pm2_env.status == conf.ERRORED_STATUS)) { + // Restart process if stopped + var app_name = managed_script.pm2_env.name; + + that._operate('restartProcessId', app_name, opts, function(err, list) { + if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT); + Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); + return cb(true, list); + }); + return false; + } + else if (managed_script && !opts.force) { + Common.printError(conf.PREFIX_MSG_ERR + 'Script already launched, add -f option to force re-execution'); + return cb(new Error('Script already launched')); + } + + var resolved_paths = null; + + try { + resolved_paths = Common.resolveAppAttributes({ + cwd : that.cwd, + pm2_home : that.pm2_home + }, app_conf); + } catch(e) { + Common.printError(e); + return cb(Common.retErr(e)); + } + + Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')', + resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances); + + if (!resolved_paths.env) resolved_paths.env = {}; + + // Set PM2 HOME in case of child process using PM2 API + resolved_paths.env['PM2_HOME'] = that.pm2_home; + + var additional_env = Modularizer.getAdditionalConf(resolved_paths.name); + util._extend(resolved_paths.env, additional_env); + + // Is KM linked? + resolved_paths.km_link = that.gl_is_km_linked; + + that.Client.executeRemote('prepare', resolved_paths, function(err, data) { + if (err) { + Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err); + return cb(Common.retErr(err)); + } + + Common.printOut(conf.PREFIX_MSG + 'Done.'); + return cb(true, data); + }); + return false; + }); + } + + async.series([ + restartExistingProcessName, + restartExistingProcessId, + restartExistingProcessPath + ], function(err, data) { + + if (err instanceof Error) + return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); + + var ret = {}; + data.forEach(function(_dt) { + if (_dt !== undefined) + ret = _dt; + }); + + return cb ? cb(null, ret) : that.speedList(); + }); + } + + /** + * Method to start/restart/reload processes from a JSON file + * It will start app not started + * Can receive only option to skip applications + * + * @private + */ + _startJson (file, opts, action, pipe, cb) { + var config = {}; + var appConf = {}; + var deployConf = {}; + var apps_info = []; + var that = this; + + if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') { + cb = pipe; + } + + if (typeof(file) === 'object') { + config = file; + } else if (pipe === 'pipe') { + config = Common.parseConfig(file, 'pipe'); + } else { + var data = null; + + var isAbsolute = path.isAbsolute(file) + var file_path = isAbsolute ? file : path.join(that.cwd, file); + + debug('Resolved filepath %s', file_path); + + try { + data = fs.readFileSync(file_path); + } catch(e) { + Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found'); + return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); + } + + try { + config = Common.parseConfig(data, file); + } catch(e) { + Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated'); + console.error(e); + return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); + } + } + + if (config.deploy) + deployConf = config.deploy; + + if (config.apps) + appConf = config.apps; + else if (config.pm2) + appConf = config.pm2; + else + appConf = config; + + if (!Array.isArray(appConf)) + appConf = [appConf]; //convert to array + + if ((appConf = Common.verifyConfs(appConf)) instanceof Error) + return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT); + + process.env.PM2_JSON_PROCESSING = true; + + // Get App list + var apps_name = []; + var proc_list = {}; + + // Here we pick only the field we want from the CLI when starting a JSON + appConf.forEach(function(app) { + if (!app.env) { app.env = {}; } + app.env.io = app.io; + // --only + if (opts.only && opts.only != app.name) + return false; + // --watch + if (!app.watch && opts.watch && opts.watch === true) + app.watch = true; + // --ignore-watch + if (!app.ignore_watch && opts.ignore_watch) + app.ignore_watch = opts.ignore_watch; + // --instances + if (opts.instances && typeof(opts.instances) === 'number') + app.instances = opts.instances; + // --uid + if (opts.uid) + app.uid = opts.uid; + // --gid + if (opts.gid) + app.gid = opts.gid; + // Specific + if (app.append_env_to_name && opts.env) + app.name += ('-' + opts.env); + app.username = Common.getCurrentUsername(); + apps_name.push(app.name); + }); + + that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + + /** + * Uniquify in memory process list + */ + raw_proc_list.forEach(function(proc) { + proc_list[proc.name] = proc; + }); + + /** + * Auto detect application already started + * and act on them depending on action + */ + async.eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) { + + // Skip app name (--only option) + if (apps_name.indexOf(proc_name) == -1) + return next(); + + if (!(action == 'reloadProcessId' || + action == 'softReloadProcessId' || + action == 'restartProcessId')) + throw new Error('Wrong action called'); + + var apps = appConf.filter(function(app) { + return app.name == proc_name; + }); + + var envs = apps.map(function(app){ + // Binds env_diff to env and returns it. + return Common.mergeEnvironmentVariables(app, opts.env, deployConf); + }); + + // Assigns own enumerable properties of all + // Notice: if people use the same name in different apps, + // duplicated envs will be overrode by the last one + var env = envs.reduce(function(e1, e2){ + return util._extend(e1, e2); + }); + + // When we are processing JSON, allow to keep the new env by default + env.updateEnv = true; + + // Pass `env` option + that._operate(action, proc_name, env, function(err, ret) { + if (err) Common.printError(err); + + // For return + apps_info = apps_info.concat(ret); + + that.Client.notifyGod(action, proc_name); + // And Remove from array to spy + apps_name.splice(apps_name.indexOf(proc_name), 1); + return next(); + }); + + }, function(err) { + if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + if (apps_name.length > 0 && action != 'start') + Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', ')); + // Start missing apps + return startApps(apps_name, function(err, apps) { + apps_info = apps_info.concat(apps); + return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0); + }); + }); + return false; + }); + + function startApps(app_name_to_start, cb) { + var apps_to_start = []; + var apps_started = []; + + appConf.forEach(function(app, i) { + if (app_name_to_start.indexOf(app.name) != -1) { + apps_to_start.push(appConf[i]); + } + }); + + async.eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) { + if (opts.cwd) + app.cwd = opts.cwd; + if (opts.force_name) + app.name = opts.force_name; + if (opts.started_as_module) + app.pmx_module = true; + + var resolved_paths = null; + + // hardcode script name to use `serve` feature inside a process file + if (app.script === 'serve') { + app.script = path.resolve(__dirname, 'API', 'Serve.js') + } + + try { + resolved_paths = Common.resolveAppAttributes({ + cwd : that.cwd, + pm2_home : that.pm2_home + }, app); + } catch (e) { + return next(); + } + + if (!resolved_paths.env) resolved_paths.env = {}; + + // Set PM2 HOME in case of child process using PM2 API + resolved_paths.env['PM2_HOME'] = that.pm2_home; + + var additional_env = Modularizer.getAdditionalConf(resolved_paths.name); + util._extend(resolved_paths.env, additional_env); + + resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf); + + delete resolved_paths.env.current_conf; + + // Is KM linked? + resolved_paths.km_link = that.gl_is_km_linked; + + that.Client.executeRemote('prepare', resolved_paths, function(err, data) { + if (err) { + Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err); + return next(); + } + if (data.length === 0) { + Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data); + return next(); + } + + Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length); + apps_started = apps_started.concat(data); + next(); + }); + + }, function(err) { + return cb ? cb(err || null, apps_started) : that.speedList(); + }); + return false; + } + } + + /** + * Apply a RPC method on the json file + * @private + * @method actionFromJson + * @param {string} action RPC Method + * @param {object} options + * @param {string|object} file file + * @param {string} jsonVia action type (=only 'pipe' ?) + * @param {Function} + */ + actionFromJson (action, file, opts, jsonVia, cb) { + var appConf = {}; + var ret_processes = []; + var that = this; + + //accept programmatic calls + if (typeof file == 'object') { + cb = typeof jsonVia == 'function' ? jsonVia : cb; + appConf = file; + } + else if (jsonVia == 'file') { + var data = null; + + try { + data = fs.readFileSync(file); + } catch(e) { + Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found'); + return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); + } + + try { + appConf = Common.parseConfig(data, file); + } catch(e) { + Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated'); + console.error(e); + return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); + } + } else if (jsonVia == 'pipe') { + appConf = Common.parseConfig(file, 'pipe'); + } else { + Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe'); + return that.exitCli(conf.ERROR_EXIT); + } + + // Backward compatibility + if (appConf.apps) + appConf = appConf.apps; + + if (!Array.isArray(appConf)) + appConf = [appConf]; + + if ((appConf = Common.verifyConfs(appConf)) instanceof Error) + return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT); + + async.eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) { + var name = ''; + var new_env; + + if (!proc.name) + name = path.basename(proc.script); + else + name = proc.name; + + if (opts.only && opts.only != name) + return process.nextTick(next1); + + if (opts && opts.env) + new_env = Common.mergeEnvironmentVariables(proc, opts.env); + else + new_env = Common.mergeEnvironmentVariables(proc); + + that.Client.getProcessIdByName(name, function(err, ids) { + if (err) { + Common.printError(err); + return next1(); + } + if (!ids) return next1(); + + async.eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) { + var opts = {}; + + //stopProcessId could accept options to? + if (action == 'restartProcessId') { + opts = {id : id, env : new_env}; + } else { + opts = id; + } + + that.Client.executeRemote(action, opts, function(err, res) { + ret_processes.push(res); + if (err) { + Common.printError(err); + return next2(); + } + + if (action == 'restartProcessId') { + that.Client.notifyGod('restart', id); + } else if (action == 'deleteProcessId') { + that.Client.notifyGod('delete', id); + } else if (action == 'stopProcessId') { + that.Client.notifyGod('stop', id); + } + + Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', name, id); + return next2(); + }); + }, function(err) { + return next1(null, ret_processes); + }); + }); + }, function(err) { + if (cb) return cb(null, ret_processes); + else return that.speedList(); + }); + } + + + /** + * Main function to operate with PM2 daemon + * + * @param {String} action_name Name of action (restartProcessId, deleteProcessId, stopProcessId) + * @param {String} process_name can be 'all', a id integer or process name + * @param {Object} envs object with CLI options / environment + */ + _operate (action_name, process_name, envs, cb) { + var that = this; + var update_env = false; + var ret = []; + + // Make sure all options exist + if (!envs) + envs = {}; + + if (typeof(envs) == 'function'){ + cb = envs; + envs = {}; + } + + // Set via env.update (JSON processing) + if (envs.updateEnv === true) + update_env = true; + + var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS; + + if (!process.env.PM2_JSON_PROCESSING || envs.commands) { + envs = that._handleAttributeUpdate(envs); + } + + /** + * Set current updated configuration if not passed + */ + if (!envs.current_conf) { + var _conf = fclone(envs); + envs = { + current_conf : _conf + } + + // Is KM linked? + envs.current_conf.km_link = that.gl_is_km_linked; + } + + /** + * Operate action on specific process id + */ + function processIds(ids, cb) { + Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids); + + if (action_name == 'deleteProcessId') + concurrent_actions = 10; + + async.eachLimit(ids, concurrent_actions, function(id, next) { + var opts; + + // These functions need extra param to be passed + if (action_name == 'restartProcessId' || + action_name == 'reloadProcessId' || + action_name == 'softReloadProcessId') { + var new_env = {}; + + if (update_env === true) { + if (conf.PM2_PROGRAMMATIC == true) + new_env = Common.safeExtend({}, process.env); + else + new_env = util._extend({}, process.env); + + Object.keys(envs).forEach(function(k) { + new_env[k] = envs[k]; + }); + } + else { + new_env = envs; + } + + opts = { + id : id, + env : new_env + }; + } + else { + opts = id; + } + + that.Client.executeRemote(action_name, opts, function(err, res) { + if (err) { + Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id); + return next('Process not found'); + } + + if (action_name == 'restartProcessId') { + that.Client.notifyGod('restart', id); + } else if (action_name == 'deleteProcessId') { + that.Client.notifyGod('delete', id); + } else if (action_name == 'stopProcessId') { + that.Client.notifyGod('stop', id); + } else if (action_name == 'reloadProcessId') { + that.Client.notifyGod('reload', id); + } else if (action_name == 'softReloadProcessId') { + that.Client.notifyGod('graceful reload', id); + } + + if (!Array.isArray(res)) + res = [res]; + + // Filter return + res.forEach(function(proc) { + Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id); + + if (!proc.pm2_env) return false; + + ret.push({ + name : proc.pm2_env.name, + pm_id : proc.pm2_env.pm_id, + status : proc.pm2_env.status, + restart_time : proc.pm2_env.restart_time, + pm2_env : { + name : proc.pm2_env.name, + pm_id : proc.pm2_env.pm_id, + status : proc.pm2_env.status, + restart_time : proc.pm2_env.restart_time, + env : proc.pm2_env.env + } + }); + }); + + return next(); + }); + }, function(err) { + if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + return cb ? cb(null, ret) : that.speedList(); + }); + } + + if (process_name == 'all') { + that.Client.getAllProcessId(function(err, ids) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + if (!ids || ids.length === 0) { + Common.printError(conf.PREFIX_MSG_WARNING + 'No process found'); + return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); + } + + return processIds(ids, cb); + }); + } + // operate using regex + else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') { + var regex = new RegExp(process_name.replace(/\//g, '')); + + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError('Error retrieving process list: ' + err); + return cb(err); + } + var found_proc = []; + list.forEach(function(proc) { + if (regex.test(proc.pm2_env.name)) { + found_proc.push(proc.pm_id); + } + }); + + if (found_proc.length === 0) { + Common.printError(conf.PREFIX_MSG_WARNING + 'No process found'); + return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); + } + + return processIds(found_proc, cb); + }); + } + else if (isNaN(process_name)) { + /** + * We can not stop or delete a module but we can restart it + * to refresh configuration variable + */ + var allow_module_restart = action_name == 'restartProcessId' ? true : false; + + that.Client.getProcessIdByName(process_name, allow_module_restart, function(err, ids) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + if (!ids || ids.length === 0) { + Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', process_name); + return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); + } + + /** + * Determine if the process to restart is a module + * if yes load configuration variables and merge with the current environment + */ + var additional_env = Modularizer.getAdditionalConf(process_name); + util._extend(envs, additional_env); + + return processIds(ids, cb); + }); + } else { + // Check if application name as number is an app name + that.Client.getProcessIdByName(process_name, function(err, ids) { + if (ids.length > 0) + return processIds(ids, cb); + // Else operate on pm id + return processIds([process_name], cb); + }); + } + } + + /** + * Converts CamelCase Commander.js arguments + * to Underscore + * (nodeArgs -> node_args) + */ + _handleAttributeUpdate (opts) { + var conf = Config.transCMDToConf(opts); + var that = this; + + if (typeof(conf.name) != 'string') + delete conf.name; + + var argsIndex = 0; + if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) { + conf.args = opts.rawArgs.slice(argsIndex + 1); + } + + var appConf = Common.verifyConfs(conf)[0]; + + if (appConf instanceof Error) { + Common.printError('Error while transforming CamelCase args to underscore'); + return appConf; + } + + if (argsIndex == -1) + delete appConf.args; + if (appConf.name == 'undefined') + delete appConf.name; + + delete appConf.exec_mode; + + if (util.isArray(appConf.watch) && appConf.watch.length === 0) { + if (!~opts.rawArgs.indexOf('--watch')) + delete appConf.watch + } + + // Options set via environment variables + if (process.env.PM2_DEEP_MONITORING) + appConf.deep_monitoring = true; + + // Force deletion of defaults values set by commander + // to avoid overriding specified configuration by user + if (appConf.treekill === true) + delete appConf.treekill; + if (appConf.pmx === true) + delete appConf.pmx; + if (appConf.vizion === true) + delete appConf.vizion; + if (appConf.automation === true) + delete appConf.automation; + if (appConf.autorestart === true) + delete appConf.autorestart; + + return appConf; + } + + getProcessIdByName (name, cb) { + var that = this; + + this.Client.getProcessIdByName(name, function(err, id) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + console.log(id); + return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT); + }); + } + + /** + * Description + * @method jlist + * @param {} debug + * @return + */ + jlist (debug) { + var that = this; + + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError(err); + that.exitCli(conf.ERROR_EXIT); + } + + if (debug) { + process.stdout.write(util.inspect(list, false, null, false)); + } + else { + process.stdout.write(JSON.stringify(list)); + } + + that.exitCli(conf.SUCCESS_EXIT); + + }); + } + + /** + * Description + * @method speedList + * @return + */ + speedList (code, list) { + var that = this; + + // Do nothing if PM2 called programmatically and not called from CLI (also in exitCli) + if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false; + + if (list) { + return doList(null, list) + } + + that.Client.executeRemote('getMonitorData', {}, doList); + + function doList(err, list) { + if (err) { + if (gl_retry == 0) { + gl_retry += 1; + return setTimeout(that.speedList.bind(that), 1400); + } + console.error('Error retrieving process list: %s.\nA process seems to be on infinite loop, retry in 5 seconds',err); + return that.exitCli(conf.ERROR_EXIT); + } + if (process.stdout.isTTY === false) { + UX.miniDisplay(list); + } + else if (commander.miniList && !commander.silent) + UX.miniDisplay(list); + else if (!commander.silent) { + if (that.gl_interact_infos) { + Common.printOut('%s Agent Online | Access: %s | Server: %s | Transport %s', + chalk.green.bold('⇆'), + chalk.bold('https://app.pm2.io/#/r/' + that.gl_interact_infos.public_key), + chalk.bold(that.gl_interact_infos.machine_name), + that.gl_interact_infos.transporters); + } + UX.dispAsTable(list, commander); + Common.printOut(chalk.white.italic(' Use `pm2 show ` to get more details about an app')); + } + + if (that.Client.daemon_mode == false) { + Common.printOut('[--no-daemon] Continue to stream logs'); + Common.printOut('[--no-daemon] Exit on target PM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString()); + global._auto_exit = true; + return that.streamLogs('all', 0, false, 'HH:mm:ss', false); + } + else if (commander.attach === true) { + return that.streamLogs('all', 0, false, null, false); + } + else { + return that.exitCli(code ? code : conf.SUCCESS_EXIT); + } + } + } + + /** + * Scale up/down a process + * @method scale + */ + scale (app_name, number, cb) { + var that = this; + + function addProcs(proc, value, cb) { + (function ex(proc, number) { + if (number-- === 0) return cb(); + Common.printOut(conf.PREFIX_MSG + 'Scaling up application'); + that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number)); + })(proc, number); + } + + function rmProcs(procs, value, cb) { + var i = 0; + + (function ex(procs, number) { + if (number++ === 0) return cb(); + that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number)); + })(procs, number); + } + + function end() { + return cb ? cb(null, {success:true}) : that.speedList(); + } + + this.Client.getProcessByName(app_name, function(err, procs) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + + if (!procs || procs.length === 0) { + Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name); + return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT); + } + + var proc_number = procs.length; + + if (typeof(number) === 'string' && number.indexOf('+') >= 0) { + number = parseInt(number, 10); + return addProcs(procs[0], number, end); + } + else if (typeof(number) === 'string' && number.indexOf('-') >= 0) { + number = parseInt(number, 10); + return rmProcs(procs[0], number, end); + } + else { + number = parseInt(number, 10); + number = number - proc_number; + + if (number < 0) + return rmProcs(procs, number, end); + else if (number > 0) + return addProcs(procs[0], number, end); + else { + Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do'); + return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT); + } + } + }); + } + + /** + * Description + * @method describeProcess + * @param {} pm2_id + * @return + */ + describe (pm2_id, cb) { + var that = this; + + var found_proc = []; + + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError('Error retrieving process list: ' + err); + that.exitCli(conf.ERROR_EXIT); + } + + list.forEach(function(proc) { + if ((!isNaN(pm2_id) && proc.pm_id == pm2_id) || + (typeof(pm2_id) === 'string' && proc.name == pm2_id)) { + found_proc.push(proc); + } + }); + + if (found_proc.length === 0) { + Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\'t exist', pm2_id); + return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT); + } + + if (!cb) { + found_proc.forEach(function(proc) { + UX.describeTable(proc); + }); + } + + return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT); + }); + } + + /** + * API method to perform a deep update of PM2 + * @method deepUpdate + */ + deepUpdate (cb) { + var that = this; + + Common.printOut(conf.PREFIX_MSG + 'Updating PM2...'); + + var exec = require('shelljs').exec; + var child = exec("npm i -g pm2@latest; pm2 update", {async : true}); + + child.stdout.on('end', function() { + Common.printOut(conf.PREFIX_MSG + 'PM2 successfully updated'); + cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT); + }); + } }; @@ -135,1553 +1652,12 @@ require('./API/Extra.js')(API); require('./API/Interaction.js')(API); require('./API/Deploy.js')(API); require('./API/Modules/Modules.js')(API); -require('./API/Keymetrics/cli-api.js')(API); +require('./API/PM2/PM2IO.js')(API); require('./API/Configuration.js')(API); require('./API/Version.js')(API); require('./API/Startup.js')(API); require('./API/LogManagement.js')(API); require('./API/Containerizer.js')(API); -/** - * Connect to PM2 - * Calling this command is now optional - * - * @param {Function} cb callback once pm2 is ready for commands - */ -API.prototype.connect = function(noDaemon, cb) { - var that = this; - this.start_timer = new Date(); - if (typeof(cb) == 'undefined') { - cb = noDaemon; - noDaemon = false; - } else if (noDaemon === true) { - // Backward compatibility with PM2 1.x - this.Client.daemon_mode = false; - this.daemon_mode = false; - } - - this.Client.start(function(err, meta) { - if (err) - return cb(err); - - if (meta.new_pm2_instance == false && that.daemon_mode === true) - return cb(err, meta); - - // If new pm2 instance has been popped - // Lauch all modules - Modularizer.launchAll(that, function(err_mod) { - return cb(err, meta); - }); - }); -} - -/** - * Usefull when custom PM2 created with independent flag set to true - * This will cleanup the newly created instance - * by removing folder, killing PM2 and so on - * - * @param {Function} cb callback once cleanup is successfull - */ -API.prototype.destroy = function(cb) { - var exec = require('shelljs').exec; - var that = this; - - debug('Killing and deleting current deamon'); - - this.killDaemon(function() { - var cmd = 'rm -rf ' + that.pm2_home; - var test_path = path.join(that.pm2_home, 'module_conf.json'); - var test_path_2 = path.join(that.pm2_home, 'pm2.pid'); - - if (that.pm2_home.indexOf('.pm2') > -1) - return cb(new Error('Destroy is not a allowed method on .pm2')); - - if (fs.accessSync) { - fs.access(test_path, fs.R_OK, function(err) { - if (err) return cb(err); - debug('Deleting temporary folder %s', that.pm2_home); - exec(cmd, cb); - }); - return false; - } - - // Support for Node 0.10 - fs.exists(test_path, function(exist) { - if (exist) { - debug('Deleting temporary folder %s', that.pm2_home); - exec(cmd, cb); - } - return cb(null); - }); - }); -}; - -/** - * Disconnect from PM2 instance - * This will allow your software to exit by itself - * - * @param {Function} [cb] optional callback once connection closed - */ -API.prototype.disconnect = API.prototype.close = function(cb) { - var that = this; - - if (!cb) cb = function() {}; - - this.Client.close(function(err, data) { - debug('The session lasted %ds', (new Date() - that.start_timer) / 1000); - return cb(err, data); - }); -}; - -/** - * Launch modules - * - * @param {Function} cb callback once pm2 has launched modules - */ -API.prototype.launchModules = function(cb) { - Modularizer.launchAll(this, cb); -}; - -/** - * Enable bus allowing to retrieve various process event - * like logs, restarts, reloads - * - * @param {Function} cb callback called with 1st param err and 2nb param the bus - */ -API.prototype.launchBus = function(cb) { - this.Client.launchBus(cb); -}; - -/** - * Exit methods for API - * @param {Integer} code exit code for terminal - */ -API.prototype.exitCli = function(code) { - var that = this; - - // Do nothing if PM2 called programmatically (also in speedlist) - if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false; - - KMDaemon.disconnectRPC(function() { - that.Client.close(function() { - code = code || 0; - // Safe exits process after all streams are drained. - // file descriptor flag. - var fds = 0; - // exits process when stdout (1) and sdterr(2) are both drained. - function tryToExit() { - if ((fds & 1) && (fds & 2)) { - debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000); - process.exit(code); - } - } - - [process.stdout, process.stderr].forEach(function(std) { - var fd = std.fd; - if (!std.bufferSize) { - // bufferSize equals 0 means current stream is drained. - fds = fds | fd; - } else { - // Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`. - std.write && std.write('', function() { - fds = fds | fd; - tryToExit(); - }); - } - // Does not write anything more. - delete std.write; - }); - tryToExit(); - }); - }); -}; - -//////////////////////////// -// Application management // -//////////////////////////// - -/** - * Start a file or json with configuration - * @param {Object||String} cmd script to start or json - * @param {Function} cb called when application has been started - */ -API.prototype.start = function(cmd, opts, cb) { - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - if (!opts) - opts = {}; - - var that = this; - - if (util.isArray(opts.watch) && opts.watch.length === 0) - opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false; - - if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) - that._startJson(cmd, opts, 'restartProcessId', cb); - else { - that._startScript(cmd, opts, cb); - } -}; - -/** - * Reset process counters - * - * @method resetMetaProcess - */ -API.prototype.reset = function(process_name, cb) { - var that = this; - - function processIds(ids, cb) { - async.eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) { - that.Client.executeRemote('resetMetaProcessId', id, function(err, res) { - if (err) console.error(err); - Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id); - return next(); - }); - }, function(err) { - if (err) return cb(Common.retErr(err)); - return cb ? cb(null, {success:true}) : that.speedList(); - }); - } - - if (process_name == 'all') { - that.Client.getAllProcessId(function(err, ids) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - return processIds(ids, cb); - }); - } - else if (isNaN(process_name)) { - that.Client.getProcessIdByName(process_name, function(err, ids) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - if (ids.length === 0) { - Common.printError('Unknown process name'); - return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT); - } - return processIds(ids, cb); - }); - } else { - processIds([process_name], cb); - } -}; - -/** - * Update daemonized PM2 Daemon - * - * @param {Function} cb callback when pm2 has been upgraded - */ -API.prototype.update = function(cb) { - var that = this; - - Common.printOut('Be sure to have the latest version by doing `npm install pm2@latest -g` before doing this procedure.'); - - // Dump PM2 processes - that.Client.executeRemote('notifyKillPM2', {}, function() {}); - - that.getVersion(function(err, new_version) { - // If not linked to keymetrics, and update pm2 to latest, display motd.update - if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) { - var dt = fs.readFileSync(path.join(__dirname, that._conf.KEYMETRICS_UPDATE)); - console.log(dt.toString()); - } - - that.dump(function(err) { - debug('Dumping successfull', err); - that.killDaemon(function() { - debug('------------------ Everything killed', arguments); - that.Client.launchDaemon({interactor:false}, function(err, child) { - that.Client.launchRPC(function() { - that.resurrect(function() { - Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); - Modularizer.launchAll(that, function() { - KMDaemon.launchAndInteract(that._conf, null, function(err, data, interactor_proc) { - // Interactor error can be skipped here - return cb ? cb(null, {success:true}) : that.speedList(); - }); - }); - }); - }); - }); - }); - }); - }); - - return false; -}; - -/** - * Graceful Reload an application - * - * @param {String} process_name Application Name or All - * @param {Object} opts Options - * @param {Function} cb Callback - */ -API.prototype.gracefulReload = function(process_name, opts, cb) { - var that = this; - - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Warning gracefulReload will be soon deprecated')); - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Use http://pm2.keymetrics.io/docs/usage/signals-clean-restart/ instead')); - - if (Common.isConfigFile(process_name)) - that._startJson(process_name, commander, 'softReloadProcessId'); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); - that._operate('softReloadProcessId', process_name, opts, cb); - } -}; - -/** - * Reload an application - * - * @param {String} process_name Application Name or All - * @param {Object} opts Options - * @param {Function} cb Callback - */ -API.prototype.reload = function(process_name, opts, cb) { - var that = this; - - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - - var delay = Common.lockReload(); - - if (delay > 0 && opts.force != true) { - Common.printError(conf.PREFIX_MSG_ERR + 'Reload already in progress, please try again in ' + Math.floor((conf.RELOAD_LOCK_TIMEOUT - delay) / 1000) + ' seconds or use --force'); - return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT); - } - - if (Common.isConfigFile(process_name)) - that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) { - Common.unlockReload(); - if (err) - return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); - return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);; - }); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); - - that._operate('reloadProcessId', process_name, opts, function(err, apps) { - Common.unlockReload(); - - if (err) - return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); - return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);; - }); - } -}; - -/** - * Restart process - * - * @param {String} cmd Application Name / Process id / JSON application file / 'all' - * @param {Object} opts Extra options to be updated - * @param {Function} cb Callback - */ -API.prototype.restart = function(cmd, opts, cb) { - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - var that = this; - - if (typeof(cmd) === 'number') - cmd = cmd.toString(); - - if (cmd == "-") { - // Restart from PIPED JSON - process.stdin.resume(); - process.stdin.setEncoding('utf8'); - process.stdin.on('data', function (param) { - process.stdin.pause(); - that.actionFromJson('restartProcessId', param, opts, 'pipe', cb); - }); - } - else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object') - that._startJson(cmd, opts, 'restartProcessId', cb); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); - that._operate('restartProcessId', cmd, opts, cb); - } -}; - -/** - * Delete process - * - * @param {String} process_name Application Name / Process id / Application file / 'all' - * @param {Function} cb Callback - */ -API.prototype.delete = function(process_name, jsonVia, cb) { - var that = this; - - if (typeof(jsonVia) === "function") { - cb = jsonVia; - jsonVia = null; - } - if (typeof(process_name) === "number") { - process_name = process_name.toString(); - } - - if (jsonVia == 'pipe') - return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', cb); - if (Common.isConfigFile(process_name)) - return that.actionFromJson('deleteProcessId', process_name, commander, 'file', cb); - else - that._operate('deleteProcessId', process_name, cb); -}; - -/** - * Stop process - * - * @param {String} process_name Application Name / Process id / Application file / 'all' - * @param {Function} cb Callback - */ -API.prototype.stop = function(process_name, cb) { - var that = this; - - if (typeof(process_name) === 'number') - process_name = process_name.toString(); - - if (process_name == "-") { - process.stdin.resume(); - process.stdin.setEncoding('utf8'); - process.stdin.on('data', function (param) { - process.stdin.pause(); - that.actionFromJson('stopProcessId', param, commander, 'pipe', cb); - }); - } - else if (Common.isConfigFile(process_name)) - that.actionFromJson('stopProcessId', process_name, commander, 'file', cb); - else - that._operate('stopProcessId', process_name, cb); -}; - -/** - * Get list of all processes managed - * - * @param {Function} cb Callback - */ -API.prototype.list = function(opts, cb) { - var that = this; - - if (typeof(opts) == 'function') { - cb = opts; - opts = null; - } - - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - - if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) { - var moment = require('moment'); - function show() { - process.stdout.write('\033[2J'); - process.stdout.write('\033[0f'); - console.log('Last refresh: ', moment().format('LTS')); - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - UX.dispAsTable(list, null); - }); - } - - show(); - setInterval(show, 900); - return false; - } - - return cb ? cb(null, list) : that.speedList(); - }); -}; - -/** - * Kill Daemon - * - * @param {Function} cb Callback - */ -API.prototype.killDaemon = API.prototype.kill = function(cb) { - var that = this; - - var semver = require('semver'); - Common.printOut(conf.PREFIX_MSG + 'Stopping PM2...'); - - that.Client.executeRemote('notifyKillPM2', {}, function() {}); - - that.killAllModules(function() { - that._operate('deleteProcessId', 'all', function(err, list) { - Common.printOut(conf.PREFIX_MSG + 'All processes have been stopped and deleted'); - process.env.PM2_SILENT = 'false'; - - that.killInteract(function(err, data) { - that.Client.killDaemon(function(err, res) { - if (err) Common.printError(err); - Common.printOut(conf.PREFIX_MSG + 'PM2 stopped'); - return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT); - }); - }); - }); - }); -}; - -///////////////////// -// Private methods // -///////////////////// - -/** - * Method to START / RESTART a script - * - * @private - * @param {string} script script name (will be resolved according to location) - */ -API.prototype._startScript = function(script, opts, cb) { - if (typeof opts == "function") { - cb = opts; - opts = {}; - } - var that = this; - - var app_conf = Config.transCMDToConf(opts); - var appConf = {}; - - if (!!opts.executeCommand) - app_conf.exec_mode = 'fork'; - else if (opts.instances !== undefined) - app_conf.exec_mode = 'cluster'; - else - app_conf.exec_mode = 'fork'; - - // Options set via environment variables - if (process.env.PM2_DEEP_MONITORING) - app_conf.deep_monitoring = true; - - if (typeof app_conf.name == 'function'){ - delete app_conf.name; - } - - delete app_conf.args; - - var argsIndex; - - if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) { - app_conf.args = opts.rawArgs.slice(argsIndex + 1); - } - else if (opts.scriptArgs) { - app_conf.args = opts.scriptArgs; - } - - app_conf.script = script; - - if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) - return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT); - - app_conf = appConf[0]; - - app_conf.username = Common.getCurrentUsername(); - - /** - * If -w option, write configuration to configuration.json file - */ - if (appConf.write) { - var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-pm2.json'); - Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path)); - // pretty JSON - try { - fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2)); - } catch (e) { - console.error(e.stack || e); - } - } - - /** - * If start start/restart application - */ - function restartExistingProcessName(cb) { - if (!isNaN(script) || - (typeof script === 'string' && script.indexOf('/') != -1) || - (typeof script === 'string' && path.extname(script) !== '')) - return cb(null); - - if (script !== 'all') { - that.Client.getProcessIdByName(script, function(err, ids) { - if (err && cb) return cb(err); - if (ids.length > 0) { - that._operate('restartProcessId', script, opts, function(err, list) { - if (err) return cb(err); - Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); - return cb(true, list); - }); - } - else return cb(null); - }); - } - else { - that._operate('restartProcessId', 'all', function(err, list) { - if (err) return cb(err); - Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); - return cb(true, list); - }); - } - } - - function restartExistingProcessId(cb) { - if (isNaN(script)) return cb(null); - - that._operate('restartProcessId', script, opts, function(err, list) { - if (err) return cb(err); - Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); - return cb(true, list); - }); - } - - /** - * Restart a process with the same full path - * Or start it - */ - function restartExistingProcessPath(cb) { - that.Client.executeRemote('getMonitorData', {}, function(err, procs) { - if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT); - - var full_path = path.resolve(that.cwd, script); - var managed_script = null; - - procs.forEach(function(proc) { - if (proc.pm2_env.pm_exec_path == full_path && - proc.pm2_env.name == app_conf.name) - managed_script = proc; - }); - - if (managed_script && - (managed_script.pm2_env.status == conf.STOPPED_STATUS || - managed_script.pm2_env.status == conf.STOPPING_STATUS || - managed_script.pm2_env.status == conf.ERRORED_STATUS)) { - // Restart process if stopped - var app_name = managed_script.pm2_env.name; - - that._operate('restartProcessId', app_name, opts, function(err, list) { - if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT); - Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); - return cb(true, list); - }); - return false; - } - else if (managed_script && !opts.force) { - Common.printError(conf.PREFIX_MSG_ERR + 'Script already launched, add -f option to force re-execution'); - return cb(new Error('Script already launched')); - } - - var resolved_paths = null; - - try { - resolved_paths = Common.resolveAppAttributes({ - cwd : that.cwd, - pm2_home : that.pm2_home - }, app_conf); - } catch(e) { - Common.printError(e); - return cb(Common.retErr(e)); - } - - Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')', - resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances); - - if (!resolved_paths.env) resolved_paths.env = {}; - - // Set PM2 HOME in case of child process using PM2 API - resolved_paths.env['PM2_HOME'] = that.pm2_home; - - var additional_env = Modularizer.getAdditionalConf(resolved_paths.name); - util._extend(resolved_paths.env, additional_env); - - // Is KM linked? - resolved_paths.km_link = that.gl_is_km_linked; - - that.Client.executeRemote('prepare', resolved_paths, function(err, data) { - if (err) { - Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err); - return cb(Common.retErr(err)); - } - - Common.printOut(conf.PREFIX_MSG + 'Done.'); - return cb(true, data); - }); - return false; - }); - } - - async.series([ - restartExistingProcessName, - restartExistingProcessId, - restartExistingProcessPath - ], function(err, data) { - - if (err instanceof Error) - return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); - - var ret = {}; - data.forEach(function(_dt) { - if (_dt !== undefined) - ret = _dt; - }); - - return cb ? cb(null, ret) : that.speedList(); - }); -}; - -/** - * Method to start/restart/reload processes from a JSON file - * It will start app not started - * Can receive only option to skip applications - * - * @private - */ -API.prototype._startJson = function(file, opts, action, pipe, cb) { - var config = {}; - var appConf = {}; - var deployConf = {}; - var apps_info = []; - var that = this; - - if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') { - cb = pipe; - } - - if (typeof(file) === 'object') { - config = file; - } else if (pipe === 'pipe') { - config = Common.parseConfig(file, 'pipe'); - } else { - var data = null; - - var isAbsolute = false - - //node 0.11 compatibility #2815 - if (typeof path.isAbsolute === 'function') { - isAbsolute = path.isAbsolute(file) - } else { - isAbsolute = require('./tools/IsAbsolute.js')(file) - } - - var file_path = isAbsolute ? file : path.join(that.cwd, file); - - debug('Resolved filepath %s', file_path); - - try { - data = fs.readFileSync(file_path); - } catch(e) { - Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found'); - return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); - } - - try { - config = Common.parseConfig(data, file); - } catch(e) { - Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated'); - console.error(e); - return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); - } - } - - if (config.deploy) - deployConf = config.deploy; - - if (config.apps) - appConf = config.apps; - else if (config.pm2) - appConf = config.pm2; - else - appConf = config; - - if (!Array.isArray(appConf)) - appConf = [appConf]; //convert to array - - if ((appConf = Common.verifyConfs(appConf)) instanceof Error) - return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT); - - process.env.PM2_JSON_PROCESSING = true; - - // Get App list - var apps_name = []; - var proc_list = {}; - - // Here we pick only the field we want from the CLI when starting a JSON - appConf.forEach(function(app) { - // --only - if (opts.only && opts.only != app.name) - return false; - // --watch - if (!app.watch && opts.watch && opts.watch === true) - app.watch = true; - // --ignore-watch - if (!app.ignore_watch && opts.ignore_watch) - app.ignore_watch = opts.ignore_watch; - // --instances - if (opts.instances && typeof(opts.instances) === 'number') - app.instances = opts.instances; - // --uid - if (opts.uid) - app.uid = opts.uid; - // --gid - if (opts.gid) - app.gid = opts.gid; - // Specific - if (app.append_env_to_name && opts.env) - app.name += ('-' + opts.env); - app.username = Common.getCurrentUsername(); - apps_name.push(app.name); - }); - - that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - - /** - * Uniquify in memory process list - */ - raw_proc_list.forEach(function(proc) { - proc_list[proc.name] = proc; - }); - - /** - * Auto detect application already started - * and act on them depending on action - */ - async.eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) { - // Skip app name (--only option) - if (apps_name.indexOf(proc_name) == -1) - return next(); - - if (!(action == 'reloadProcessId' || - action == 'softReloadProcessId' || - action == 'restartProcessId')) - throw new Error('Wrong action called'); - - var apps = appConf.filter(function(app) { - return app.name == proc_name; - }); - - var envs = apps.map(function(app){ - // Binds env_diff to env and returns it. - return Common.mergeEnvironmentVariables(app, opts.env, deployConf); - }); - - // Assigns own enumerable properties of all - // Notice: if people use the same name in different apps, - // duplicated envs will be overrode by the last one - var env = envs.reduce(function(e1, e2){ - return util._extend(e1, e2); - }); - - // When we are processing JSON, allow to keep the new env by default - env.updateEnv = true; - - // Pass `env` option - that._operate(action, proc_name, env, function(err, ret) { - if (err) Common.printError(err); - - // For return - apps_info = apps_info.concat(ret); - - that.Client.notifyGod(action, proc_name); - // And Remove from array to spy - apps_name.splice(apps_name.indexOf(proc_name), 1); - return next(); - }); - - }, function(err) { - if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - if (apps_name.length > 0 && action != 'start') - Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', ')); - // Start missing apps - return startApps(apps_name, function(err, apps) { - apps_info = apps_info.concat(apps); - return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0); - }); - }); - return false; - }); - - function startApps(app_name_to_start, cb) { - var apps_to_start = []; - var apps_started = []; - - appConf.forEach(function(app, i) { - if (app_name_to_start.indexOf(app.name) != -1) { - apps_to_start.push(appConf[i]); - } - }); - - async.eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) { - if (opts.cwd) - app.cwd = opts.cwd; - if (opts.force_name) - app.name = opts.force_name; - if (opts.started_as_module) - app.pmx_module = true; - - var resolved_paths = null; - - // hardcode script name to use `serve` feature inside a process file - if (app.script === 'serve') { - app.script = path.resolve(__dirname, 'API', 'Serve.js') - } - - try { - resolved_paths = Common.resolveAppAttributes({ - cwd : that.cwd, - pm2_home : that.pm2_home - }, app); - } catch (e) { - return next(); - } - - if (!resolved_paths.env) resolved_paths.env = {}; - - // Set PM2 HOME in case of child process using PM2 API - resolved_paths.env['PM2_HOME'] = that.pm2_home; - - var additional_env = Modularizer.getAdditionalConf(resolved_paths.name); - util._extend(resolved_paths.env, additional_env); - - resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf); - - delete resolved_paths.env.current_conf; - - // Is KM linked? - resolved_paths.km_link = that.gl_is_km_linked; - - that.Client.executeRemote('prepare', resolved_paths, function(err, data) { - if (err) { - Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err); - return next(); - } - if (data.length === 0) { - Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data); - return next(); - } - - Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length); - apps_started = apps_started.concat(data); - next(); - }); - - }, function(err) { - return cb ? cb(err || null, apps_started) : that.speedList(); - }); - return false; - } -}; - -/** - * Apply a RPC method on the json file - * @private - * @method actionFromJson - * @param {string} action RPC Method - * @param {object} options - * @param {string|object} file file - * @param {string} jsonVia action type (=only 'pipe' ?) - * @param {Function} - */ -API.prototype.actionFromJson = function(action, file, opts, jsonVia, cb) { - var appConf = {}; - var ret_processes = []; - var that = this; - - //accept programmatic calls - if (typeof file == 'object') { - cb = typeof jsonVia == 'function' ? jsonVia : cb; - appConf = file; - } - else if (jsonVia == 'file') { - var data = null; - - try { - data = fs.readFileSync(file); - } catch(e) { - Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found'); - return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); - } - - try { - appConf = Common.parseConfig(data, file); - } catch(e) { - Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated'); - console.error(e); - return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); - } - } else if (jsonVia == 'pipe') { - appConf = Common.parseConfig(file, 'pipe'); - } else { - Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe'); - return that.exitCli(conf.ERROR_EXIT); - } - - // Backward compatibility - if (appConf.apps) - appConf = appConf.apps; - - if (!Array.isArray(appConf)) - appConf = [appConf]; - - if ((appConf = Common.verifyConfs(appConf)) instanceof Error) - return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT); - - async.eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) { - var name = ''; - var new_env; - - if (!proc.name) - name = path.basename(proc.script); - else - name = proc.name; - - if (opts.only && opts.only != name) - return process.nextTick(next1); - - if (opts && opts.env) - new_env = Common.mergeEnvironmentVariables(proc, opts.env); - else - new_env = Common.mergeEnvironmentVariables(proc); - - that.Client.getProcessIdByName(name, function(err, ids) { - if (err) { - Common.printError(err); - return next1(); - } - if (!ids) return next1(); - - async.eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) { - var opts = {}; - - //stopProcessId could accept options to? - if (action == 'restartProcessId') { - opts = {id : id, env : new_env}; - } else { - opts = id; - } - - that.Client.executeRemote(action, opts, function(err, res) { - ret_processes.push(res); - if (err) { - Common.printError(err); - return next2(); - } - - if (action == 'restartProcessId') { - that.Client.notifyGod('restart', id); - } else if (action == 'deleteProcessId') { - that.Client.notifyGod('delete', id); - } else if (action == 'stopProcessId') { - that.Client.notifyGod('stop', id); - } - - Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', name, id); - return next2(); - }); - }, function(err) { - return next1(null, ret_processes); - }); - }); - }, function(err) { - if (cb) return cb(null, ret_processes); - else return that.speedList(); - }); -}; - - -/** - * Main function to operate with PM2 daemon - * - * @param {String} action_name Name of action (restartProcessId, deleteProcessId, stopProcessId) - * @param {String} process_name can be 'all', a id integer or process name - * @param {Object} envs object with CLI options / environment - */ -API.prototype._operate = function(action_name, process_name, envs, cb) { - var that = this; - var update_env = false; - var ret = []; - - // Make sure all options exist - if (!envs) - envs = {}; - - if (typeof(envs) == 'function'){ - cb = envs; - envs = {}; - } - - // Set via env.update (JSON processing) - if (envs.updateEnv === true) - update_env = true; - - var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS; - - if (!process.env.PM2_JSON_PROCESSING || envs.commands) { - envs = that._handleAttributeUpdate(envs); - } - - /** - * Set current updated configuration if not passed - */ - if (!envs.current_conf) { - var _conf = fclone(envs); - envs = { - current_conf : _conf - } - - // Is KM linked? - envs.current_conf.km_link = that.gl_is_km_linked; - } - - /** - * Operate action on specific process id - */ - function processIds(ids, cb) { - Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids); - - if (action_name == 'deleteProcessId') - concurrent_actions = 10; - - async.eachLimit(ids, concurrent_actions, function(id, next) { - var opts; - - // These functions need extra param to be passed - if (action_name == 'restartProcessId' || - action_name == 'reloadProcessId' || - action_name == 'softReloadProcessId') { - var new_env = {}; - - if (update_env === true) { - if (conf.PM2_PROGRAMMATIC == true) - new_env = Common.safeExtend({}, process.env); - else - new_env = util._extend({}, process.env); - - Object.keys(envs).forEach(function(k) { - new_env[k] = envs[k]; - }); - } - else { - new_env = envs; - } - - opts = { - id : id, - env : new_env - }; - } - else { - opts = id; - } - - that.Client.executeRemote(action_name, opts, function(err, res) { - if (err) { - Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id); - return next('Process not found'); - } - - if (action_name == 'restartProcessId') { - that.Client.notifyGod('restart', id); - } else if (action_name == 'deleteProcessId') { - that.Client.notifyGod('delete', id); - } else if (action_name == 'stopProcessId') { - that.Client.notifyGod('stop', id); - } else if (action_name == 'reloadProcessId') { - that.Client.notifyGod('reload', id); - } else if (action_name == 'softReloadProcessId') { - that.Client.notifyGod('graceful reload', id); - } - - if (!Array.isArray(res)) - res = [res]; - - // Filter return - res.forEach(function(proc) { - Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id); - - if (!proc.pm2_env) return false; - - ret.push({ - name : proc.pm2_env.name, - pm_id : proc.pm2_env.pm_id, - status : proc.pm2_env.status, - restart_time : proc.pm2_env.restart_time, - pm2_env : { - name : proc.pm2_env.name, - pm_id : proc.pm2_env.pm_id, - status : proc.pm2_env.status, - restart_time : proc.pm2_env.restart_time, - env : proc.pm2_env.env - } - }); - }); - - return next(); - }); - }, function(err) { - if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - return cb ? cb(null, ret) : that.speedList(); - }); - } - - if (process_name == 'all') { - that.Client.getAllProcessId(function(err, ids) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - if (!ids || ids.length === 0) { - Common.printError(conf.PREFIX_MSG_WARNING + 'No process found'); - return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); - } - - return processIds(ids, cb); - }); - } - // operate using regex - else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') { - var regex = new RegExp(process_name.replace(/\//g, '')); - - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - Common.printError('Error retrieving process list: ' + err); - return cb(err); - } - var found_proc = []; - list.forEach(function(proc) { - if (regex.test(proc.pm2_env.name)) { - found_proc.push(proc.pm_id); - } - }); - - if (found_proc.length === 0) { - Common.printError(conf.PREFIX_MSG_WARNING + 'No process found'); - return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); - } - - return processIds(found_proc, cb); - }); - } - else if (isNaN(process_name)) { - /** - * We can not stop or delete a module but we can restart it - * to refresh configuration variable - */ - var allow_module_restart = action_name == 'restartProcessId' ? true : false; - - that.Client.getProcessIdByName(process_name, allow_module_restart, function(err, ids) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - if (!ids || ids.length === 0) { - Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', process_name); - return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); - } - - /** - * Determine if the process to restart is a module - * if yes load configuration variables and merge with the current environment - */ - var additional_env = Modularizer.getAdditionalConf(process_name); - util._extend(envs, additional_env); - - return processIds(ids, cb); - }); - } else { - // Check if application name as number is an app name - that.Client.getProcessIdByName(process_name, function(err, ids) { - if (ids.length > 0) - return processIds(ids, cb); - // Else operate on pm id - return processIds([process_name], cb); - }); - } -}; - -/** - * Converts CamelCase Commander.js arguments - * to Underscore - * (nodeArgs -> node_args) - */ -API.prototype._handleAttributeUpdate = function(opts) { - var conf = Config.transCMDToConf(opts); - var that = this; - - if (typeof(conf.name) != 'string') - delete conf.name; - - var argsIndex = 0; - if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) { - conf.args = opts.rawArgs.slice(argsIndex + 1); - } - - var appConf = Common.verifyConfs(conf)[0]; - - if (appConf instanceof Error) { - Common.printError('Error while transforming CamelCase args to underscore'); - return appConf; - } - - if (argsIndex == -1) - delete appConf.args; - if (appConf.name == 'undefined') - delete appConf.name; - - delete appConf.exec_mode; - - if (util.isArray(appConf.watch) && appConf.watch.length === 0) { - if (!~opts.rawArgs.indexOf('--watch')) - delete appConf.watch - } - - // Options set via environment variables - if (process.env.PM2_DEEP_MONITORING) - appConf.deep_monitoring = true; - - // Force deletion of defaults values set by commander - // to avoid overriding specified configuration by user - if (appConf.treekill === true) - delete appConf.treekill; - if (appConf.pmx === true) - delete appConf.pmx; - if (appConf.vizion === true) - delete appConf.vizion; - if (appConf.automation === true) - delete appConf.automation; - if (appConf.autorestart === true) - delete appConf.autorestart; - - return appConf; -}; - -API.prototype.getProcessIdByName = function(name, cb) { - var that = this; - - this.Client.getProcessIdByName(name, function(err, id) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - console.log(id); - return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT); - }); -}; - -/** - * Description - * @method jlist - * @param {} debug - * @return - */ -API.prototype.jlist = function(debug) { - var that = this; - - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - Common.printError(err); - that.exitCli(conf.ERROR_EXIT); - } - - if (debug) { - process.stdout.write(util.inspect(list, false, null, false)); - } - else { - process.stdout.write(JSON.stringify(list)); - } - - that.exitCli(conf.SUCCESS_EXIT); - - }); -}; - -var gl_retry = 0; - -/** - * Description - * @method speedList - * @return - */ -API.prototype.speedList = function(code) { - var that = this; - - // Do nothing if PM2 called programmatically and not called from CLI (also in exitCli) - if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false; - - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - if (gl_retry == 0) { - gl_retry += 1; - return setTimeout(that.speedList.bind(that), 1400); - } - console.error('Error retrieving process list: %s.\nA process seems to be on infinite loop, retry in 5 seconds',err); - return that.exitCli(conf.ERROR_EXIT); - } - if (process.stdout.isTTY === false) { - UX.miniDisplay(list); - } - else if (commander.miniList && !commander.silent) - UX.miniDisplay(list); - else if (!commander.silent) { - if (that.gl_interact_infos) { - Common.printOut(chalk.green.bold('●') + ' Agent Online | Dashboard Access: ' + chalk.bold('https://app.keymetrics.io/#/r/%s') + ' | Server name: %s', that.gl_interact_infos.public_key, that.gl_interact_infos.machine_name); - } - UX.dispAsTable(list, commander); - Common.printOut(chalk.white.italic(' Use `pm2 show ` to get more details about an app')); - } - - if (that.Client.daemon_mode == false) { - Common.printOut('[--no-daemon] Continue to stream logs'); - Common.printOut('[--no-daemon] Exit on target PM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString()); - global._auto_exit = true; - return that.streamLogs('all', 0, false, 'HH:mm:ss', false); - } - else if (commander.attach === true) { - return that.streamLogs('all', 0, false, null, false); - } - else { - return that.exitCli(code ? code : conf.SUCCESS_EXIT); - } - }); -} - -/** - * Scale up/down a process - * @method scale - */ -API.prototype.scale = function(app_name, number, cb) { - var that = this; - - function addProcs(proc, value, cb) { - (function ex(proc, number) { - if (number-- === 0) return cb(); - Common.printOut(conf.PREFIX_MSG + 'Scaling up application'); - that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number)); - })(proc, number); - } - - function rmProcs(procs, value, cb) { - var i = 0; - - (function ex(procs, number) { - if (number++ === 0) return cb(); - that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number)); - })(procs, number); - } - - function end() { - return cb ? cb(null, {success:true}) : that.speedList(); - } - - this.Client.getProcessByName(app_name, function(err, procs) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - - if (!procs || procs.length === 0) { - Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name); - return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT); - } - - var proc_number = procs.length; - - if (typeof(number) === 'string' && number.indexOf('+') >= 0) { - number = parseInt(number, 10); - return addProcs(procs[0], number, end); - } - else if (typeof(number) === 'string' && number.indexOf('-') >= 0) { - number = parseInt(number, 10); - return rmProcs(procs[0], number, end); - } - else { - number = parseInt(number, 10); - number = number - proc_number; - - if (number < 0) - return rmProcs(procs, number, end); - else if (number > 0) - return addProcs(procs[0], number, end); - else { - Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do'); - return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT); - } - } - }); -}; - -/** - * Description - * @method describeProcess - * @param {} pm2_id - * @return - */ -API.prototype.describe = function(pm2_id, cb) { - var that = this; - - var found_proc = []; - - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - Common.printError('Error retrieving process list: ' + err); - that.exitCli(conf.ERROR_EXIT); - } - - list.forEach(function(proc) { - if ((!isNaN(pm2_id) && proc.pm_id == pm2_id) || - (typeof(pm2_id) === 'string' && proc.name == pm2_id)) { - found_proc.push(proc); - } - }); - - if (found_proc.length === 0) { - Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\'t exist', pm2_id); - return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT); - } - - if (!cb) { - found_proc.forEach(function(proc) { - UX.describeTable(proc); - }); - } - - return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT); - }); -}; - -/** - * API method to perform a deep update of PM2 - * @method deepUpdate - */ -API.prototype.deepUpdate = function(cb) { - var that = this; - - Common.printOut(conf.PREFIX_MSG + 'Updating PM2...'); - - var exec = require('shelljs').exec; - var child = exec("npm i -g pm2@latest; pm2 update", {async : true}); - - child.stdout.on('end', function() { - Common.printOut(conf.PREFIX_MSG + 'PM2 successfully updated'); - cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT); - }); -}; +module.exports = API; diff --git a/lib/API/Configuration.js b/lib/API/Configuration.js index 01b4439a..321a295e 100644 --- a/lib/API/Configuration.js +++ b/lib/API/Configuration.js @@ -1,5 +1,4 @@ -var Password = require('../Interactor/Password.js'); var Common = require('../Common.js'); var cst = require('../../constants.js'); var UX = require('./CliUx'); @@ -7,7 +6,7 @@ var chalk = require('chalk'); var async = require('async'); var Configuration = require('../Configuration.js'); //@todo double check that imported methods works -var InteractorDaemonizer = require('../Interactor/InteractorDaemonizer'); +var InteractorDaemonizer = require('@pm2/agent/src/InteractorClient'); module.exports = function(CLI) { @@ -63,36 +62,6 @@ module.exports = function(CLI) { return false; } - /** - * Specific when setting pm2 password - * Used for restricted remote actions - * Also alert Interactor that password has been set - */ - if (key.indexOf('pm2:passwd') > -1) { - value = Password.generate(value); - Configuration.set(key, value, function(err) { - if (err) - return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT); - InteractorDaemonizer.launchRPC(that._conf, function(err) { - if (err) { - displayConf('pm2', function() { - return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT) - }); - return false; - } - InteractorDaemonizer.rpc.passwordSet(function() { - InteractorDaemonizer.disconnectRPC(function() { - displayConf('pm2', function() { - return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT); - }); - }); - }); - return false; - }); - }); - return false; - } - /** * Set value */ diff --git a/lib/API/Containerizer.js b/lib/API/Containerizer.js index 28f4d572..686edfd1 100644 --- a/lib/API/Containerizer.js +++ b/lib/API/Containerizer.js @@ -4,7 +4,6 @@ var exec = require('child_process').exec; var chalk = require('chalk'); var util = require('util'); var fmt = require('../tools/fmt.js'); -var vizion = require('vizion'); var fs = require('fs'); var path = require('path'); var cst = require('../../constants.js'); @@ -148,7 +147,7 @@ function handleExit(CLI, opts, mode) { if (err) { console.error(err); } - vizion.analyze({folder : process.cwd()}, function recur_path(err, meta){ + require('vizion').analyze({folder : process.cwd()}, function recur_path(err, meta){ if (!err && meta.revision) { var commit_id = util.format('#%s(%s) %s', meta.branch, diff --git a/lib/API/Dashboard.js b/lib/API/Dashboard.js index 6e12f6c8..85c37a7f 100644 --- a/lib/API/Dashboard.js +++ b/lib/API/Dashboard.js @@ -162,7 +162,7 @@ Dashboard.init = function() { }); this.box4 = blessed.text({ - content: ' left/right: switch boards | up/down/mouse: scroll | Ctrl-C: exit{|} {cyan-fg}{bold}To go further check out https://keymetrics.io/{/} ', + content: ' left/right: switch boards | up/down/mouse: scroll | Ctrl-C: exit{|} {cyan-fg}{bold}To go further check out https://pm2.io/{/} ', left: '0%', top: '95%', width: '100%', diff --git a/lib/API/Deploy.js b/lib/API/Deploy.js index 9a19b516..7cc7845a 100644 --- a/lib/API/Deploy.js +++ b/lib/API/Deploy.js @@ -5,8 +5,6 @@ */ var fs = require('fs'); -var Deploy = require('pm2-deploy'); - var cst = require('../../constants.js'); var Utility = require('../Utility.js'); var Common = require('../Common.js'); @@ -104,7 +102,7 @@ module.exports = function(CLI) { json_conf.deploy[env]['post-deploy'] = 'pm2 startOrRestart ' + file + ' --env ' + env; } - Deploy.deployForEnv(json_conf.deploy, env, args, function(err, data) { + require('pm2-deploy').deployForEnv(json_conf.deploy, env, args, function(err, data) { if (err) { Common.printError('Deploy failed'); return cb ? cb(err) : that.exitCli(cst.ERROR_EXIT); diff --git a/lib/API/Extra.js b/lib/API/Extra.js index 00f445dd..bca1da25 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -15,6 +15,7 @@ var fs = require('fs'); var fmt = require('../tools/fmt.js'); var moment = require('moment'); var pkg = require('../../package.json'); +const semver = require('semver'); module.exports = function(CLI) { @@ -38,7 +39,6 @@ module.exports = function(CLI) { */ CLI.prototype.report = function() { var that = this; - var semver = require('semver'); function reporting(cb) { @@ -108,7 +108,7 @@ module.exports = function(CLI) { } that.Client.executeRemote('getVersion', {}, function(err, data) { - if (semver.satisfies(data, '>= 2.6.0')) + if (semver.satisfies(semver.coerce(data), '>=2.6.0')) reporting(); else { Common.printError(cst.PREFIX_MSG_ERR + 'You need to update your Daemon, please type `$ pm2 update`'); @@ -639,4 +639,27 @@ module.exports = function(CLI) { launchMonitor(); }; + + CLI.prototype.inspect = function(app_name, cb) { + const that = this; + if(semver.satisfies(process.versions.node, '>= 8.0.0')) { + this.trigger(app_name, 'internal:inspect', function (err, res) { + + if(res && res[0]) { + if (res[0].data.return === '') { + Common.printOut(`Inspect disabled on ${app_name}`); + } else { + Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); + } + } else { + Common.printOut(`Unable to activate inspect mode on ${app_name} !!!`); + } + + that.exitCli(cst.SUCCESS_EXIT); + }); + } else { + Common.printOut('Inspect is available for node version >=8.x !'); + that.exitCli(cst.SUCCESS_EXIT); + } + }; }; diff --git a/lib/API/Interaction.js b/lib/API/Interaction.js index 1bde2cd7..621dbf27 100644 --- a/lib/API/Interaction.js +++ b/lib/API/Interaction.js @@ -6,7 +6,8 @@ var chalk = require('chalk'); var async = require('async'); var path = require('path'); var fs = require('fs'); -var KMDaemon = require('../Interactor/InteractorDaemonizer'); +var KMDaemon = require('@pm2/agent/src/InteractorClient'); +var pkg = require('../../package.json') module.exports = function(CLI) { @@ -45,7 +46,8 @@ module.exports = function(CLI) { KMDaemon.launchAndInteract(that._conf, { secret_key : secret_key || null, public_key : public_key || null, - machine_name : machine_name || null + machine_name : machine_name || null, + pm2_version: pkg.version }, function(err, dt) { if (err) { return cb ? cb(err) : that.exitCli(cst.ERROR_EXIT); @@ -74,20 +76,20 @@ module.exports = function(CLI) { // // Interact // - CLI.prototype._pre_interact = function(cmd, public_key, machine, info_node) { + CLI.prototype._pre_interact = function(cmd, public_key, machine, opts) { var that = this; if (cmd == 'stop' || cmd == 'kill') { - console.log(chalk.cyan('[Keymetrics.io]') + ' Stopping agent...'); + console.log(chalk.cyan('[PM2 agent]') + ' Stopping agent...'); that.killInteract(function() { - console.log(chalk.cyan('[Keymetrics.io]') + ' Stopped'); + console.log(chalk.cyan('[PM2 agent]') + ' Stopped'); return process.exit(cst.SUCCESS_EXIT); }); return false; } if (cmd == 'info') { - console.log(chalk.cyan('[Keymetrics.io]') + ' Getting agent information...'); + console.log(chalk.cyan('[PM2 agent]') + ' Getting agent information...'); that.interactInfos(function(err, infos) { if (err) { console.error(err.message); @@ -104,10 +106,10 @@ module.exports = function(CLI) { try { fs.unlinkSync(cst.INTERACTION_CONF); } catch(e) { - console.log(chalk.cyan('[Keymetrics.io]') + ' No interaction config file found'); + console.log(chalk.cyan('[PM2 agent]') + ' No interaction config file found'); return process.exit(cst.SUCCESS_EXIT); } - console.log(chalk.cyan('[Keymetrics.io]') + ' Agent interaction ended'); + console.log(chalk.cyan('[PM2 agent]') + ' Agent interaction ended'); return process.exit(cst.SUCCESS_EXIT); }); return false; @@ -118,7 +120,8 @@ module.exports = function(CLI) { public_key : null, secret_key : null, machine_name : null, - info_node : null + info_node : null, + pm2_version: pkg.version }, function(err, dt) { if (err) { Common.printError(err); @@ -129,7 +132,7 @@ module.exports = function(CLI) { } if (cmd && !public_key) { - console.error(chalk.cyan('[Keymetrics.io]') + ' Command [%s] unknown or missing public key', cmd); + console.error(chalk.cyan('[PM2 agent]') + ' Command [%s] unknown or missing public key', cmd); return process.exit(cst.ERROR_EXIT); } @@ -143,9 +146,23 @@ module.exports = function(CLI) { public_key : public_key, secret_key : cmd, machine_name : machine, - info_node : info_node.infoNode || null + info_node : opts.infoNode || null, + pm2_version: pkg.version } + if (opts.ws === true && infos) { + infos.agent_transport_axon = false + infos.agent_transport_websocket = true + process.env.AGENT_TRANSPORT_AXON = false + process.env.AGENT_TRANSPORT_WEBSOCKET = true + } + else if (infos) { + infos.agent_transport_axon = true + infos.agent_transport_websocket = false + process.env.AGENT_TRANSPORT_AXON = true + process.env.AGENT_TRANSPORT_WEBSOCKET = false + } + KMDaemon.launchAndInteract(that._conf, infos, function(err, dt) { if (err) return that.exitCli(cst.ERROR_EXIT); diff --git a/lib/API/Keymetrics/cli-api.js b/lib/API/Keymetrics/cli-api.js deleted file mode 100644 index 3ad3ea03..00000000 --- a/lib/API/Keymetrics/cli-api.js +++ /dev/null @@ -1,248 +0,0 @@ -var cst = require('../../../constants.js'); -var Common = require('../../Common.js'); -var UX = require('../CliUx'); -var chalk = require('chalk'); -var async = require('async'); -var path = require('path'); -var fs = require('fs'); -var KMDaemon = require('../../Interactor/InteractorDaemonizer'); -var KM = require('./kmapi.js'); -var Table = require('cli-table-redemption'); -var open = require('../../tools/open.js'); -var promptly = require('promptly'); - -module.exports = function(CLI) { - - CLI.prototype.openDashboard = function() { - var that = this; - - KMDaemon.getInteractInfo(this._conf, function(err, data) { - if (err) { - Common.printError(chalk.bold.white('Agent if offline, type `$ pm2 register` to log in')); - return that.exitCli(cst.ERROR_EXIT); - } - Common.printOut(chalk.bold('Opening Dashboard in Browser...')); - open('https://app.keymetrics.io/#/r/' + data.public_key); - setTimeout(function() { - that.exitCli(); - }, 200); - }); - }; - - CLI.prototype.loginToKM = function() { - printMotd(); - return loginPrompt(); - }; - - CLI.prototype.registerToKM = function() { - printMotd(); - - promptly.confirm(chalk.bold('Do you have a Keymetrics.io account? (y/n)'), function (err, answer) { - if (answer == true) { - return loginPrompt(); - } - registerPrompt(); - }); - }; - - /** - * Monitor Selectively Processes (auto filter in interaction) - * @param String state 'monitor' or 'unmonitor' - * @param String target - * @param Function cb callback - */ - CLI.prototype.monitorState = function(state, target, cb) { - var that = this; - - if (process.env.NODE_ENV !== 'test') { - try { - fs.statSync(this._conf.INTERACTION_CONF); - } catch(e) { - printMotd(); - return registerPrompt(); - } - } - - if (!target) { - Common.printError(cst.PREFIX_MSG_ERR + 'Please specify an '); - return cb ? cb(new Error('argument missing')) : that.exitCli(cst.ERROR_EXIT); - } - - function monitor (pm_id, cb) { - // State can be monitor or unmonitor - that.Client.executeRemote(state, pm_id, cb); - } - if (target === 'all') { - that.Client.getAllProcessId(function (err, procs) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT); - } - async.forEachLimit(procs, 1, monitor, function (err, res) { - return typeof cb === 'function' ? cb(err, res) : that.speedList(); - }); - }); - } else if (!Number.isInteger(parseInt(target))) { - this.Client.getProcessIdByName(target, true, function (err, procs) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT); - } - async.forEachLimit(procs, 1, monitor, function (err, res) { - return typeof cb === 'function' ? cb(err, res) : that.speedList(); - }); - }); - } else { - monitor(parseInt(target), function (err, res) { - return typeof cb === 'function' ? cb(err, res) : that.speedList(); - }); - } - }; - - - /** - * Private Functions - */ - - function printMotd() { - var dt = fs.readFileSync(path.join(__dirname, 'motd')); - console.log(dt.toString()); - } - - function validateEmail(email) { - var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - if (re.test(email) == false) - throw new Error('Not an email'); - return email; - } - - function validateUsername(value) { - if (value.length < 6) { - throw new Error('Min length of 6'); - } - return value; - }; - - - function linkOpenExit(target_bucket) { - KMDaemon.launchAndInteract(cst, { - public_key : target_bucket.public_id, - secret_key : target_bucket.secret_id - }, function(err, dt) { - open('https://app.keymetrics.io/#/r/' + target_bucket.public_id); - setTimeout(function() { - process.exit(cst.SUCCESS_EXIT); - }, 100); - }); - } - - /** - * Login on Keymetrics - * Link to the only bucket or list bucket for selection - * Open Browser - */ - function loginPrompt(cb) { - console.log(chalk.bold('Log in to Keymetrics')); - (function retry() { - promptly.prompt('Username or Email: ', function(err, username) { - promptly.password('Password: ', { replace : '*' }, function(err, password) { - KM.loginAndGetAccessToken({ username : username, password: password }, function(err) { - if (err) { - console.error(chalk.red.bold(err) + '\n'); - return retry(); - } - KM.getBuckets(function(err, buckets) { - if (err) { - console.error(chalk.red.bold(err) + '\n'); - return retry(); - } - - if (buckets.length > 1) { - console.log(chalk.bold('Bucket list')); - - var table = new Table({ - style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}, - head : ['Bucket name', 'Plan type'] - }); - - buckets.forEach(function(bucket) { - table.push([bucket.name, bucket.credits.offer_type]); - }); - - console.log(table.toString()); - - (function retryInsertion() { - promptly.prompt('Type the bucket you want to link to: ', function(err, bucket_name) { - var target_bucket = null; - - buckets.some(function(bucket) { - if (bucket.name == bucket_name) { - target_bucket = bucket; - return true; - } - }); - - if (target_bucket == null) - return retryInsertion(); - linkOpenExit(target_bucket); - }); - })(); - } - else { - var target_bucket = buckets[0]; - console.log('Connecting local PM2 to Keymetrics Bucket [%s]', target_bucket.name); - - KMDaemon.launchAndInteract(cst, { - public_key : target_bucket.public_id, - secret_key : target_bucket.secret_id - }, function(err, dt) { - linkOpenExit(target_bucket); - }); - } - }); - }); - - }); - }) - })() - } - - /** - * Register on Keymetrics - * Create Bucket - * Auto Link local PM2 to new Bucket - * Open Browser for access to monitoring dashboard - */ - function registerPrompt() { - console.log(chalk.bold('Now registering to Keymetrics')); - promptly.prompt('Username: ', { - validator : validateUsername, - retry : true - }, function(err, username) { - promptly.prompt('Email: ', { - validator : validateEmail, - retry : true - }, function(err, email) { - promptly.password('Password: ', { replace : '*' }, function(err, password) { - process.stdout.write(chalk.bold('Creating account on Keymetrics..')); - var inter = setInterval(function() { - process.stdout.write('.'); - }, 300); - KM.fullCreationFlow({ - email : email, - password : password, - username : username - }, function(err, target_bucket) { - clearInterval(inter); - if (err) { - console.error('\n' + chalk.red.bold(err) + '\n'); - return registerPrompt(); - } - linkOpenExit(target_bucket); - }); - }); - }); - }) - } - -}; diff --git a/lib/API/Keymetrics/kmapi.js b/lib/API/Keymetrics/kmapi.js deleted file mode 100644 index af5d91f8..00000000 --- a/lib/API/Keymetrics/kmapi.js +++ /dev/null @@ -1,162 +0,0 @@ -var querystring = require('querystring'); -var https = require('https'); -var fs = require('fs'); -var needle = require('needle'); -var url = require('url'); -var cst = require('../../../constants.js'); - -var KM = function() { - this.BASE_URI = 'https://app.keymetrics.io'; - this.CLIENT_ID = '938758711'; - this.CB_URI = 'https://app.keymetrics.io'; - this.ACCESS_TOKEN_FILE = cst.KM_ACCESS_TOKEN; - this.access_token = null; -} - -/** - * @param user_info.username - * @param user_info.password - * @return promise - */ -KM.prototype.loginAndGetAccessToken = function (user_info, cb) { - var that = this; - var URL_AUTH = '/api/oauth/authorize?response_type=token&scope=all&client_id=' + - that.CLIENT_ID + '&redirect_uri=' + that.CB_URI; - - needle.get(that.BASE_URI + URL_AUTH, function(err, res) { - if (err) return cb(err); - - var cookie = res.cookies; - - needle.post(that.BASE_URI + '/api/oauth/login', user_info, { - cookies : cookie - }, function(err, resp, body) { - if (err) return cb(err); - if (body.indexOf('/api/oauth/login') > -1) return cb('Wrong credentials'); - - var location = resp.headers.location; - var redirect = that.BASE_URI + location; - - needle.get(redirect, { - cookies : cookie - }, function(err, res) { - if (err) return cb(err); - var refresh_token = querystring.parse(url.parse(res.headers.location).query).access_token; - - needle.post(that.BASE_URI + '/api/oauth/token', { - client_id : that.CLIENT_ID, - grant_type : 'refresh_token', - refresh_token : refresh_token, - scope : 'all' - }, function(err, res, body) { - if (err) return cb(err); - that.access_token = body.access_token; - return cb(null, body.access_token); - }) - }); - }); - }); -} - -KM.prototype.getLocalAccessToken = function(cb) { - var that = this; - - fs.readFile(that.ACCESS_TOKEN_FILE, function(e, content) { - if (e) return cb(e); - cb(null, content.toString()); - }); -}; - -KM.prototype.saveLocalAccessToken = function(access_token, cb) { - var that = this; - fs.writeFile(that.ACCESS_TOKEN_FILE, access_token, function(e, content) { - if (e) return cb(e); - cb(); - }); -}; - -KM.prototype.getBuckets = function(cb) { - var that = this; - - needle.get(that.BASE_URI + '/api/bucket', { - headers : { - 'Authorization' : 'Bearer ' + that.access_token - }, - json : true - }, function(err, res, body) { - if (err) return cb(err); - return cb(null, body); - }); -} - -/** - * @param user_info.username - * @param user_info.password - * @param user_info.email - * @return promise - */ -KM.prototype.register = function(user_info, cb) { - var that = this; - - needle.post(that.BASE_URI + '/api/oauth/register', user_info, { - json: true, - headers: { - 'X-Register-Provider': 'pm2-register' - } - }, function (err, res, body) { - if (err) return cb(err); - if (body.email && body.email.message) return cb(body.email.message); - if (body.username && body.username.message) return cb(body.username.message); - - cb(null, { - token : body.access_token.token - }) - }); -}; - -KM.prototype.defaultNode = function(cb) { - var that = this; - - needle.get(that.BASE_URI + '/api/node/default', function(err, res, body) { - if (err) return cb(err); - cb(null, url.parse(body.endpoints.web).protocol + '//' + url.parse(body.endpoints.web).hostname); - }); -} - - -KM.prototype.createBucket = function(default_node, bucket_name, cb) { - var that = this; - - needle.post(default_node + '/api/bucket/create_classic', { - name : bucket_name - }, { - json : true, - headers : { - 'Authorization' : 'Bearer ' + that.access_token - } - }, function(err, res, body) { - if (err) return cb(err); - cb(null, body); - }); -} - -KM.prototype.fullCreationFlow = function(user_info, cb) { - var that = this; - - this.register(user_info, function(err, dt) { - if (err) return cb(err); - that.access_token = dt.token; - that.defaultNode(function(err, default_node) { - if (err) return cb(err); - that.createBucket(default_node, 'Node Monitoring', function(err, packet) { - if (err) return cb(err); - return cb(null, { - secret_id : packet.bucket.secret_id, - public_id : packet.bucket.public_id - }); - }); - }) - }); -} - -module.exports = new KM; diff --git a/lib/API/Keymetrics/motd b/lib/API/Keymetrics/motd deleted file mode 100644 index 26b30058..00000000 --- a/lib/API/Keymetrics/motd +++ /dev/null @@ -1,8 +0,0 @@ - __ __ __ _ - / //_/__ __ ______ ___ ___ / /______(_)_________ - / ,< / _ \/ / / / __ `__ \/ _ \/ __/ ___/ / ___/ ___/ - / /| / __/ /_/ / / / / / / __/ /_/ / / / /__(__ ) -/_/ |_\___/\__, /_/ /_/ /_/\___/\__/_/ /_/\___/____/ - /____/ - - Harden your Node.js application, today diff --git a/lib/API/Modules/Modularizer.js b/lib/API/Modules/Modularizer.js index 7093324b..b8b88e1e 100644 --- a/lib/API/Modules/Modularizer.js +++ b/lib/API/Modules/Modularizer.js @@ -3,9 +3,9 @@ * Use of this source code is governed by a license that * can be found in the LICENSE file. */ -var shelljs = require('shelljs'); var path = require('path'); var fs = require('fs'); +var os = require('os'); var async = require('async'); var p = path; var readline = require('readline'); @@ -17,7 +17,6 @@ var Common = require('../../Common'); var Utility = require('../../Utility.js'); var ModularizerV1 = require('./Modularizerv1.js'); var Modularizer = module.exports = {}; -var mkdirp = require('mkdirp'); var MODULE_CONF_PREFIX = 'module-db-v2'; @@ -47,7 +46,7 @@ var KNOWN_MODULES = { * - Generate sample module via pm2 module:generate */ Modularizer.install = function (CLI, moduleName, opts, cb) { - // if user want to install module from ecosystem.json file + // if user want to install module from ecosystem.config.js file // it can also be a custom json file's name if (!moduleName || moduleName.length === 0 || moduleName.indexOf('.json') > 0) { var file = moduleName || cst.APP_CONF_DEFAULT_FILE; @@ -184,8 +183,8 @@ Modularizer.installModule = function(CLI, module_name, opts, cb) { var canonic_module_name = Utility.getCanonicModuleName(module_name); var install_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name); - mkdirp(install_path, function() { - process.chdir(process.env.HOME); + require('mkdirp')(install_path, function() { + process.chdir(os.homedir()); var install_instance = spawn(cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error', '--prefix', install_path ], { stdio : 'inherit', @@ -404,14 +403,14 @@ function uninstallModule(CLI, opts, cb) { if (module_name != '.') { console.log(proc_path); - shelljs.rm('-r', proc_path); + require('shelljs').rm('-r', proc_path); } return cb(err); } if (module_name != '.') { - shelljs.rm('-r', proc_path); + require('shelljs').rm('-r', proc_path); } return cb(null, data); @@ -435,9 +434,9 @@ var Rollback = { CLI.deleteModule(canonic_module_name, function() { // Delete failing module - shelljs.rm('-r', module_path); + require('shelljs').rm('-r', module_path); // Restore working version - shelljs.cp('-r', backup_path, module_path); + require('shelljs').cp('-r', backup_path, cst.DEFAULT_MODULE_PATH); var proc_path = path.join(module_path, 'node_modules', canonic_module_name); var package_json_path = path.join(proc_path, 'package.json'); @@ -455,7 +454,7 @@ var Rollback = { var tmpdir = require('os').tmpdir(); var canonic_module_name = Utility.getCanonicModuleName(module_name); var module_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name); - shelljs.cp('-r', module_path, tmpdir); + require('shelljs').cp('-r', module_path, tmpdir); } } @@ -520,13 +519,13 @@ Modularizer.publish = function(cb) { package_json.name, package_json.version); - shelljs.exec('npm publish', function(code) { + require('shelljs').exec('npm publish', function(code) { Common.printOut(cst.PREFIX_MSG_MOD + 'Module - %s@%s successfully published', package_json.name, package_json.version); Common.printOut(cst.PREFIX_MSG_MOD + 'Pushing module on Git'); - shelljs.exec('git add . ; git commit -m "' + package_json.version + '"; git push origin master', function(code) { + require('shelljs').exec('git add . ; git commit -m "' + package_json.version + '"; git push origin master', function(code) { Common.printOut(cst.PREFIX_MSG_MOD + 'Installable with pm2 install %s', package_json.name); return cb(null, package_json); @@ -549,11 +548,11 @@ Modularizer.generateSample = function(app_name, cb) { var cmd3 = 'cd ' + module_name + ' ; npm install'; Common.printOut(cst.PREFIX_MSG_MOD + 'Getting sample app'); - shelljs.exec(cmd1, function(err) { + require('shelljs').exec(cmd1, function(err) { if (err) Common.printError(cst.PREFIX_MSG_MOD_ERR + err.message); - shelljs.exec(cmd2, function(err) { + require('shelljs').exec(cmd2, function(err) { console.log(''); - shelljs.exec(cmd3, function(err) { + require('shelljs').exec(cmd3, function(err) { console.log(''); Common.printOut(cst.PREFIX_MSG_MOD + 'Module sample created in folder: ', path.join(process.cwd(), module_name)); console.log(''); diff --git a/lib/API/Modules/Modularizerv1.js b/lib/API/Modules/Modularizerv1.js index 00d48991..ad63a560 100644 --- a/lib/API/Modules/Modularizerv1.js +++ b/lib/API/Modules/Modularizerv1.js @@ -3,7 +3,6 @@ * Use of this source code is governed by a license that * can be found in the LICENSE file. */ -var shelljs = require('shelljs'); var path = require('path'); var fs = require('fs'); var async = require('async'); diff --git a/lib/API/Modules/Modules.js b/lib/API/Modules/Modules.js index 77412b01..3d9f6f2a 100644 --- a/lib/API/Modules/Modules.js +++ b/lib/API/Modules/Modules.js @@ -11,7 +11,6 @@ var UX = require('../CliUx'); var chalk = require('chalk'); var async = require('async'); -var shelljs = require('shelljs'); var path = require('path'); var fs = require('fs'); var p = path; diff --git a/lib/API/Monit.js b/lib/API/Monit.js index 08d9a63a..9d86c27c 100644 --- a/lib/API/Monit.js +++ b/lib/API/Monit.js @@ -45,7 +45,7 @@ Monit.reset = function(msg) { this.multi.charm.reset(); - this.multi.write('\x1B[32m⌬ PM2 \x1B[39mmonitoring\x1B[96m (To go further check out https://app.keymetrics.io) \x1B[39m\n\n'); + this.multi.write('\x1B[32m⌬ PM2 \x1B[39mmonitoring\x1B[96m (To go further check out https://app.pm2.io) \x1B[39m\n\n'); if(msg) { this.multi.write(msg); @@ -244,4 +244,4 @@ Monit.updateBars = function(proc) { return this; } -module.exports = Monit; \ No newline at end of file +module.exports = Monit; diff --git a/lib/API/PM2/CliAuth.js b/lib/API/PM2/CliAuth.js new file mode 100644 index 00000000..17e74d8b --- /dev/null +++ b/lib/API/PM2/CliAuth.js @@ -0,0 +1,285 @@ + + +'use strict' + +const AuthStrategy = require('@pm2/js-api/src/auth_strategies/strategy') + +const http = require('http') +const fs = require('fs') +const url = require('url') +const exec = require('child_process').exec +const async = require('async') +const path = require('path') +const os = require('os') +const needle = require('needle'); +const chalk = require('chalk') +const cst = require('../../../constants.js'); + +module.exports = class CustomStrategy extends AuthStrategy { + // the client will try to call this but we handle this part ourselves + retrieveTokens (km, cb) { + this.authenticated = false + this.callback = cb + this.km = km + this.BASE_URI = 'https://app.keymetrics.io'; + } + + // so the cli know if we need to tell user to login/register + isAuthenticated () { + return new Promise((resolve, reject) => { + if (this.authenticated) return resolve(true) + + let tokensPath = cst.PM2_IO_ACCESS_TOKEN + fs.readFile(tokensPath, (err, tokens) => { + if (err && err.code === 'ENOENT') return resolve(false) + if (err) return reject(err) + + // verify that the token is valid + try { + tokens = JSON.parse(tokens || '{}') + } catch (err) { + fs.unlinkSync(tokensPath) + return resolve(false) + } + + // if the refresh tokens is here, the user could be automatically authenticated + return resolve(typeof tokens.refresh_token === 'string') + }) + }) + } + + verifyToken (refresh) { + return this.km.auth.retrieveToken({ + client_id: this.client_id, + refresh_token: refresh + }) + } + + // called when we are sure the user asked to be logged in + _retrieveTokens (optionalCallback) { + const km = this.km + const cb = this.callback + + async.tryEach([ + // try to find the token via the environement + (next) => { + if (!process.env.KM_TOKEN) { + return next(new Error('No token in env')) + } + this.verifyToken(process.env.KM_TOKEN) + .then((res) => { + return next(null, res.data) + }).catch(next) + }, + // try to find it in the file system + (next) => { + return next(new Error('nope')) + + fs.readFile(cst.PM2_IO_ACCESS_TOKEN, (err, tokens) => { + if (err) return next(err) + + // verify that the token is valid + tokens = JSON.parse(tokens || '{}') + if (new Date(tokens.expire_at) > new Date(new Date().toISOString())) { + return next(null, tokens) + } + + this.verifyToken(tokens.refresh_token) + .then((res) => { + return next(null, res.data) + }).catch(next) + }) + }, + // otherwise make the whole flow + (next) => { + return this.loginViaCLI((data) => { + // verify that the token is valid + this.verifyToken(data.refresh_token) + .then((res) => { + return next(null, res.data) + }).catch(next) + }) + } + ], (err, result) => { + // if present run the optional callback + if (typeof optionalCallback === 'function') { + optionalCallback(err, result) + } + + if (result.refresh_token) { + this.authenticated = true + let file = cst.PM2_IO_ACCESS_TOKEN + fs.writeFile(file, JSON.stringify(result), () => { + return cb(err, result) + }) + } else { + return cb(err, result) + } + }) + } + + loginViaCLI (cb) { + var promptly = require('promptly'); + + let retry = () => { + promptly.prompt('Username or Email: ', (err, username) => { + if (err) return retry(); + + promptly.password('Password: ', { replace : '*' }, (err, password) => { + if (err) return retry(); + + this._loginUser({ + username: username, + password: password + }, (err, data) => { + if (err) return retry() + cb(data) + }) + }) + }) + } + + retry() + } + + _loginUser (user_info, cb) { + const querystring = require('querystring'); + const AUTH_URI = 'https://id.keymetrics.io' + const URL_AUTH = '/api/oauth/authorize?response_type=token&scope=all&client_id=' + + this.client_id + '&redirect_uri=https://app.keymetrics.io'; + + console.log(chalk.bold('[-] Logging to pm2.io')) + + needle.get(AUTH_URI + URL_AUTH, (err, res) => { + if (err) return cb(err); + + var cookie = res.cookies; + + needle.post(AUTH_URI + '/api/oauth/login', user_info, { + cookies : cookie + }, (err, resp, body) => { + if (err) return cb(err); + if (resp.statusCode != 200) return cb('Wrong credentials'); + + var location = resp.headers['x-redirect']; + var redirect = AUTH_URI + location; + + needle.get(redirect, { + cookies : cookie + }, (err, res) => { + if (err) return cb(err); + var refresh_token = querystring.parse(url.parse(res.headers.location).query).access_token; + needle.post(AUTH_URI + '/api/oauth/token', { + client_id : this.client_id, + grant_type : 'refresh_token', + refresh_token : refresh_token, + scope : 'all' + }, (err, res, body) => { + if (err) return cb(err); + console.log(chalk.bold.green('[+] Logged in!')) + return cb(null, body); + }) + }); + }); + }); + } + + registerViaCLI (cb) { + var promptly = require('promptly'); + console.log(chalk.bold('[-] Registering to pm2.io')); + + var retry = () => { + promptly.prompt('Username: ', { + validator : this._validateUsername, + retry : true + }, (err, username) => { + promptly.prompt('Email: ', { + validator : this._validateEmail, + retry : true + },(err, email) => { + promptly.password('Password: ', { replace : '*' }, (err, password) => { + process.stdout.write('Creating account on pm2.io...'); + + var inter = setInterval(function() { + process.stdout.write('.'); + }, 200); + + this._registerUser({ + email : email, + password : password, + username : username + }, (err, data) => { + clearInterval(inter) + if (err) { + console.error() + console.error(chalk.bold.red(err)); + return retry() + } + console.log(chalk.green.bold('\n[+] Account created!')) + + this._loginUser({ + username: username, + password: password + }, (err, data) => { + this.callback(err, data) + return cb(err, data) + }) + }) + }); + }); + }) + } + retry(); + } + + /** + * Register function + * @param user_info.username + * @param user_info.password + * @param user_info.email + */ + _registerUser (user_info, cb) { + needle.post(this.BASE_URI + '/api/oauth/register', user_info, { + json: true, + headers: { + 'X-Register-Provider': 'pm2-register' + } + }, function (err, res, body) { + if (err) return cb(err); + if (body.email && body.email.message) return cb(body.email.message); + if (body.username && body.username.message) return cb(body.username.message); + if (!body.access_token) return cb(body.msg) + + cb(null, { + token : body.refresh_token.token + }) + }); + } + + _validateEmail (email) { + var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + if (re.test(email) == false) + throw new Error('Not an email'); + return email; + } + + _validateUsername (value) { + if (value.length < 6) { + throw new Error('Min length of 6'); + } + return value; + }; + + deleteTokens (km) { + return new Promise((resolve, reject) => { + // revoke the refreshToken + km.auth.revoke() + .then(res => { + // remove the token from the filesystem + let file = cst.PM2_IO_ACCESS_TOKEN + fs.unlinkSync(file) + return resolve(res) + }).catch(reject) + }) + } +} diff --git a/lib/API/PM2/PM2IO.js b/lib/API/PM2/PM2IO.js new file mode 100644 index 00000000..09048071 --- /dev/null +++ b/lib/API/PM2/PM2IO.js @@ -0,0 +1,245 @@ +'use strict' + +var cst = require('../../../constants.js'); +var Common = require('../../Common.js'); +var KMDaemon = require('@pm2/agent/src/InteractorClient'); + +const chalk = require('chalk'); +const async = require('async'); +const path = require('path'); +const fs = require('fs'); +const Table = require('cli-table-redemption'); +const open = require('../../tools/open.js'); +const pkg = require('../../../package.json') +const IOAPI = require('@pm2/js-api') + + +// const CustomStrategy = require('./custom_auth') +// const strategy = new CustomStrategy({ +// client_id: '7412235273' +// }) + +const CLIAuth = require('./CliAuth') + +const CLIAuthStrategy = new CLIAuth({ + client_id: '938758711' +}) + +const io = new IOAPI().use(CLIAuthStrategy) + +module.exports = function(CLI) { + + CLI.prototype.openDashboard = function() { + KMDaemon.getInteractInfo(this._conf, (err, data) => { + if (err) { + Common.printError(chalk.bold.white('Agent if offline, type `$ pm2 register` to log in')); + return this.exitCli(cst.ERROR_EXIT); + } + Common.printOut(chalk.bold('Opening Dashboard in Browser...')); + open('https://app.pm2.io/#/r/' + data.public_key); + setTimeout(_ => { + this.exitCli(); + }, 200); + }); + }; + + CLI.prototype.loginToKM = function() { + var promptly = require('promptly') + printMotd(); + + return CLIAuthStrategy._retrieveTokens((err, tokens) => { + if (err) { + console.error(`Oups, a error happened : ${err}`) + process.exit(1) + } + + // query both the user and all bucket + Promise.all([ io.user.retrieve(), io.bucket.retrieveAll() ]) + .then(results => { + let user = results[0].data + let buckets = results[1].data + + if (buckets.length > 1) { + var table = new Table({ + style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}, + head : ['Bucket name', 'Plan type'] + }); + + buckets.forEach(function(bucket) { + table.push([bucket.name, bucket.credits.offer_type]); + }); + + console.log(table.toString()); + + (function retryInsertion() { + promptly.prompt('Type the bucket you want to link to: ', function(err, bucket_name) { + var target_bucket = null; + + buckets.some(function(bucket) { + if (bucket.name == bucket_name) { + target_bucket = bucket; + return true; + } + }); + + if (target_bucket == null) + return retryInsertion(); + linkOpenExit(target_bucket); + }); + })(); + } + else { + var target_bucket = buckets[0]; + linkOpenExit(target_bucket) + } + }).catch(err => { + console.error(chalk.bold.red(`Oups, a error happened : ${err}`)) + return process.exit(1) + }) + }) + }; + + CLI.prototype.registerToKM = function() { + var promptly = require('promptly'); + + promptly.confirm(chalk.bold('Do you have a pm2.io account? (y/n)'), (err, answer) => { + if (answer == true) { + return this.loginToKM(); + } + CLIAuthStrategy.registerViaCLI((err, data) => { + console.log('[-] Creating Bucket...') + + io.bucket.create({ + name: 'Node.JS Monitoring' + }).then(res => { + const bucket = res.data.bucket + console.log(chalk.bold.green('[+] Bucket created!')) + linkOpenExit(bucket) + }) + }) + }); + } + + CLI.prototype.logoutToKM = function () { + CLIAuthStrategy._retrieveTokens(_ => { + io.auth.logout() + .then(res => { + console.log(`- Logout successful`) + return process.exit(0) + }).catch(err => { + console.error(`Oups, a error happened : ${err.message}`) + return process.exit(1) + }) + }) + } + + CLI.prototype.connectToPM2IO = function() { + io.bucket.create({ + name: 'Node.JS Monitoring' + }).then(res => { + const bucket = res.data.bucket + console.log(`Succesfully created a bucket !`) + console.log(`To start using it, you should push data with : + pm2 link ${bucket.secret_id} ${bucket.public_id} + `) + console.log(`You can also access our dedicated UI by going here : + https://app.pm2.io/#/r/${bucket.public_id} + `) + + KMDaemon.launchAndInteract(cst, { + public_key : bucket.public_id, + secret_key : bucket.secret_id + }, function(err, dt) { + open(`https://app.pm2.io/#/r/${bucket.public_id}`); + setTimeout(_ => { + return process.exit(0) + }, 200) + }); + + }) + } + + /** + * Monitor Selectively Processes (auto filter in interaction) + * @param String state 'monitor' or 'unmonitor' + * @param String target + * @param Function cb callback + */ + CLI.prototype.monitorState = function(state, target, cb) { + var that = this; + + if (process.env.NODE_ENV !== 'test') { + try { + fs.statSync(this._conf.INTERACTION_CONF); + } catch(e) { + printMotd(); + return this.registerToKM(); + } + } + + if (!target) { + Common.printError(cst.PREFIX_MSG_ERR + 'Please specify an '); + return cb ? cb(new Error('argument missing')) : that.exitCli(cst.ERROR_EXIT); + } + + function monitor (pm_id, cb) { + // State can be monitor or unmonitor + that.Client.executeRemote(state, pm_id, cb); + } + if (target === 'all') { + that.Client.getAllProcessId(function (err, procs) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT); + } + async.forEachLimit(procs, 1, monitor, function (err, res) { + return typeof cb === 'function' ? cb(err, res) : that.speedList(); + }); + }); + } else if (!Number.isInteger(parseInt(target))) { + this.Client.getProcessIdByName(target, true, function (err, procs) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT); + } + async.forEachLimit(procs, 1, monitor, function (err, res) { + return typeof cb === 'function' ? cb(err, res) : that.speedList(); + }); + }); + } else { + monitor(parseInt(target), function (err, res) { + return typeof cb === 'function' ? cb(err, res) : that.speedList(); + }); + } + }; + + + function linkOpenExit(target_bucket) { + console.log('[-] Linking local PM2 to newly created bucket...') + KMDaemon.launchAndInteract(cst, { + public_key : target_bucket.public_id, + secret_key : target_bucket.secret_id, + pm2_version: pkg.version + }, function(err, dt) { + console.log(chalk.bold.green('[+] Local PM2 Connected!')) + + console.log('[-] Opening Monitoring Interface in Browser...') + + setTimeout(function() { + open('https://app.pm2.io/#/r/' + target_bucket.public_id); + console.log(chalk.bold.green('[+] Opened! Exiting now.')) + setTimeout(function() { + process.exit(cst.SUCCESS_EXIT); + }, 100); + }, 1000) + }); + } + + /** + * Private Functions + */ + function printMotd() { + var dt = fs.readFileSync(path.join(__dirname, 'motd')); + console.log(dt.toString()); + } +}; diff --git a/lib/API/PM2/WebAuth.js b/lib/API/PM2/WebAuth.js new file mode 100644 index 00000000..49a52631 --- /dev/null +++ b/lib/API/PM2/WebAuth.js @@ -0,0 +1,192 @@ + +'use strict' + +const cst = require('../../../constants.js'); + +const AuthStrategy = require('@pm2/js-api/src/auth_strategies/strategy') +const http = require('http') +const fs = require('fs') +const url = require('url') +const exec = require('child_process').exec +const async = require('async') +const path = require('path') +const os = require('os') +const needle = require('needle'); + +module.exports = class CustomStrategy extends AuthStrategy { + // the client will try to call this but we handle this part ourselves + retrieveTokens (km, cb) { + this.authenticated = false + this.callback = cb + this.km = km + } + + // so the cli know if we need to tell user to login/register + isAuthenticated () { + return new Promise((resolve, reject) => { + if (this.authenticated) return resolve(true) + + let tokensPath = cst.PM2_IO_ACCESS_TOKEN + fs.readFile(tokensPath, (err, tokens) => { + if (err && err.code === 'ENOENT') return resolve(false) + if (err) return reject(err) + + // verify that the token is valid + try { + tokens = JSON.parse(tokens || '{}') + } catch (err) { + fs.unlinkSync(tokensPath) + return resolve(false) + } + + // if the refresh tokens is here, the user could be automatically authenticated + return resolve(typeof tokens.refresh_token === 'string') + }) + }) + } + + // called when we are sure the user asked to be logged in + _retrieveTokens (optionalCallback) { + const km = this.km + const cb = this.callback + + let verifyToken = (refresh) => { + return km.auth.retrieveToken({ + client_id: this.client_id, + refresh_token: refresh + }) + } + async.tryEach([ + // try to find the token via the environement + (next) => { + if (!process.env.KM_TOKEN) { + return next(new Error('No token in env')) + } + verifyToken(process.env.KM_TOKEN) + .then((res) => { + return next(null, res.data) + }).catch(next) + }, + // try to find it in the file system + (next) => { + return next(new Error('nope')) + + fs.readFile(cst.PM2_IO_ACCESS_TOKEN, (err, tokens) => { + if (err) return next(err) + + // verify that the token is valid + tokens = JSON.parse(tokens || '{}') + if (new Date(tokens.expire_at) > new Date(new Date().toISOString())) { + return next(null, tokens) + } + + verifyToken(tokens.refresh_token) + .then((res) => { + return next(null, res.data) + }).catch(next) + }) + }, + // otherwise make the whole flow + (next) => { + return this.loginViaWeb((data) => { + // verify that the token is valid + verifyToken(data.access_token) + .then((res) => { + return next(null, res.data) + }).catch(next) + }) + } + ], (err, result) => { + // if present run the optional callback + if (typeof optionalCallback === 'function') { + optionalCallback(err, result) + } + + if (result.refresh_token) { + this.authenticated = true + let file = cst.PM2_IO_ACCESS_TOKEN + fs.writeFile(file, JSON.stringify(result), () => { + return cb(err, result) + }) + } else { + return cb(err, result) + } + }) + } + + loginViaWeb (cb) { + let shutdown = false + let server = http.createServer((req, res) => { + // only handle one request + if (shutdown === true) return res.end() + shutdown = true + + let query = url.parse(req.url, true).query + + res.write(` + + + + +

+ You can go back to your terminal now :) +

+ `) + res.end() + server.close() + return cb(query) + }) + server.listen(43532, () => { + this.open(`${this.oauth_endpoint}${this.oauth_query}`) + }) + } + + deleteTokens (km) { + return new Promise((resolve, reject) => { + // revoke the refreshToken + km.auth.revoke() + .then(res => { + // remove the token from the filesystem + let file = cst.PM2_IO_ACCESS_TOKEN + fs.unlinkSync(file) + return resolve(res) + }).catch(reject) + }) + } + + open (target, appName, callback) { + let opener + const escape = function (s) { + return s.replace(/"/g, '\\"') + } + + if (typeof (appName) === 'function') { + callback = appName + appName = null + } + + switch (process.platform) { + case 'darwin': { + opener = appName ? `open -a "${escape(appName)}"` : `open` + break + } + case 'win32': { + opener = appName ? `start "" ${escape(appName)}"` : `start ""` + break + } + default: { + opener = appName ? escape(appName) : `xdg-open` + break + } + } + + if (process.env.SUDO_USER) { + opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener + } + return exec(`${opener} "${escape(target)}"`, callback) + } +} diff --git a/lib/API/PM2/motd b/lib/API/PM2/motd new file mode 100644 index 00000000..9d44fc76 --- /dev/null +++ b/lib/API/PM2/motd @@ -0,0 +1,13 @@ + + 88888888ba 88b d88 ad888888b, 88 ,ad8888ba, + 88 "8b 888b d888 d8" "88 88 d8"' `"8b + 88 ,8P 88`8b d8'88 a8P 88 d8' `8b + 88aaaaaa8P' 88 `8b d8' 88 ,d8P" 88 88 88 + 88""""""' 88 `8b d8' 88 a8P" 88 88 88 + 88 88 `8b d8' 88 a8P' 88 Y8, ,8P + 88 88 `888' 88 d8" 888 88 Y8a. .a8P + 88 88 `8' 88 88888888888 888 88 `"Y8888Y"' + + https://pm2.io/ + + Add Control and Monitoring to Your Node.js Apps diff --git a/lib/API/Serve.js b/lib/API/Serve.js index d4707ade..5027b25f 100644 --- a/lib/API/Serve.js +++ b/lib/API/Serve.js @@ -8,7 +8,7 @@ var http = require('http'); var url = require('url'); var path = require('path'); var debug = require('debug')('pm2:serve'); -var probe = require('pmx').probe(); +var probe = require('@pm2/io').probe(); /** * list of supported content types. diff --git a/lib/API/Startup.js b/lib/API/Startup.js index 7810dc82..8916e037 100644 --- a/lib/API/Startup.js +++ b/lib/API/Startup.js @@ -12,7 +12,6 @@ var exec = require('child_process').exec; var Common = require('../Common.js'); var cst = require('../../constants.js'); var spawn = require('child_process').spawn; -var shelljs = require('shelljs'); module.exports = function(CLI) { /** @@ -42,12 +41,13 @@ module.exports = function(CLI) { 'chkconfig' : 'systemv', 'rc-update' : 'openrc', 'launchctl' : 'launchd', - 'sysrc' : 'rcd' + 'sysrc' : 'rcd', + 'rcctl' : 'rcd-openbsd', }; var init_systems = Object.keys(hash_map); for (var i = 0; i < init_systems.length; i++) { - if (shelljs.which(init_systems[i]) != null) { + if (require('shelljs').which(init_systems[i]) != null) { break; } } @@ -148,9 +148,19 @@ module.exports = function(CLI) { 'sysrc -x ' + service_name + '_enable', 'rm /usr/local/etc/rc.d/' + service_name ]; + break; + case 'rcd-openbsd': + service_name = (opts.serviceName || 'pm2_' + user); + var destination = path.join('/etc/rc.d', service_name); + commands = [ + 'rcctl stop ' + service_name, + 'rcctl disable ' + service_name, + 'rm ' + destination + ]; + break; }; - shelljs.exec(commands.join('&& '), function(code, stdout, stderr) { + require('shelljs').exec(commands.join('&& '), function(code, stdout, stderr) { Common.printOut(stdout); Common.printOut(stderr); if (code == 0) { @@ -222,21 +232,9 @@ module.exports = function(CLI) { else template = getTemplate('systemd'); destination = '/etc/systemd/system/' + service_name + '.service'; - commands = [ 'systemctl enable ' + service_name - ] - - try { - fs.readFileSync(cst.PM2_PID_FILE_PATH).toString() - } catch(e) { - commands = [ - 'systemctl enable ' + service_name, - 'systemctl start ' + service_name, - 'systemctl daemon-reload', - 'systemctl status ' + service_name - ] - } + ]; break; case 'ubuntu14': case 'ubuntu12': @@ -283,6 +281,17 @@ module.exports = function(CLI) { 'sysrc ' + service_name + '_enable=YES' ]; break; + case 'openbsd': + case 'rcd-openbsd': + template = getTemplate('rcd-openbsd'); + service_name = (opts.serviceName || 'pm2_' + user); + destination = path.join('/etc/rc.d/', service_name); + commands = [ + 'chmod 755 ' + destination, + 'rcctl enable ' + service_name, + 'rcctl start ' + service_name + ]; + break; case 'openrc': template = getTemplate('openrc'); service_name = openrc_service_name; @@ -326,7 +335,7 @@ module.exports = function(CLI) { async.forEachLimit(commands, 1, function(command, next) { Common.printOut(cst.PREFIX_MSG + '[-] Executing: %s...', chalk.bold(command)); - shelljs.exec(command, function(code, stdout, stderr) { + require('shelljs').exec(command, function(code, stdout, stderr) { if (code === 0) { Common.printOut(cst.PREFIX_MSG + chalk.bold('[v] Command successfully executed.')); return next(); @@ -380,13 +389,32 @@ module.exports = function(CLI) { * @return */ function fin(err) { + + // try to fix issues with empty dump file + // like #3485 + if (env_arr.length === 0) { + + // fix : if no dump file, no process, only module and after pm2 update + if (!fs.existsSync(cst.DUMP_FILE_PATH)) { + that.clearDump(function(){}); + } + + // if no process in list don't modify dump file + // process list should not be empty + if(cb) { + return cb(null, {success: true}); + } else { + Common.printOut(cst.PREFIX_MSG + 'Nothing to save !!!'); + Common.printOut(cst.PREFIX_MSG + 'In this case we keep old dump file. To clear dump file you can delete it manually !'); + that.exitCli(cst.SUCCESS_EXIT); + return; + } + } + // Back up dump file try { if (fs.existsSync(cst.DUMP_FILE_PATH)) { - if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { - fs.unlinkSync(cst.DUMP_BACKUP_FILE_PATH); - } - fs.renameSync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); + fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, fs.readFileSync(cst.DUMP_FILE_PATH)); } } catch (e) { console.error(e.stack || e); @@ -399,8 +427,13 @@ module.exports = function(CLI) { } catch (e) { console.error(e.stack || e); try { - fs.unlinkSync(cst.DUMP_FILE_PATH); + // try to backup file + if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { + fs.writeFileSync(cst.DUMP_FILE_PATH, fs.readFileSync(cst.DUMP_BACKUP_FILE_PATH)); + } } catch (e) { + // don't keep broken file + fs.unlinkSync(cst.DUMP_FILE_PATH); console.error(e.stack || e); } Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to save dump file in %s', cst.DUMP_FILE_PATH); @@ -424,6 +457,21 @@ module.exports = function(CLI) { }); }; + /** + * Remove DUMP_FILE_PATH file and DUMP_BACKUP_FILE_PATH file + * @method dump + * @param {} cb + * @return + */ + CLI.prototype.clearDump = function(cb) { + fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify([])); + + if(cb && typeof cb === 'function') return cb(); + + Common.printOut(cst.PREFIX_MSG + 'Successfully created %s', cst.DUMP_FILE_PATH); + return this.exitCli(cst.SUCCESS_EXIT); + }; + /** * Resurrect processes * @method resurrect diff --git a/lib/API/Version.js b/lib/API/Version.js index 8f5bb93b..aefe9352 100644 --- a/lib/API/Version.js +++ b/lib/API/Version.js @@ -3,7 +3,6 @@ var cst = require('../../constants.js'); var Common = require('../Common.js'); var fs = require('fs'); var async = require('async'); -var vizion = require('vizion'); var child = require('child_process'); var printError = Common.printError; @@ -21,11 +20,11 @@ module.exports = function(CLI) { printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name); - that.Client.getProcessByName(process_name, function(err, processes) { + that.Client.getProcessByNameOrId(process_name, function (err, processes) { - if (processes.length === 0) { - printError('No processes with this name: %s', process_name); - return cb ? cb({msg:'Process not found: '+process_name}) : that.exitCli(cst.ERROR_EXIT); + if (err || processes.length === 0) { + printError('No processes with this name or id : %s', process_name); + return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); } var proc = processes[0]; @@ -33,7 +32,7 @@ module.exports = function(CLI) { printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name); return cb ? cb({success:false, msg: 'No versioning system found for process'}) : that.exitCli(cst.SUCCESS_EXIT); } - vizion.update({ + require('vizion').update({ folder: proc.pm2_env.versioning.repo_path }, function(err, meta) { if (err !== null) { @@ -82,19 +81,19 @@ module.exports = function(CLI) { printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name); - that.Client.getProcessByName(process_name, function(err, processes) { + that.Client.getProcessByNameOrId(process_name, function (err, processes) { - if (processes.length === 0) { - printError('No processes with this name: %s', process_name); - return cb ? cb({msg:'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); + if (err || processes.length === 0) { + printError('No processes with this name or id : %s', process_name); + return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); } var proc = processes[0]; if (proc.pm2_env.versioning) { - vizion.isUpToDate({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) { + require('vizion').isUpToDate({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) { if (err !== null) return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT); - vizion.revertTo( + require('vizion').revertTo( {revision: commit_id, folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) { @@ -138,20 +137,22 @@ module.exports = function(CLI) { var that = this; printOut(cst.PREFIX_MSG + 'Downgrading to previous commit repository for process name %s', process_name); - that.Client.getProcessByName(process_name, function(err, processes) { + that.Client.getProcessByNameOrId(process_name, function (err, processes) { - if (processes.length === 0) { - printError('No processes with this name: %s', process_name); - return cb ? cb({msg:'Process not found: '+process_name}) : that.exitCli(cst.ERROR_EXIT); + if (err || processes.length === 0) { + printError('No processes with this name or id : %s', process_name); + return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); } var proc = processes[0]; + // in case user searched by id/pid + process_name = proc.name; if (proc.pm2_env.versioning === undefined || proc.pm2_env.versioning === null) return cb({msg : 'Versioning unknown'}); - vizion.prev({ + require('vizion').prev({ folder: proc.pm2_env.versioning.repo_path }, function(err, meta) { if (err) @@ -165,7 +166,7 @@ module.exports = function(CLI) { getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) { execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) { if (err !== null) { - vizion.next({folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) { + require('vizion').next({folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) { printError(err); return cb ? cb({msg: meta.output + err}) : that.exitCli(cst.ERROR_EXIT); }); @@ -194,16 +195,18 @@ module.exports = function(CLI) { var that = this; printOut(cst.PREFIX_MSG + 'Updating to next commit repository for process name %s', process_name); - that.Client.getProcessByName(process_name, function(err, processes) { + that.Client.getProcessByNameOrId(process_name, function (err, processes) { - if (processes.length === 0) { - printError('No processes with this name: %s', process_name); - return cb ? cb({msg:'Process not found: '+process_name}) : that.exitCli(cst.ERROR_EXIT); + if (err || processes.length === 0) { + printError('No processes with this name or id: %s', process_name); + return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); } var proc = processes[0]; + // in case user searched by id/pid + process_name = proc.name; if (proc.pm2_env.versioning) { - vizion.next({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) { + require('vizion').next({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) { if (err !== null) return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT); if (meta.success === true) { @@ -211,7 +214,7 @@ module.exports = function(CLI) { execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) { if (err !== null) { - vizion.prev({folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) { + require('vizion').prev({folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) { printError(err); return cb ? cb({msg:meta.output + err}) : that.exitCli(cst.ERROR_EXIT); }); @@ -366,16 +369,6 @@ module.exports = function(CLI) { this._pull({process_name: process_name, action: 'reload'}, cb); }; - /** - * CLI method for updating a repository - * @method pullAndGracefulReload - * @param {string} process_name name of processes to pull - * @return - */ - CLI.prototype.pullAndGracefulReload = function (process_name, cb) { - this._pull({process_name: process_name, action: 'gracefulReload'}, cb); - }; - /** * CLI method for updating a repository to a specific commit id * @method pullCommitId diff --git a/lib/API/schema.json b/lib/API/schema.json index d650a73d..94ef70e0 100644 --- a/lib/API/schema.json +++ b/lib/API/schema.json @@ -2,23 +2,87 @@ "script": { "type": "string", "require": true, - "alias" : "exec" + "alias" : "exec", + "docDescription": "Path of the script to launch, required field" + }, + "name": { + "type": "string", + "docDefault": "Script filename without the extension (app for app.js)", + "docDescription": "Process name in the process list" + }, + "cwd": { + "type": "string", + "docDefault": "CWD of the current environment (from your shell)", + "docDescription": "Current working directory to start the process with" }, "args": { "type": [ "array", "string" - ] + ], + "docDescription": "Arguments to pass to the script" + }, + "exec_interpreter": { + "type": "string", + "alias": "interpreter", + "docDefault": "node", + "docDescription": "Interpreter absolute path" }, "node_args": { "type": [ "array", "string" ], - "alias": ["interpreterArgs", "interpreter_args"] + "alias": ["interpreterArgs", "interpreter_args"], + "docDescription": "Arguments to pass to the interpreter" }, - "name": { - "type": "string" + "out_file": { + "type": "string", + "alias": ["out", "output", "out_log"], + "docDefault": "~/.pm2/logs/-out.log", + "docDescription": "File path for stdout (each line is appended to this file)" + }, + "error_file": { + "type": "string", + "alias": ["error", "err", "err_file", "err_log"], + "docDefault": "~/.pm2/logs/-error.err", + "docDescription": "File path for stderr (each line is appended to this file)" + }, + "log_file": { + "type": [ + "boolean", + "string" + ], + "alias": "log", + "docDefault": "/dev/null", + "docDescription": "File path for combined stdout and stderr (each line is appended to this file)" + }, + "disable_logs": { + "type": "boolean", + "docDefault": false, + "docDescription": "Disable all logs storage" + }, + "log_type": { + "type": "string", + "docDescription": "Define a specific log output type, possible value: json" + }, + "log_date_format": { + "type": "string", + "docDescription": "Format for log timestamps in moment.js format (eg YYYY-MM-DD HH:mm Z)" + }, + "env": { + "type": [ + "object", + "string" + ], + "docDescription": "Specify environment variables to be injected" + }, + "^env_\\S*$": { + "type": [ + "object", + "string" + ], + "docDescription": "Specify environment variables to be injected when using --env " }, "max_memory_restart": { "type": [ @@ -27,124 +91,91 @@ ], "regex": "^\\d+(G|M|K)?$", "ext_type": "sbyte", - "desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)" - }, - "uid" : { - "type" : "string" - }, - "gid" : { - "type" : "string" - }, - "restart_delay": { - "type" : "number" - }, - "source_map_support" : { - "type": "boolean" - }, - "wait_ready" : { - "type": "boolean" - }, - "disable_source_map_support" : { - "type": "boolean" - }, - "instances": { - "type": "number" - }, - "kill_timeout": { - "type": "number" - }, - "listen_timeout": { - "type": "number" - }, - "port": { - "type": "number" - }, - "log_file": { - "type": [ - "boolean", - "string" - ], - "alias": "log" - }, - "error_file": { - "type": "string", - "alias": ["error", "err", "err_file", "err_log"] - }, - "log_type": { - "type": "string" - }, - "out_file": { - "type": "string", - "alias": ["output", "out", "out_log"] + "desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)", + "docDescription": "Restart the app if an amount of memory is exceeded (format: /[0-9](K|M|G)?/ K for KB, 'M' for MB, 'G' for GB, default to B)" }, "pid_file": { "type": "string", - "alias": "pid" + "alias": "pid", + "docDefault": "~/.pm2/pids/app_name-id.pid", + "docDescription": "File path where the pid of the started process is written by pm2" + }, + "restart_delay": { + "type" : "number", + "docDefault": 0, + "docDescription": "Time in ms to wait before restarting a crashing app" + }, + "source_map_support": { + "type": "boolean", + "docDefault": true, + "docDescription": "Enable or disable the source map support" + }, + "disable_source_map_support": { + "type": "boolean", + "docDefault": false, + "docDescription": "Enable or disable the source map support" + }, + "wait_ready": { + "type": "boolean", + "docDefault": false, + "docDescription": "Make the process wait for a process.send('ready')" + }, + "instances": { + "type": "number", + "docDefault": 1, + "docDescription": "Number of instances to be started in cluster mode" + }, + "kill_timeout": { + "type": "number", + "docDefault": 1600, + "docDescription": "Time in ms before sending the final SIGKILL signal after SIGINT" + }, + "listen_timeout": { + "type": "number", + "docDescription": "Time in ms before forcing a reload if app is still not listening/has still note sent ready" }, "cron_restart": { "type": "string", - "alias": "cron" - }, - "cwd": { - "type": "string" + "alias": "cron", + "docDescription": "A cron pattern to restart your app" }, "merge_logs": { "type": "boolean", - "alias" : "combine_logs" + "alias" : "combine_logs", + "docDefault": false, + "docDescription": "In cluster mode, merge each type of logs into a single file (instead of having one for each cluster)" }, - "vizion" : { + "vizion": { "type": "boolean", - "default" : true + "default" : true, + "docDefault" : "True", + "docDescription": "Enable or disable the versioning metadatas (vizion library)" }, - "pmx" : { + "autorestart": { "type": "boolean", - "default" : true - }, - "automation" : { - "type": "boolean", - "default" : true - }, - "autorestart" : { - "type": "boolean", - "default" : true - }, - "treekill" : { - "type": "boolean", - "default" : true + "default": true, + "docDefault": "True", + "docDescription": "Enable or disable auto restart after process failure" }, "watch": { "type": [ "boolean", "array", "string" - ] + ], + "docDefault": false, + "docDescription": "Enable or disable the watch mode" }, "ignore_watch": { "type": [ "array", "string" - ] + ], + "docDescription": "List of paths to ignore (regex)" }, "watch_options": { - "type": "object" - }, - "env": { - "type": [ - "object", - "string" - ] - }, - "^env_\\S*$": { - "type": [ - "object", - "string" - ] - }, - "disable_logs" : { - "type": "boolean" - }, - "log_date_format": { - "type": "string" + "type": "object", + "docDescription": "Object that will be used as an options with chokidar (refer to chokidar documentation)" }, "min_uptime": { "type": [ @@ -154,43 +185,51 @@ "regex": "^\\d+(h|m|s)?$", "desc": "it should be a NUMBER - milliseconds, \"[NUMBER]h\"(hours), \"[NUMBER]m\"(minutes) or \"[NUMBER]s\"(seconds)", "min": 100, - "ext_type": "stime" + "ext_type": "stime", + "docDefault": 1000, + "docDescription": "Minimum uptime of the app to be considered started (format is /[0-9]+(h|m|s)?/, for hours, minutes, seconds, docDefault to ms)" }, "max_restarts": { "type": "number", - "min": 0 + "min": 0, + "docDefault": 16, + "docDescription": "Number of times a script is restarted when it exits in less than min_uptime" }, "exec_mode": { "type": "string", "regex": "^(cluster|fork)(_mode)?$", "alias": "executeCommand", - "desc": "it should be \"cluster\"(\"cluster_mode\") or \"fork\"(\"fork_mode\") only" - }, - "exec_interpreter": { - "type": "string", - "alias": "interpreter" - }, - "write": { - "type": "boolean" + "desc": "it should be \"cluster\"(\"cluster_mode\") or \"fork\"(\"fork_mode\") only", + "docDefault": "fork", + "docDescription": "Set the execution mode, possible values: fork|cluster" }, "force": { - "type": "boolean" + "type": "boolean", + "docDefault": false, + "docDescription": "Start a script even if it is already running (only the script path is considered)" }, "append_env_to_name": { - "type": "boolean" + "type": "boolean", + "docDefault": false, + "docDescription": "Append the environment name to the app name" }, "post_update": { - "type": "array" - }, - "disable_trace": { - "type": [ - "boolean" - ] + "type": "array", + "docDescription": "List of commands executed after a pull/upgrade operation performed from Keymetrics dashboard" }, "trace": { "type": [ "boolean" - ] + ], + "docDefault": false, + "docDescription": "Enable or disable the transaction tracing" + }, + "disable_trace": { + "type": [ + "boolean" + ], + "docDefault": true, + "docDescription": "Enable or disable the transaction tracing" }, "v8": { "type": [ @@ -208,14 +247,66 @@ ] }, "increment_var": { - "type": "string" + "type": "string", + "docDescription": "Specify the name of an environment variable to inject which increments for each cluster" }, "instance_var": { "type": "string", - "default" : "NODE_APP_INSTANCE" -}, + "default": "NODE_APP_INSTANCE", + "docDefault": "NODE_APP_INSTANCE", + "docDescription": "Rename the NODE_APP_INSTANCE environment variable" + }, + "pmx": { + "type": "boolean", + "default": true, + "docDefault": "True", + "docDescription": "Enable or disable pmx wrapping" + }, + "automation": { + "type": "boolean", + "default": true, + "docDefault": "True", + "docDescription": "Enable or disable pmx wrapping" + }, + "treekill": { + "type": "boolean", + "default": true, + "docDefault": "True", + "docDescription": "Only kill the main process, not detached children" + }, + "port": { + "type": "number", + "docDescription": "Shortcut to inject a PORT environment variable" + }, + "username" : { + "type": "string", + "docDescription": "Current user that started the process" + }, + "uid": { + "type" : "string", + "docDefault": "Current user uid", + "docDescription": "Set user id" + }, + "gid": { + "type" : "string", + "docDefault": "Current user gid", + "docDescription": "Set group id" + }, "windowsHide": { "type": "boolean", - "default" : true + "docDefault": "True", + "docDescription": "Enable or disable the Windows popup when starting an app", + "default": true + }, + "kill_retry_time": { + "type": "number", + "default" : 100 + }, + "write": { + "type": "boolean" + }, + "io": { + "type": "object", + "docDescription": "Specify apm values and configuration" } } diff --git a/lib/Client.js b/lib/Client.js index 74103fd3..6edb18f7 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -6,15 +6,14 @@ var debug = require('debug')('pm2:client'); var Common = require('./Common.js'); -var KMDaemon = require('./Interactor/InteractorDaemonizer.js'); +var KMDaemon = require('@pm2/agent/src/InteractorClient'); var rpc = require('pm2-axon-rpc'); var async = require('async'); var axon = require('pm2-axon'); var util = require('util'); var fs = require('fs'); var path = require('path'); -var mkdirp = require('mkdirp'); -var shelljs = require('shelljs'); +var pkg = require('../package.json') function noop() {} @@ -79,7 +78,8 @@ Client.prototype.start = function(cb) { KMDaemon.launchAndInteract(that.conf, { machine_name : that.machine_name, public_key : that.public_key, - secret_key : that.secret_key + secret_key : that.secret_key, + pm2_version : pkg.version }, function(err, data, interactor_proc) { that.interactor_process = interactor_proc; }); @@ -129,7 +129,7 @@ Client.prototype.start = function(cb) { Client.prototype.initFileStructure = function (opts) { if (!fs.existsSync(opts.DEFAULT_LOG_PATH)) { try { - mkdirp.sync(opts.DEFAULT_LOG_PATH); + require('mkdirp').sync(opts.DEFAULT_LOG_PATH); } catch (e) { console.error(e.stack || e); } @@ -137,7 +137,7 @@ Client.prototype.initFileStructure = function (opts) { if (!fs.existsSync(opts.DEFAULT_PID_PATH)) { try { - mkdirp.sync(opts.DEFAULT_PID_PATH); + require('mkdirp').sync(opts.DEFAULT_PID_PATH); } catch (e) { console.error(e.stack || e); } @@ -153,7 +153,7 @@ Client.prototype.initFileStructure = function (opts) { if (!fs.existsSync(opts.DEFAULT_MODULE_PATH)) { try { - mkdirp.sync(opts.DEFAULT_MODULE_PATH); + require('mkdirp').sync(opts.DEFAULT_MODULE_PATH); } catch (e) { console.error(e.stack || e); } @@ -241,7 +241,7 @@ Client.prototype.launchDaemon = function(opts, cb) { var interpreter = 'node'; - if (shelljs.which('node') == null) + if (require('shelljs').which('node') == null) interpreter = process.execPath; var child = require('child_process').spawn(interpreter, node_args, { @@ -278,7 +278,8 @@ Client.prototype.launchDaemon = function(opts, cb) { KMDaemon.launchAndInteract(that.conf, { machine_name : that.machine_name, public_key : that.public_key, - secret_key : that.secret_key + secret_key : that.secret_key, + pm2_version : pkg.version }, function(err, data, interactor_proc) { that.interactor_process = interactor_proc; return cb(null, child); @@ -717,3 +718,25 @@ Client.prototype.getProcessByName = function(name, cb) { return cb(null, found_proc); }); }; + +Client.prototype.getProcessByNameOrId = function (nameOrId, cb) { + var foundProc = []; + + this.executeRemote('getMonitorData', {}, function (err, list) { + if (err) { + Common.printError('Error retrieving process list: ' + err); + return cb(err); + } + + list.forEach(function (proc) { + if (proc.pm2_env.name === nameOrId || + proc.pm2_env.pm_exec_path === path.resolve(nameOrId) || + proc.pid === parseInt(nameOrId) || + proc.pm2_env.pm_id === parseInt(nameOrId)) { + foundProc.push(proc); + } + }); + + return cb(null, foundProc); + }); +}; diff --git a/lib/Common.js b/lib/Common.js index 2dd053aa..35b13389 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -12,9 +12,7 @@ var fs = require('fs'); var path = require('path'); var os = require('os'); var util = require('util'); -var mkdirp = require('mkdirp'); var async = require('async'); -var shelljs = require('shelljs'); var chalk = require('chalk'); var fclone = require('fclone'); var semver = require('semver'); @@ -23,7 +21,6 @@ var isBinary = require('./tools/isbinaryfile.js'); var cst = require('../constants.js'); var extItps = require('./API/interpreter.json'); var Config = require('./tools/Config'); -var KMDaemon = require('./Interactor/InteractorDaemonizer.js'); var Common = module.exports; @@ -151,7 +148,7 @@ Common.prepareAppConf = function(opts, app) { if (!fs.existsSync(app.pm_exec_path)) { var ckd; // Try resolve command available in $PATH - if ((ckd = shelljs.which(app.script))) { + if ((ckd = require('shelljs').which(app.script))) { if (typeof(ckd) !== 'string') ckd = ckd.toString(); app.pm_exec_path = ckd; @@ -226,7 +223,7 @@ Common.prepareAppConf = function(opts, app) { if (!fs.existsSync(dir)) { Common.printError(cst.PREFIX_MSG_WARNING + 'Folder does not exists: ' + dir); Common.printOut(cst.PREFIX_MSG + 'Creating folder: ' + dir); - mkdirp(dir, function(err) { + require('mkdirp')(dir, function(err) { if (!err) return; Common.printError(cst.PREFIX_MSG_ERR + 'Could not create folder: ' + path.dirname(af)); throw new Error('Could not create folder'); @@ -247,20 +244,22 @@ Common.prepareAppConf = function(opts, app) { * @param {string} filename * @return {mixed} null if not conf file, json or yaml if conf */ -Common.isConfigFile = function(filename) { - if (typeof(filename) != 'string') +Common.isConfigFile = function (filename) { + if (typeof (filename) !== 'string') return null; - if (filename.indexOf('.json') != -1) + if (filename.indexOf('.json') !== -1) return 'json'; if (filename.indexOf('.yml') > -1 || filename.indexOf('.yaml') > -1) return 'yaml'; - if (filename.indexOf('.config.js') != -1) + if (filename.indexOf('.config.js') !== -1) return 'js'; + if (filename.indexOf('.config.mjs') !== -1) + return 'mjs'; return null; }; /** - * Parses a config file like ecosystem.json. Supported formats: JS, JSON, JSON5, YAML. + * Parses a config file like ecosystem.config.js. Supported formats: JS, JSON, JSON5, YAML. * @param {string} confString contents of the config file * @param {string} filename path to the config file * @return {Object} config object @@ -288,7 +287,7 @@ Common.parseConfig = function(confObj, filename) { filename.indexOf('.yaml') > -1) { return yamljs.parse(confObj.toString()); } - else if (filename.indexOf('.config.js') > -1) { + else if (filename.indexOf('.config.js') > -1 || filename.indexOf('.config.mjs') > -1) { var confPath = require.resolve(path.resolve(filename)); delete require.cache[confPath]; return require(confPath); @@ -366,7 +365,7 @@ var resolveNodeInterpreter = function(app) { var nvm_cmd = '. ' + nvm_bin + ' ; nvm install ' + node_version; Common.printOut(cst.PREFIX_MSG + 'Executing: %s', nvm_cmd); - shelljs.exec(nvm_cmd); + require('shelljs').exec(nvm_cmd); } Common.printOut(cst.PREFIX_MSG + chalk.green.bold('Setting Node to v%s (path=%s)'), @@ -409,7 +408,7 @@ Common.sink.resolveInterpreter = function(app) { app.exec_interpreter = path.resolve(__dirname, '../node_modules/.bin/coffee'); } - if (app.exec_interpreter != 'none' && shelljs.which(app.exec_interpreter) == null) { + if (app.exec_interpreter != 'none' && require('shelljs').which(app.exec_interpreter) == null) { Common.printError(cst.PREFIX_MSG_ERR + 'Interpreter ' + app.exec_interpreter + ' does not seem to be available'); } return app; @@ -585,9 +584,34 @@ Common.verifyConfs = function(appConfs){ for (var i = 0; i < appConfs.length; i++) { var app = appConfs[i]; - if (app.disable_trace) { - app.trace = false - delete app.disable_trace; + // JSON conf: alias cmd to script + if (app.cmd && !app.script) { + app.script = app.cmd + delete app.cmd + } + // JSON conf: alias command to script + if (app.command && !app.script) { + app.script = app.command + delete app.command + } + + app.username = Common.getCurrentUsername(); + + // If command is like pm2 start "python xx.py --ok" + // Then automatically start the script with bash -c and set a name eq to command + if (app.script && app.script.indexOf(' ') > -1) { + var _script = app.script; + if (require('shelljs').which('bash')) + app.script = 'bash'; + else if (require('shelljs').which('sh')) + app.script = 'sh'; + else + throw new Error('bash and sh not available in $PATH') + + app.args = ['-c', _script]; + if (!app.name) { + app.name = _script + } } if ((app.uid || app.gid) && app.force !== true) { @@ -597,25 +621,34 @@ Common.verifyConfs = function(appConfs){ } } - // If no uid set and command runned as sudo, use the parent shell USER - // to set it to his uid and not root - // if (!app.uid && process.env.SUDO_USER) { - // app.uid = process.env.SUDO_USER; - // } + // Specific options of PM2.io + if (process.env.PM2_DEEP_MONITORING) + app.deep_monitoring = true; - // Warn deprecates. - checkDeprecates(app); + if (app.disable_trace) { + app.trace = false + delete app.disable_trace; + } + + if (app.instances == 'max') + app.instances = 0; + // Sanity check, default to number of cores if value can't be parsed + if (typeof(app.instances) === 'string') + app.instances = parseInt(app.instances) || 0; // Check Exec mode checkExecMode(app); + if (app.exec_mode != 'cluster_mode' && + !app.instances && typeof(app.merge_logs) == 'undefined') + app.merge_logs = true; + // Render an app name if not existing. prepareAppName(app); var ret = Config.validateJSON(app); - //debug('After processing', ret); - // Show errors if existing. + // Show errors if existing. if (ret.errors && ret.errors.length > 0){ ret.errors.forEach(function(err){ warn(err); @@ -689,18 +722,6 @@ function checkExecMode(conf) { } } -/** - * Check deprecates and show warnings. - * @param {Object} conf - */ -function checkDeprecates(conf){ - if (conf.instances == 'max') - conf.instances = 0; - // Sanity check, default to number of cores if value can't be parsed - if (typeof(conf.instances) === 'string') - conf.instances = parseInt(conf.instances) || 0; -} - /** * Render an app name if not existing. * @param {Object} conf diff --git a/lib/Daemon.js b/lib/Daemon.js index 22f5fc7d..95eee01b 100644 --- a/lib/Daemon.js +++ b/lib/Daemon.js @@ -153,7 +153,7 @@ Daemon.prototype.innerStart = function(cb) { var profiler; try { - profiler = require('v8-profiler'); + profiler = require('v8-profiler-node8'); } catch(e) { profiler = null; } @@ -231,7 +231,6 @@ Daemon.prototype.innerStart = function(cb) { notifyByProcessId : God.notifyByProcessId, notifyKillPM2 : God.notifyKillPM2, - forceGc : God.forceGc, monitor : God.monitor, unmonitor : God.unmonitor, diff --git a/lib/God.js b/lib/God.js index dc001997..b6a64404 100644 --- a/lib/God.js +++ b/lib/God.js @@ -20,7 +20,6 @@ var numCPUs = require('os').cpus() ? require('os').cpus().length : 1; var path = require('path'); var EventEmitter2 = require('eventemitter2').EventEmitter2; var fs = require('fs'); -var pidusage = require('pidusage'); var vizion = require('vizion'); var debug = require('debug')('pm2:god'); var Utility = require('./Utility'); @@ -291,9 +290,6 @@ God.handleExit = function handleExit(clu, exit_code, kill_signal) { return false; } - if (proc.process.pid) - pidusage.unmonitor(proc.process.pid); - var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS || proc.pm2_env.status == cst.STOPPED_STATUS || proc.pm2_env.status == cst.ERRORED_STATUS) || (proc.pm2_env.autorestart === false || @@ -384,6 +380,9 @@ God.handleExit = function handleExit(clu, exit_code, kill_signal) { * Init new process */ God.prepare = function prepare (env, cb) { + // generate a new unique id for each processes + env.env.unique_id = Utility.generateUUID() + // if the app is standalone, no multiple instance if (typeof env.instances === 'undefined') { env.vizion_running = false; diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 65cb6415..808170b6 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -40,55 +40,63 @@ module.exports = function(God) { */ God.getMonitorData = function getMonitorData(env, cb) { var processes = God.getFormatedProcesses(); + var pids = processes.filter(filterBadProcess) + .map(function(pro, i) { + var pid = getProcessId(pro) + return pid; + }) - async.eachSeries(processes, function computeMonitor(pro, next) { - if (pro.pm2_env.status == cst.ONLINE_STATUS) { - var pid = pro.pid; - - if (pro.pm2_env.axm_options && pro.pm2_env.axm_options.pid) { - if (isNaN(pro.pm2_env.axm_options.pid)) { - pro['monit'] = { - memory : 0, - cpu : 0 - }; - return process.nextTick(next); - } - pid = pro.pm2_env.axm_options.pid; - } - - pidusage.stat(pid, function retPidUsage(err, res) { - if (err) { - // Do not log, some time modules does not retrieve PID - // console.error('Error caught while calling pidusage'); - // console.error(err); - pro['monit'] = { - memory : 0, - cpu : 0 - }; - return next(); - } - - pro['monit'] = { - memory : Math.floor(res.memory), - cpu : Math.floor(res.cpu) - }; - res = null; - pid = null; - return next(); - }); - } - else { + // No pids, return empty statistics + if (pids.length === 0) { + return cb(null, processes.map(function(pro) { pro['monit'] = { memory : 0, cpu : 0 }; - return next(); - } - }, function retMonitor(err, res) { - if (err) return cb(God.logAndGenerateError(err), null); - return cb(null, processes); - }); + return pro + })) + } + + pidusage(pids, function retPidUsage(err, statistics) { + // Just log, we'll set empty statistics + if (err) { + console.error('Error caught while calling pidusage'); + console.error(err); + } + + processes = processes.map(function(pro) { + if (filterBadProcess(pro) === false) { + pro['monit'] = { + memory : 0, + cpu : 0 + }; + + return pro; + } + + var pid = getProcessId(pro); + var stat = statistics[pid]; + + if (!stat) { + pro['monit'] = { + memory : 0, + cpu : 0 + }; + + return pro; + } + + pro['monit'] = { + memory: stat.memory, + cpu: Math.round(stat.cpu * 10) / 10 + }; + + return pro; + }); + + cb(null, processes); + }); }; /** @@ -126,6 +134,7 @@ module.exports = function(God) { God.dumpProcessList = function(cb) { var process_list = []; var apps = Utility.clone(God.getFormatedProcesses()); + var that = this; // Don't override the actual dump file if process list is empty // unless user explicitely did `pm2 dump`. @@ -137,13 +146,25 @@ module.exports = function(God) { } function fin(err) { + + // try to fix issues with empty dump file + // like #3485 + if (process_list.length === 0) { + + // fix : if no dump file, no process, only module and after pm2 update + if (!fs.existsSync(cst.DUMP_FILE_PATH)) { + that.clearDump(function(){}); + } + + // if no process in list don't modify dump file + // process list should not be empty + return cb(null, {success:true, process_list: process_list}); + } + // Back up dump file try { if (fs.existsSync(cst.DUMP_FILE_PATH)) { - if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { - fs.unlinkSync(cst.DUMP_BACKUP_FILE_PATH); - } - fs.renameSync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); + fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, fs.readFileSync(cst.DUMP_FILE_PATH)); } } catch (e) { console.error(e.stack || e); @@ -155,8 +176,13 @@ module.exports = function(God) { } catch (e) { console.error(e.stack || e); try { - fs.unlinkSync(cst.DUMP_FILE_PATH); + // try to backup file + if(fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { + fsExtra.copySync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); + } } catch (e) { + // don't keep broken file + fs.unlinkSync(cst.DUMP_FILE_PATH); console.error(e.stack || e); } } @@ -213,8 +239,13 @@ module.exports = function(God) { var proc = Utility.clone(God.clusters_db[id].pm2_env); + delete proc.created_at; delete proc.pm_id; + delete proc.unique_id; + + // generate a new unique id for new process + proc.unique_id = Utility.generateUUID() God.injectVariables(proc, function inject (_err, proc) { return God.executeApp(Utility.clone(proc), function (err, clu) { @@ -264,17 +295,19 @@ module.exports = function(God) { if (!(id in God.clusters_db)) return cb(God.logAndGenerateError(id + ' : id unknown'), {}); - //clear time-out restart task - clearTimeout(God.clusters_db[id].pm2_env.restart_task); - - if (God.clusters_db[id].pm2_env.status == cst.STOPPED_STATUS) - return cb(null, God.getFormatedProcess(id)); - // state == 'none' means that the process is not online yet - if (God.clusters_db[id].state && God.clusters_db[id].state === 'none') - return setTimeout(function() { God.stopProcessId(id, cb); }, 250); - var proc = God.clusters_db[id]; + //clear time-out restart task + clearTimeout(proc.pm2_env.restart_task); + + if (proc.pm2_env.status == cst.STOPPED_STATUS) { + proc.process.pid = 0; + return cb(null, God.getFormatedProcess(id)); + } + // state == 'none' means that the process is not online yet + if (proc.state && proc.state === 'none') + return setTimeout(function() { God.stopProcessId(id, cb); }, 250); + console.log('Stopping app:%s id:%s', proc.pm2_env.name, proc.pm2_env.pm_id); proc.pm2_env.status = cst.STOPPING_STATUS; @@ -286,7 +319,6 @@ module.exports = function(God) { God.killProcess(proc.process.pid, proc.pm2_env, function(err) { proc.pm2_env.status = cst.STOPPED_STATUS; - pidusage.unmonitor(proc.process.pid); God.notify('exit', proc); @@ -823,3 +855,27 @@ module.exports = function(God) { }); }; }; + +function filterBadProcess(pro) { + if (pro.pm2_env.status !== cst.ONLINE_STATUS) { + return false; + } + + if (pro.pm2_env.axm_options && pro.pm2_env.axm_options.pid) { + if (isNaN(pro.pm2_env.axm_options.pid)) { + return false; + } + } + + return true; +} + +function getProcessId(pro) { + var pid = pro.pid + + if (pro.pm2_env.axm_options && pro.pm2_env.axm_options.pid) { + pid = pro.pm2_env.axm_options.pid; + } + + return pid +} diff --git a/lib/God/Methods.js b/lib/God/Methods.js index e7b8e9fa..60fc31be 100644 --- a/lib/God/Methods.js +++ b/lib/God/Methods.js @@ -167,9 +167,9 @@ module.exports = function(God) { clearInterval(timer); return cb(null, true); } - console.log('pid=%d msg=failed to kill - retrying in 100ms', pid); + console.log('pid=%d msg=failed to kill - retrying in %dms', pid, pm2_env.kill_retry_time); return false; - }, 100); + }, pm2_env.kill_retry_time); timeout = setTimeout(function() { clearInterval(timer); @@ -247,21 +247,4 @@ module.exports = function(God) { pm2_env.unstable_restarts = 0; }; - /** - * Description - * @method forcegc - * @return - */ - God.forceGc = function(opts, cb) { - if (global.gc) { - global.gc(); - debug('Garbage collection triggered successfully'); - if (cb) cb(null, {success: true}); - } - else { - debug('Garbage collection failed'); - if (cb) cb(null, {success: false}); - } - }; - }; diff --git a/lib/God/Reload.js b/lib/God/Reload.js index d55cad1c..5472f3b4 100644 --- a/lib/God/Reload.js +++ b/lib/God/Reload.js @@ -174,7 +174,7 @@ function hardReload(God, id, wait_msg, cb) { module.exports = function(God) { /** - * GracefulReload + * Reload * @method softReloadProcessId * @param {} id * @param {} cb diff --git a/lib/Interactor/Cipher.js b/lib/Interactor/Cipher.js deleted file mode 100644 index 1204a3eb..00000000 --- a/lib/Interactor/Cipher.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -var crypto = require('crypto'); - -const CIPHER_ALGORITHM = 'aes256'; - -var Cipher = module.exports = {}; - -/** - * Description - * @method decipherMessage - * @param {} msg - * @return ret - */ -Cipher.decipherMessage = function(msg, key) { - var ret = {}; - - try { - var decipher = crypto.createDecipher(CIPHER_ALGORITHM, key); - var decipheredMessage = decipher.update(msg, 'hex', 'utf8'); - decipheredMessage += decipher.final('utf8'); - ret = JSON.parse(decipheredMessage); - } catch(e) { - return null; - } - - return ret; -} - -/** - * Description - * @method cipherMessage - * @param {} data - * @param {} key - * @return - */ -Cipher.cipherMessage = function(data, key) { - try { - var cipher = crypto.createCipher(CIPHER_ALGORITHM, key); - var cipheredData = cipher.update(data, 'utf8', 'hex'); - cipheredData += cipher.final('hex'); - return cipheredData; - } catch(e) { - return null; - } -} diff --git a/lib/Interactor/Daemon.js b/lib/Interactor/Daemon.js deleted file mode 100644 index 4bcf9c78..00000000 --- a/lib/Interactor/Daemon.js +++ /dev/null @@ -1,443 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -var fs = require('fs'); -var ipm2 = require('./pm2-interface.js'); -var rpc = require('pm2-axon-rpc'); -var axon = require('pm2-axon'); -var debug = require('debug')('interface:driver'); // Interface -var chalk = require('chalk'); -var Url = require('url'); -var os = require('os'); -var domain = require('domain'); -var fmt = require('../tools/fmt.js'); -var pkg = require('../../package.json'); -var PM2 = require('../..'); - -var cst = require('../../constants.js'); -var Cipher = require('./Cipher.js'); -var ReverseInteractor = require('./ReverseInteractor.js'); -var PushInteractor = require('./PushInteractor.js'); -var Utility = require('../Utility.js'); -var WatchDog = require('./WatchDog.js'); -var Conf = require('../Configuration.js'); -var HttpRequest = require('./HttpRequest.js'); -var InternalIP = require('./internal-ip.js'); - -global._pm2_password_protected = false; - -// Flag for log streaming status -global._logs = false; - -var Daemon = module.exports = { - connectToPM2 : function() { - return ipm2(); - }, - exit : function() { - var self = this; - - this.opts.pm2_instance.disconnect(function() { - console.log('Connection to PM2 via CLI closed'); - }); - - process.nextTick(function() { - try { - fs.unlinkSync(cst.INTERACTOR_RPC_PORT); - fs.unlinkSync(cst.INTERACTOR_PID_PATH); - } catch(e) {} - - if (self.opts.ipm2) - self.opts.ipm2.disconnect(); - - console.log('Exiting Interactor'); - - if (!this._rpc || !this._rpc.sock) - return process.exit(cst.ERROR_EXIT); - - this._rpc.sock.close(function() { - console.log('RPC closed - Interactor killed'); - process.exit(cst.SUCCESS_EXIT); - }); - }); - }, - activateRPC : function() { - console.log('Launching Interactor exposure'); - - var self = this; - var rep = axon.socket('rep'); - var daemon_server = new rpc.Server(rep); - var sock = rep.bind(cst.INTERACTOR_RPC_PORT); - - daemon_server.expose({ - kill : function(cb) { - console.log('Killing interactor'); - cb(null); - return Daemon.exit(); - }, - passwordSet : function(cb) { - global._pm2_password_protected = true; - return cb(null); - }, - getInfos : function(cb) { - if (self.opts && - self.opts.DAEMON_ACTIVE == true) - return cb(null, { - machine_name : self.opts.MACHINE_NAME, - public_key : self.opts.PUBLIC_KEY, - secret_key : self.opts.SECRET_KEY, - remote_host : cst.REMOTE_HOST, - remote_port : cst.REMOTE_PORT, - reverse_interaction : self.opts.REVERSE_INTERACT, - socket_path : cst.INTERACTOR_RPC_PORT, - pm2_home_monitored : cst.PM2_HOME - }); - else { - return cb(null); - } - } - }); - return daemon_server; - }, - formatMetada : function() { - var cpu, memory; - - var self = this; - - try { - cpu = os.cpus(); - memory = Math.floor(os.totalmem() / 1024 / 1024); - } catch(e) { - cpu = 0; - memory = 0; - }; - - var ciphered_data = Cipher.cipherMessage(JSON.stringify({ - MACHINE_NAME : this.opts.MACHINE_NAME, - PUBLIC_KEY : this.opts.PUBLIC_KEY, - PM2_VERSION : this.opts.PM2_VERSION, - RECYCLE : this.opts.RECYCLE || false, - MEMORY : memory, - HOSTNAME : os.hostname(), - CPUS : cpu.length - }), this.opts.SECRET_KEY); - - return ciphered_data; - }, - pingKeepAlive : function() { - var self = this; - - (function checkInternet() { - require('dns').lookup('google.com',function(err) { - if (err && (err.code == 'ENOTFOUND' || err.code == 'EAI_AGAIN')) { - if (self.opts._connection_is_up == true) - console.error('[CRITICAL] Internet is unreachable (via DNS lookup strategy)'); - self.opts._connection_is_up = false; - } else { - if (self.opts._connection_is_up == false) { - console.log('[TENTATIVE] Reactivating connection'); - PushInteractor.connectRemote(); - ReverseInteractor.reconnect(); - } - self.opts._connection_is_up = true; - } - setTimeout(checkInternet, 15000); - }); - })(); - }, - changeUrls : function(push_url, reverse) { - if (push_url) - PushInteractor.connectRemote(push_url); - if (reverse) - ReverseInteractor.changeUrl(reverse); - }, - refreshWorker : function() { - var self = this; - - function refreshMetadata() { - var ciphered_data = Daemon.formatMetada(); - - HttpRequest.post({ - url : self.opts.ROOT_URL, - port : self.opts.ROOT_PORT, - data : { - public_id : self.opts.PUBLIC_KEY, - data : ciphered_data - } - }, function(err, km_data) { - if (err) return console.error(err); - - /** protect against malformated data **/ - if (!km_data || - !km_data.endpoints || - !km_data.endpoints.push || - !km_data.endpoints.reverse) { - console.error('[CRITICAL] Malformated data received, skipping...'); - return false; - } - - /************************************** - * Urls has changed = update workers * - **************************************/ - - if ((Daemon.current_km_data.endpoints.push != km_data.endpoints.push) || - (Daemon.current_km_data.endpoints.reverse != km_data.endpoints.reverse)) { - self.changeUrls(km_data.endpoints.push, km_data.endpoints.reverse); - Daemon.current_km_data = km_data; - } - else { - debug('[REFRESH META] No need to update URL (same)', km_data); - } - return false; - }); - - }; - - // Refresh metadata every minutes - setInterval(function() { - refreshMetadata(); - }, 60000); - }, - validateData : function() { - var opts = {}; - - opts.MACHINE_NAME = process.env.PM2_MACHINE_NAME; - opts.PUBLIC_KEY = process.env.PM2_PUBLIC_KEY; - opts.SECRET_KEY = process.env.PM2_SECRET_KEY; - opts.RECYCLE = process.env.KM_RECYCLE ? JSON.parse(process.env.KM_RECYCLE) : false; - opts.REVERSE_INTERACT = JSON.parse(process.env.PM2_REVERSE_INTERACT); - opts.PM2_VERSION = pkg.version; - - if (!opts.MACHINE_NAME) { - console.error('You must provide a PM2_MACHINE_NAME environment variable'); - process.exit(cst.ERROR_EXIT); - } - else if (!opts.PUBLIC_KEY) { - console.error('You must provide a PM2_PUBLIC_KEY environment variable'); - process.exit(cst.ERROR_EXIT); - } - else if (!opts.SECRET_KEY) { - console.error('You must provide a PM2_SECRET_KEY environment variable'); - process.exit(cst.ERROR_EXIT); - } - return opts; - }, - welcome : function(cb) { - var self = this; - var ciphered_data = Daemon.formatMetada(); - - if (!ciphered_data) { - process.send({ - msg : 'Error while ciphering data', - error : true - }); - return process.exit(1); - } - - var retries = 0; - - function doWelcomeQuery(cb) { - HttpRequest.post({ - url : self.opts.ROOT_URL, - data : { - public_id : self.opts.PUBLIC_KEY, - data : ciphered_data - } - }, function(err, km_data) { - self.current_km_data = km_data; - if (err) { - console.error('Got error while connecting: %s', err.message || err); - - if (retries < 30) { - retries++; - - setTimeout(function() { - doWelcomeQuery(cb); - }, 200 * retries); - return false; - } - return cb(err); - } - - if (self.opts.RECYCLE) { - if (!km_data.name) { - console.error('Error no previous machine name for recycle option returned!'); - } - self.opts.MACHINE_NAME = km_data.name; - }; - - // For Human feedback - if (process.send) { - try { - process.send({ - error : false, - km_data : km_data, - online : true, - pid : process.pid, - machine_name : self.opts.MACHINE_NAME, - public_key : self.opts.PUBLIC_KEY, - secret_key : self.opts.SECRET_KEY, - reverse_interaction : self.opts.REVERSE_INTERACT - }); - } catch(e) { - // Just in case the CLI has been disconected - } - } - // Return get data - return cb(null, km_data); - }) - } - - doWelcomeQuery(function(err, meta) { - return cb(err, meta); - }); - }, - protectedStart : function() { - var self = this; - var d = domain.create(); - - d.once('error', function(err) { - fmt.sep(); - fmt.title('Agent global error caught'); - fmt.field('Time', new Date()); - console.error(err.message); - console.error(err.stack); - fmt.sep(); - - console.error('[Agent] Resurrecting'); - - var KMDaemon = require('../Interactor/InteractorDaemonizer'); - - KMDaemon.rescueStart(cst, function(err, dt) { - if (err) { - console.error('[Agent] Failed to rescue agent, error:'); - console.error(err.message || err); - process.exit(1); - } - console.log('[Agent] Rescued.'); - process.exit(0); - }); - }); - - d.run(function() { - self.start(); - }); - }, - start : function() { - var self = this; - - self.opts = self.validateData(); - self.opts.ipm2 = null; - self.opts.internal_ip = InternalIP(); - self.opts.pm2_instance = PM2; - self.opts._connection_is_up = true; - self.current_km_data = null; - - self.opts.pm2_instance.connect(function() { - console.log('Connected to PM2'); - }); - - self._rpc = self.activateRPC(); - - // Test mode #1 - if (cst.DEBUG) { - self.opts.ROOT_URL = '127.0.0.1'; - if (process.env.NODE_ENV == 'test') - self.opts.ROOT_PORT = 3400; - else - self.opts.ROOT_PORT = 3000; - } - else { - self.opts.ROOT_URL = cst.KEYMETRICS_ROOT_URL; - } - - if (Conf.getSync('pm2:passwd')) - global._pm2_password_protected = true; - - // Test mode #2 - if (process.env.NODE_ENV == 'local_test') { - self.opts.DAEMON_ACTIVE = true; - - self.opts.ipm2 = self.connectToPM2(); - - PushInteractor.start({ - url : 'http://127.0.0.1:4321', - conf : self.opts - }); - - ReverseInteractor.start({ - url : 'http://127.0.0.1:4322', - conf : self.opts - }); - if (process.send) - process.send({ - success : true, - debug : true - }); - return false; - } - - Daemon.welcome(function(err, km_data) { - if (err) { - if (process.send) - process.send({ - error : true, - msg : err.stack || err - }); - console.log(err.stack || err); - return Daemon.exit(); - } - - if (km_data.disabled == true) { - console.error('Interactor disabled'); - return Daemon.exit(); - } - if (km_data.pending == true) { - console.error('Interactor pending'); - return Daemon.exit(); - } - - if (km_data.active == true) { - self.opts.DAEMON_ACTIVE = true; - - self.opts.ipm2 = self.connectToPM2(); - - WatchDog.start({ - conf : self.opts - }); - - PushInteractor.start({ - url : km_data.endpoints.push, - conf : self.opts - }); - - if (self.opts.REVERSE_INTERACT == true) { - ReverseInteractor.start({ - url : km_data.endpoints.reverse, - conf : self.opts - }); - } - Daemon.refreshWorker(); - Daemon.pingKeepAlive(); - } - else { - console.log('Nothing to do, exiting'); - Daemon.exit(); - } - return false; - }); - } -}; - -/** - * MAIN - */ -if (require.main === module) { - console.log(chalk.cyan.bold('[Keymetrics.io]') + ' Launching agent'); - process.title = 'PM2: KM Agent (' + process.env.PM2_HOME + ')'; - - Utility.overrideConsole(); - Daemon.protectedStart(); -} diff --git a/lib/Interactor/Filter.js b/lib/Interactor/Filter.js deleted file mode 100644 index 52f44e6c..00000000 --- a/lib/Interactor/Filter.js +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -/** - * @file Filter process and system data to be sent to server - * @author Alexandre Strzelewicz - * @project Interface - */ - -var os = require('os'); - -var cpu_info = { - number : 0, - info : 'no-data' -}; - -try { - cpu_info = { - number : os.cpus().length, - info : os.cpus()[0].model - }; -} catch(e) { -} - -var SERVER_META = { - totalMem : os.totalmem(), - hostname : os.hostname(), - type : os.type(), - platform : os.platform(), - arch : os.arch() -}; - -var Filter = {}; - -Filter.getProcessID = function(machine_name, name, id) { - return machine_name + ':' + name + ':' + id; -}; - -Filter.machineSnapshot = function(processes, conf) { - if (!processes) return null; - - var filter_procs = []; - - processes.forEach(function(proc) { - if (proc.pm2_env.pm_id.toString().indexOf('_old_') == -1) - filter_procs.push({ - pid : proc.pid, - name : proc.pm2_env.name, - interpreter : proc.pm2_env.exec_interpreter, - restart_time : proc.pm2_env.restart_time, - created_at : proc.pm2_env.created_at, - exec_mode : proc.pm2_env.exec_mode, - watching : proc.pm2_env.watch, - pm_uptime : proc.pm2_env.pm_uptime, - status : proc.pm2_env.status, - pm_id : proc.pm2_env.pm_id, - - cpu : Math.floor(proc.monit.cpu) || 0, - memory : Math.floor(proc.monit.memory) || 0, - - versioning : proc.pm2_env.versioning || null, - - node_env : proc.pm2_env.NODE_ENV || null, - - axm_actions : proc.pm2_env.axm_actions || [], - axm_monitor : proc.pm2_env.axm_monitor || {}, - axm_options : proc.pm2_env.axm_options || {}, - axm_dynamic : proc.pm2_env.dynamic || {} - }); - }); - - var node_version = process.version || ''; - - if (node_version != '') { - if (node_version.indexOf('v1.') === 0 || node_version.indexOf('v2.') === 0 || node_version.indexOf('v3.') === 0) - node_version = 'iojs ' + node_version; - } - var username = process.env.SUDO_USER || process.env.C9_USER || process.env.LOGNAME || - process.env.USER || process.env.LNAME || process.env.USERNAME; - - return { - process : filter_procs, - server : { - loadavg : os.loadavg(), - total_mem : SERVER_META.totalMem, - free_mem : os.freemem(), - cpu : cpu_info, - hostname : SERVER_META.hostname, - uptime : os.uptime(), - type : SERVER_META.type, - platform : SERVER_META.platform, - arch : SERVER_META.arch, - user : username, - interaction : conf.REVERSE_INTERACT, - pm2_version : conf.PM2_VERSION, - node_version : node_version - } - }; -}; - -Filter.monitoring = function(processes, conf) { - if (!processes) return null; - - var filter_procs = {}; - - processes.forEach(function(proc) { - filter_procs[Filter.getProcessID(conf.MACHINE_NAME, proc.pm2_env.name,proc.pm2_env.pm_id)] = [ - Math.floor(proc.monit.cpu), - Math.floor(proc.monit.memory) - ]; - }); - - return { - loadavg : os.loadavg(), - total_mem : SERVER_META.totalMem, - free_mem : os.freemem(), - processes : filter_procs - }; -}; - -module.exports = Filter; diff --git a/lib/Interactor/HttpRequest.js b/lib/Interactor/HttpRequest.js deleted file mode 100644 index 1ade6cc9..00000000 --- a/lib/Interactor/HttpRequest.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -var http = require('http'); -var https = require('https'); -var url = require('url') -var debug = require('debug')('interface:http'); - -var HttpRequest = module.exports = {}; - -HttpRequest.post = function(opts, cb) { - if (!(opts.data && opts.url)) { - return cb({ - msg: 'missing parameters', - port: opts.port, - data: opts.data, - url: opts.url - }) - } - - if (!opts.port) { - var parsed = url.parse(opts.url) - if (parsed.hostname && parsed.port) { - opts.port = parseInt(parsed.port) - opts.url = parsed.hostname - } else { - opts.port = 443 - } - } - - var options = { - hostname: opts.url, - path: '/api/node/verifyPM2', - method: 'POST', - port: opts.port, - rejectUnauthorized: false, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(JSON.stringify(opts.data)) - } - } - - var client = (opts.port === 443) ? https : http; - - var req = client.request(options, function(res){ - var dt = ''; - - res.on('data', function (chunk) { - dt += chunk; - }); - - res.on('end',function(){ - try { - cb(null, JSON.parse(dt)); - } catch(e) { - cb(e); - } - }); - - res.on('error', function(e){ - cb(e); - }); - }); - - req.on('socket', function (socket) { - /** - * Configure request timeout - */ - socket.setTimeout(7000); - socket.on('timeout', function() { - debug('Connection timeout when retrieveing PM2 metadata', options); - req.abort(); - }); - }); - - req.on('error', function(e) { - cb(e); - }); - - req.write(JSON.stringify(opts.data)); - - req.end(); -}; diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js deleted file mode 100644 index 2d87715f..00000000 --- a/lib/Interactor/InteractorDaemonizer.js +++ /dev/null @@ -1,535 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -'use strict'; - -var debug = require('debug')('pm2:interface:daemon'); -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var rpc = require('pm2-axon-rpc'); -var axon = require('pm2-axon'); -var chalk = require('chalk'); -var os = require('os'); -var cst = require('../../constants.js'); -var Common = require('../Common'); -var json5 = require('../tools/json5.js'); -var UX = require('../API/CliUx.js'); - -var InteractorDaemonizer = module.exports = {}; - -InteractorDaemonizer.rpc = {}; - -/** - * Description - * @method ping - * @param {} cb - * @return - */ -InteractorDaemonizer.ping = function(conf, cb) { - var req = axon.socket('req'); - var client = new rpc.Client(req); - - debug('[PING INTERACTOR] Trying to connect to Interactor daemon'); - - client.sock.once('reconnect attempt', function() { - client.sock.close(); - debug('Interactor Daemon not launched'); - return cb(false); - }); - - client.sock.once('connect', function() { - client.sock.once('close', function() { - return cb(true); - }); - client.sock.close(); - debug('Interactor Daemon alive'); - }); - - client.sock.once('error', function(e) { - if (e.code == 'EACCES') { - fs.stat(conf.INTERACTOR_RPC_PORT, function(e, stats) { - if (stats.uid === 0) { - console.error(conf.PREFIX_MSG_ERR + 'Permission denied, activate current user:'); - console.log(chalk.bold('$sudo chown ' + process.env.USER + ':' + process.env.USER + ' ' + conf.INTERACTOR_RPC_PORT)); - return process.exit(1); - } - }); - } - }); - - req.connect(conf.INTERACTOR_RPC_PORT); -}; - -InteractorDaemonizer.killInteractorDaemon = function(conf, cb) { - process.env.PM2_INTERACTOR_PROCESSING = true; - - debug('Killing interactor #1 ping'); - InteractorDaemonizer.ping(conf, function(online) { - debug('Interactor online', online); - - if (!online) { - if (!cb) Common.printError('Interactor not launched'); - - return cb(new Error('Interactor not launched')); - } - - InteractorDaemonizer.launchRPC(conf, function(err, data) { - if (err) { - setTimeout(function() { - InteractorDaemonizer.disconnectRPC(cb); - }, 100); - return false; - } - InteractorDaemonizer.rpc.kill(function(err) { - if (err) Common.printError(err); - setTimeout(function() { - InteractorDaemonizer.disconnectRPC(cb); - }, 100); - }); - return false; - }); - return false; - }); -}; - -/** - * Description - * @method launchRPC - * @param {} cb - * @return - */ -InteractorDaemonizer.launchRPC = function(conf, cb) { - var self = this; - var req = axon.socket('req'); - this.client = new rpc.Client(req); - - debug('Generating methods'); - - /** - * Description - * @method generateMethods - * @param {} cb - * @return - */ - var generateMethods = function(cb) { - self.client.methods(function(err, methods) { - Object.keys(methods).forEach(function(key) { - var method_signature = methods[key]; - debug('+-- Creating %s method', method_signature.name); - - (function(name) { - /** - * Description - * @method name - * @return - */ - self.rpc[name] = function() { - var args = Array.prototype.slice.call(arguments); - args.unshift(name); - self.client.call.apply(self.client, args); - }; - })(method_signature.name); - - }); - return cb(); - }); - }; - - this.client.sock.once('reconnect attempt', function(e) { - self.client.sock.removeAllListeners(); - return cb({success:false, msg:'reconnect attempt'}); - }); - - this.client.sock.once('error', function(e) { - console.error('Error in error catch all on Interactor'); - console.error(e.stack || e); - }); - - this.client.sock.once('connect', function() { - self.client.sock.removeAllListeners(); - generateMethods(function() { - debug('Methods generated'); - cb(null, {success:true}); - }); - }); - - this.client_sock = req.connect(conf.INTERACTOR_RPC_PORT); -}; - -/** - * Description - * @method launchOrAttach - * @param {} secret_key - * @param {} public_key - * @param {} machine_name - * @param {} cb - * @return - */ -function launchOrAttach(conf, infos, cb) { - InteractorDaemonizer.ping(conf, function(online) { - if (online) { - debug('Interactor online, restarting it...'); - InteractorDaemonizer.launchRPC(conf, function() { - InteractorDaemonizer.rpc.kill(function(err) { - daemonize(conf, infos, function(err, msg, proc) { - return cb(err, msg, proc); - }); - }); - }); - } - else { - debug('Interactor offline, launching it...'); - daemonize(conf, infos, function(err, msg, proc) { - return cb(err, msg, proc); - }); - } - return false; - }); -}; - -/** - * Description - * @method daemonize - * @param {} secret_key - * @param {} public_key - * @param {} machine_name - * @param {} cb - * @return - */ -var daemonize = function(conf, infos, cb) { - var InteractorJS = path.resolve(path.dirname(module.filename), 'Daemon.js'); - - var out = null; - var err = null; - - if (process.env.TRAVIS || process.env.NODE_ENV == 'local_test') { - // Redirect PM2 internal err and out - // to STDERR STDOUT when running with Travis - out = 1; - err = 2; - } - else { - out = fs.openSync(conf.INTERACTOR_LOG_FILE_PATH, 'a'); - err = fs.openSync(conf.INTERACTOR_LOG_FILE_PATH, 'a'); - } - - var child = require('child_process').spawn('node', [InteractorJS], { - silent : false, - detached : true, - cwd : process.cwd(), - env : util._extend({ - PM2_HOME : conf.PM2_HOME, - PM2_MACHINE_NAME : infos.machine_name, - PM2_SECRET_KEY : infos.secret_key, - PM2_PUBLIC_KEY : infos.public_key, - PM2_REVERSE_INTERACT : infos.reverse_interact, - KEYMETRICS_NODE : infos.info_node - }, process.env), - stdio : ['ipc', out, err] - }); - - console.log('[KM] Connecting'); - - fs.writeFileSync(conf.INTERACTOR_PID_PATH, child.pid); - - function onError(msg) { - debug('Error when launching Interactor, please check the agent logs'); - return cb(msg); - } - - child.once('error', onError); - - child.unref(); - - var t = setTimeout(function() { - Common.printOut(cst.PREFIX_MSG_WARNING + ' Not managed to connect to Keymetrics, retrying in background. (check %s)', cst.INTERACTOR_LOG_FILE_PATH); - child.removeAllListeners('message'); - child.removeAllListeners('error'); - child.disconnect(); - return cb(null, {}, child); - }, 7000); - - child.once('message', function(msg) { - clearTimeout(t); - debug('Interactor daemon launched', msg); - - if (msg.debug) { - return cb(null, msg, child); - } - - child.removeAllListeners('error'); - child.disconnect(); - - /***************** - * Error messages - */ - if (msg.error == true) { - console.log(chalk.red('[Keymetrics.io][ERROR]'), msg.msg); - console.log(chalk.cyan('[Keymetrics.io]') + ' Contact support contact@keymetrics.io and send us the error message'); - return cb(msg); - } - - if (msg.km_data.disabled == true) { - console.log(chalk.cyan('[Keymetrics.io]') + ' Server DISABLED BY ADMINISTRATION contact support contact@keymetrics.io with reference to your public and secret keys)'); - return cb(msg); - } - - if (msg.km_data.error == true) { - console.log(chalk.red('[Keymetrics.io][ERROR]') + ' ' + msg.km_data.msg + ' (Public: %s) (Secret: %s) (Machine name: %s)', msg.public_key, msg.secret_key, msg.machine_name); - return cb(msg); - } - - else if (msg.km_data.active == false && msg.km_data.pending == true) { - console.log(chalk.red('[Keymetrics.io]') + ' ' + chalk.bold.red('Agent PENDING') + ' - Web Access: https://app.keymetrics.io/'); - console.log(chalk.red('[Keymetrics.io]') + ' You must upgrade your bucket in order to monitor more servers.'); - - return cb(msg); - } - - if (msg.km_data.active == true) { - console.log(chalk.green.bold('[Monitoring Enabled]') + ' Dashboard access: https://app.keymetrics.io/#/r/%s', msg.public_key); - return cb(null, msg, child); - } - - return cb(null, msg, child); - }); - -}; - -InteractorDaemonizer.update = function(conf, cb) { - InteractorDaemonizer.ping(conf, function(online) { - if (!online) { - Common.printError('Interactor not launched'); - return cb(new Error('Interactor not launched')); - } - InteractorDaemonizer.launchRPC(conf, function() { - InteractorDaemonizer.rpc.kill(function(err) { - if (err) { - Common.printError(err); - return cb(new Error(err)); - } - Common.printOut('Interactor successfully killed'); - setTimeout(function() { - InteractorDaemonizer.launchAndInteract(conf, {}, function() { - return cb(null, {msg : 'Daemon launched'}); - }); - }, 500); - }); - }); - return false; - }); -}; - -/** - * Get/Update/Merge agent configuration - * @param {object} _infos - */ -InteractorDaemonizer.getOrSetConf = function(conf, infos, cb) { - var reverse_interact = true; - var version_management_active = true; - var version_management_password = null; - var secret_key; - var public_key; - var machine_name; - var info_node; - var new_connection = false; - - // 1# Load configuration file - try { - var interaction_conf = json5.parse(fs.readFileSync(conf.INTERACTION_CONF)); - - public_key = interaction_conf.public_key; - machine_name = interaction_conf.machine_name; - secret_key = interaction_conf.secret_key; - info_node = interaction_conf.info_node; - - reverse_interact = interaction_conf.reverse_interact || true; - - if (interaction_conf.version_management) { - version_management_password = interaction_conf.version_management.password || version_management_password; - version_management_active = interaction_conf.version_management.active || version_management_active; - } - } catch (e) { - debug('Interaction file does not exists'); - } - - // 2# Override with passed informations - if (infos) { - if (infos.secret_key) - secret_key = infos.secret_key; - - if (infos.public_key) - public_key = infos.public_key; - - if (infos.machine_name) - machine_name = infos.machine_name; - - if (infos.info_node) - info_node = infos.info_node; - - new_connection = true; - } - - // 3# Override with environment variables (highest-priority conf) - if (process.env.PM2_SECRET_KEY || process.env.KEYMETRICS_SECRET) - secret_key = process.env.PM2_SECRET_KEY || process.env.KEYMETRICS_SECRET; - - if (process.env.PM2_PUBLIC_KEY || process.env.KEYMETRICS_PUBLIC) - public_key = process.env.PM2_PUBLIC_KEY || process.env.KEYMETRICS_PUBLIC; - - if (new_connection && info_node == null) - info_node = process.env.KEYMETRICS_NODE || cst.KEYMETRICS_ROOT_URL; - - if (!info_node) - info_node = cst.KEYMETRICS_ROOT_URL; - - if (!secret_key) - return cb(new Error('secret key is not defined')); - - if (!public_key) - return cb(new Error('public key is not defined')); - - if (!machine_name) - machine_name = os.hostname() + '-' + require('crypto').randomBytes(4).toString('hex'); - - /** - * Write new data to configuration file - */ - try { - var new_interaction_conf = { - secret_key : secret_key, - public_key : public_key, - machine_name : machine_name, - reverse_interact : reverse_interact, - info_node : info_node, - version_management : { - active : version_management_active, - password : version_management_password - } - }; - fs.writeFileSync(conf.INTERACTION_CONF, json5.stringify(new_interaction_conf, null, 4)); - } catch(e) { - console.error('Error when writting configuration file %s', conf.INTERACTION_CONF); - return cb(e); - } - - // Don't block the event loop - process.nextTick(function() { - cb(null, new_interaction_conf); - }); -}; - -InteractorDaemonizer.disconnectRPC = function(cb) { - if (!InteractorDaemonizer.client_sock || - !InteractorDaemonizer.client_sock.close) - return cb(null, { - success : false, - msg : 'RPC connection to Interactor Daemon is not launched' - }); - - if (InteractorDaemonizer.client_sock.connected === false || - InteractorDaemonizer.client_sock.closing === true) { - return cb(null, { - success : false, - msg : 'RPC closed' - }); - } - - try { - var timer; - - debug('Closing RPC INTERACTOR'); - - InteractorDaemonizer.client_sock.once('close', function() { - debug('RPC INTERACTOR cleanly closed'); - clearTimeout(timer); - return cb ? cb(null, {success:true}) : false; - }); - - timer = setTimeout(function() { - if (InteractorDaemonizer.client_sock.destroy) - InteractorDaemonizer.client_sock.destroy(); - return cb ? cb(null, {success:true}) : false; - }, 200); - - InteractorDaemonizer.client_sock.close(); - } catch(e) { - debug('Error while closing RPC INTERACTOR', e.stack || e); - return cb ? cb(e.stack || e) : false; - } - return false; -}; - -InteractorDaemonizer.rescueStart = function(conf, cb) { - InteractorDaemonizer.getOrSetConf(conf, null, function(err, infos) { - if (err || !infos) { - return cb(err); - } - - console.log(chalk.cyan('[Keymetrics.io]') + ' Using (Public key: %s) (Private key: %s)', infos.public_key, infos.secret_key); - - daemonize(conf, infos, function(err, msg, proc) { - return cb(err, msg, proc); - }); - }); -}; - -InteractorDaemonizer.launchAndInteract = function(conf, opts, cb) { - // For Watchdog - if (process.env.PM2_AGENT_ONLINE) { - return process.nextTick(cb); - } - - process.env.PM2_INTERACTOR_PROCESSING = true; - - this.getOrSetConf(conf, opts, function(err, data) { - if (err || !data) { - return cb(err); - } - - //console.log(chalk.cyan('[Keymetrics.io]') + ' Using (Public key: %s) (Private key: %s)', data.public_key, data.secret_key); - - launchOrAttach(conf, data, function(err, msg, proc) { - if (err) - return cb(err); - return cb(null, msg, proc); - }); - return false; - }); -}; - -/** - * Description - * @method getInteractInfo - * @param {} cb - * @return - */ -InteractorDaemonizer.getInteractInfo = function(conf, cb) { - debug('Getting interaction info'); - if (process.env.PM2_NO_INTERACTION) return; - InteractorDaemonizer.ping(conf, function(online) { - if (!online) { - return cb(new Error('Interactor is offline')); - } - InteractorDaemonizer.launchRPC(conf, function() { - InteractorDaemonizer.rpc.getInfos(function(err, infos) { - if (err) - return cb(err); - - // Avoid general CLI to interfere with Keymetrics CLI commands - if (process.env.PM2_INTERACTOR_PROCESSING) - return cb(null, infos); - - InteractorDaemonizer.disconnectRPC(function() { - return cb(null, infos); - }); - return false; - }); - }); - return false; - }); -}; diff --git a/lib/Interactor/Password.js b/lib/Interactor/Password.js deleted file mode 100644 index 86b1fdd7..00000000 --- a/lib/Interactor/Password.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ -var crypto = require('crypto'); - -var saltChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; -var saltCharsCount = saltChars.length; - -function generateSalt(len) { - if (typeof len != 'number' || len <= 0 || len !== parseInt(len, 10)) throw new Error('Invalid salt length'); - if (crypto.randomBytes) { - return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').substring(0, len); - } else { - for (var i = 0, salt = ''; i < len; i++) { - salt += saltChars.charAt(Math.floor(Math.random() * saltCharsCount)); - } - return salt; - } -} - -function generateHash(algorithm, salt, password, iterations) { - iterations = iterations || 1; - try { - var hash = password; - for(var i=0; i -1)) - return false; - - if (Object.keys(self.monitored_processes).length > 0 && - !self.monitored_processes[packet.process.pm_id]) - return false; - - // keep log in a buffer - if (event.match(/^log:/)) { - if (!LOGS_BUFFER[packet.process.name]) { - LOGS_BUFFER[packet.process.name] = []; - } - // push the log data - LOGS_BUFFER[packet.process.name].push(packet.data); - // delete the last one if too long - if (LOGS_BUFFER[packet.process.name].length >= cst.LOGS_BUFFER_SIZE) { - LOGS_BUFFER[packet.process.name].pop(); - } - - // don't send if not asked - if (!global._logs) return false; - } - - // attach additional info on exception - if (event === 'process:exception') { - packet.data.last_logs = LOGS_BUFFER[packet.process.name]; - packet.data = self.stackParser.attachContext(packet.data); - } - - /** - * This is a heapdump action - */ - if (event == 'axm:reply' && packet.data && packet.data.return && (packet.data.return.heapdump || packet.data.return.cpuprofile)) { - PushInteractor.sendFile(packet); - return false; - } - - if (event == 'human:event') { - packet.name = packet.data.__name + ''; - delete packet.data.__name; - } - - if (!packet.process) - return console.error('No process field [%s]', event); - - /** - * Process specific messages - * -- Reformat raw output of pm2-interface - */ - packet.process = { - pm_id : packet.process.pm_id, - name : packet.process.name, - rev : packet.process.rev || ((packet.process.versioning && packet.process.versioning.revision) ? packet.process.versioning.revision : null), - server: PushInteractor.conf.MACHINE_NAME - }; - - // agregate transaction data before sending them - if (event.indexOf('axm:trace') > -1) - return self.aggregator.aggregate(packet); - - if (event.match(/^log:/)) { - packet.log_type = event.split(':')[1]; - event = 'logs'; - } - return PushInteractor.bufferData(event, packet); - }); - }, - resetPacket : function() { - var self = this; - - this._packet = { - 'server_name' : self.conf.MACHINE_NAME, - 'status' : {}, - 'monitoring' : {} - }; - }, - bufferData : function(event, packet) { - var self = this; - var logs_limit_size = 1024 * 50; - - // if (Object.keys(self._packet).indexOf(event) == -1) { - // return console.error('SKIP unknown field name [%s]', event); - // } - debug('Buffering one more event %s', event); - - if (!(event in self._packet)) - self._packet[event] = []; - - if (packet.process && !packet.server) { - if (event === 'logs' - && (JSON.stringify(self._packet[event]).length > logs_limit_size - || self._packet[event].length > 100)) - return console.error('Logs packet larger than 50KB limit'); - - self._packet[event].push(packet); - } - else { - console.error('Got packet without any process'); - } - return false; - }, - preparePacket : function(cb) { - var self = this; - - this.ipm2.rpc.getMonitorData({}, function(err, processes) { - if (!processes) - return console.error('Cant access to getMonitorData RPC PM2 method'); - - processes = processes.filter(function (proc) { - return proc.pm2_env._km_monitored !== false; - }); - - var ret = null; - - if ((ret = Filter.monitoring(processes, PushInteractor.conf))) { - self._packet['monitoring'] = ret; - } - - if ((ret = Filter.machineSnapshot(processes, PushInteractor.conf))) { - self._packet['status'] = { - data : ret, - server_name : self.conf.MACHINE_NAME, - internal_ip : self.conf.internal_ip, - protected : global._pm2_password_protected, - rev_con : self.conf.rev_con - }; - } - - return cb ? cb(null, ret) : false; - }); - }, - /** - * Description - * @method send_data - * @return - */ - sendData : function() { - var self = this; - - if (self.socket.client && - self.socket.client.socks[0] && - self.socket.client.socks[0].bufferSize > 290000) { - self.resetPacket(); - self._reconnect_counter++; - console.log('Buffer size too high (%d), stopping buffering and sending', self.socket.client.socks[0].bufferSize); - - if (self._reconnect_counter > 20) { - console.log('[PUSH] Forcing reconnection'); - self._reconnect_counter = 0; - self.socket.reconnect(); - } - return false; - } - - this.preparePacket(function() { - var data = {}; - - if (process.env.NODE_ENV && - (process.env.NODE_ENV == 'test' || process.env.NODE_ENV == 'local_test')) { - data = { - public_key : PushInteractor.conf.PUBLIC_KEY, - sent_at : Utility.getDate(), - data : self._packet - }; - } - else { - var cipheredData = Cipher.cipherMessage(JSON.stringify(self._packet), - PushInteractor.conf.SECRET_KEY); - data = { - public_key : self.conf.PUBLIC_KEY, - sent_at : Utility.getDate(), - data : cipheredData - }; - } - - var str = JSON.stringify(data); - var t1 = new Date(); - - self.resetPacket(); - - if (!self.socket) return false; - - self.socket.client.sendv2(str, function() { - var duration_sec = (new Date() - t1) / 1000; - debugInfo('Time to flush data %ds (buffer size %d)', duration_sec); - - if (duration_sec > 1) - console.info('[WARN] Time to send data over TCP took %dseconds!', duration_sec); - - data = null; - str = null; - }); - }); - } -}; diff --git a/lib/Interactor/RemoteActions/CustomActions.js b/lib/Interactor/RemoteActions/CustomActions.js deleted file mode 100644 index 69749397..00000000 --- a/lib/Interactor/RemoteActions/CustomActions.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -var debug = require('debug')('interface:driver'); -var Cipher = require('../Cipher.js'); - -var CustomActions = module.exports = { - /** - * Method to trigger custom actions (axm actions) - */ - axmCustomActions : function() { - var self = this; - - this.socket.data('trigger:action', function(raw_msg) { - var msg = {}; - - if (process.env.NODE_ENV && (process.env.NODE_ENV == 'test' || - process.env.NODE_ENV == 'local_test')) - msg = raw_msg; - else - msg = Cipher.decipherMessage(raw_msg, self.conf.SECRET_KEY); - - if (!msg) return console.error('Error while receiving message! #axmCustomActions'); - - console.log('New remote action %s triggered for process %s', msg.action_name, msg.process_id); - self.pm2_instance.msgProcess({ - id : msg.process_id, - msg : msg.action_name, - opts: msg.opts || null - }, function(err, data) { - if (err) { - return self.socket.send('trigger:action:failure', { - success : false, - err : err.message, - id : msg.process_id, - action_name : msg.action_name - }); - } - console.log('[REVERSE INTERACTOR] Message received from AXM for proc_id : %s and action name %s', - msg.process_id, msg.action_name); - - return self.socket.send('trigger:action:success', { - success : true, - id : msg.process_id, - action_name : msg.action_name - }); - }); - }); - - this.socket.data('trigger:scoped_action', function(raw_msg) { - var msg = {}; - - if (process.env.NODE_ENV && (process.env.NODE_ENV == 'test' || - process.env.NODE_ENV == 'local_test')) - msg = raw_msg; - else - msg = Cipher.decipherMessage(raw_msg, self.conf.SECRET_KEY); - - if (!msg) return console.error('Error while receiving message! #axmCustomActions'); - - console.log('New SCOPED action %s triggered for process %s', msg.action_name, msg.process.pm_id); - - self.pm2_instance.msgProcess({ - id : msg.process.pm_id, - action_name : msg.action_name, - msg : msg.action_name, - opts : msg.options || {}, - uuid : msg.uuid - }, function(err, data) { - if (err) { - return self.socket.send('trigger:action:failure', { - success : false, - err : err.message, - id : msg.process.pm_id, - action_name : msg.action_name - }); - } - console.log('[REVERSE INTERACTOR] Message received from AXM for proc_id : %s and action name %s', - msg.process_id, msg.action_name); - - return self.socket.send('trigger:action:success', { - success : true, - id : msg.process.pm_id, - action_name : msg.action_name - }); - }); - }); - } -}; diff --git a/lib/Interactor/RemoteActions/Pm2Actions.js b/lib/Interactor/RemoteActions/Pm2Actions.js deleted file mode 100644 index a6de7c61..00000000 --- a/lib/Interactor/RemoteActions/Pm2Actions.js +++ /dev/null @@ -1,345 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -var debug = require('debug')('interface:driver'); -var Url = require('url'); -var Cipher = require('../Cipher.js'); -var PushInteractor = require('../PushInteractor'); -var Conf = require('../../Configuration.js'); -var Password = require('../Password.js'); - -/** - * Allowed remote PM2 methods - * with options - * - password_required : force to pass a password in parameter - * - password_optional : if a password is set, force it - * - lock : enable the locking system (block parallel commands) - */ -var PM2_REMOTE_METHOD_ALLOWED = { - 'restart' : {}, - 'reload' : {}, - 'gracefulReload' : {}, - 'reset' : {}, - 'scale' : {}, - - 'install' : { password_required : true }, - 'uninstall' : { password_required : true }, - 'stop' : { password_required : true }, - 'delete' : { password_required : true }, - 'set' : {}, - 'multiset' : {}, - 'deepUpdate' : { password_required : true }, - - 'pullAndRestart' : { password_optional : true }, - 'forward' : { password_optional : true }, - 'backward' : { password_optional : true }, - - 'startLogging' : {}, - 'stopLogging' : {}, - - 'resetTransactionCache': {}, - 'resetFileCache': {}, - - // This is just for testing purproses - 'ping' : { password_required : true } -}; - -var Pm2Actions = module.exports = { - /** - * Methods to trigger PM2 actions from remote - */ - pm2Actions : function() { - var self = this; - - function executionBox(msg, cb) { - /** - * Exemple - * msg = { - * method_name : 'restart', - * parameters : {} - * } - */ - console.log('PM2 action from remote triggered "pm2 %s %j"', - msg.method_name, - msg.parameters); - - var method_name = JSON.parse(JSON.stringify(msg.method_name)); - - var parameters = ''; - - try { - parameters = JSON.parse(JSON.stringify(msg.parameters)); - } - catch(e) { - console.error(e.stack || e); - parameters = msg.parameters; - } - - if (!method_name) { - console.error('no method name'); - return cb(new Error('no method name defined')); - } - - if (!PM2_REMOTE_METHOD_ALLOWED[method_name]) { - console.error('method %s not allowed', method_name); - return cb(new Error('method ' + method_name + ' not allowed')); - } - - if (method_name === 'startLogging') { - global._logs = true; - // Stop streaming logs automatically after timeout - setTimeout(function() { - global._logs = false; - }, 120000); - return cb(null, 'Log streaming enabled'); - } else if (method_name === 'stopLogging') { - global._logs = false; - return cb(null, 'Log streaming disabled'); - } else if (method_name === 'resetTransactionCache') { - PushInteractor.aggregator.clearData(); - return cb(null, 'Transaction cache has beem reset'); - } else if (method_name === 'resetFileCache') { - PushInteractor.cache.reset(); - return cb(null, 'File cache has beem reset'); - } - - self.pm2_instance.remote(method_name, parameters, cb); - return false; - } - - function sendBackResult(data) { - self.socket.send('trigger:pm2:result', data); - }; - - this.socket.data('trigger:pm2:action', function(raw_msg) { - var d = require('domain').create(); - - var msg = {}; - - /** - * Uncipher Data - */ - if (process.env.NODE_ENV && - (process.env.NODE_ENV == 'test' || - process.env.NODE_ENV == 'local_test')) - msg = raw_msg; - else - msg = Cipher.decipherMessage(raw_msg, self.conf.SECRET_KEY); - - d.on('error', function(e) { - console.error('Error caught in domain'); - console.error(e.stack || e); - - /** - * Send error back to - */ - sendBackResult({ - ret : { - err : e, - data : null - }, - meta : { - method_name : msg.method_name, - app_name : msg.parameters.name, - machine_name : self.conf.MACHINE_NAME, - public_key : self.conf.PUBLIC_KEY - } - }); - }); - - d.run(function() { - if (!msg) - throw new Error('Wrong SECRET KEY to uncipher package'); - - /** - * Execute command - */ - executionBox(msg, function(err, data) { - if (err) console.error(err.stack || JSON.stringify(err)); - - /** - * Send back the result - */ - sendBackResult({ - ret : { - err : err, - data : data || null - }, - meta : { - method_name : msg.method_name, - app_name : msg.parameters.name, - machine_name : self.conf.MACHINE_NAME, - public_key : self.conf.PUBLIC_KEY - } - }); - }); - }); - - }); - }, - - /**************************************************** - * - * - * Scoped PM2 Actions with streaming and multi args - * - * - ****************************************************/ - pm2ScopedActions : function() { - var self = this; - - this.socket.data('trigger:pm2:scoped:action', function(raw_msg) { - var msg = {}; - - if (process.env.NODE_ENV && (process.env.NODE_ENV == 'test' || - process.env.NODE_ENV == 'local_test')) - msg = raw_msg; - else { - /** - * Uncipher Data - */ - msg = Cipher.decipherMessage(raw_msg, self.conf.SECRET_KEY); - } - - if (!msg.uuid || - !msg.action_name) { - console.error('PM2 Scoped: Parameter missing!'); - return sendEvent('pm2:scoped:error', { - at : Date.now(), - out : 'Parameter missing', - msg : msg.uuid || null - }); - } - - sendEvent('pm2:scoped:stream', { - at : Date.now(), - out : 'Action ' + msg.action_name + ' received', - uuid : msg.uuid - }); - - executionBox(msg, function(err, data) { - if (err) { - console.error(err.stack || err); - return sendEvent('pm2:scoped:error', { - at : Date.now(), - out : err.stack || err, - uuid : msg.uuid - }); - } - return sendEvent('pm2:scoped:end', { - at : Date.now(), - out : data, - uuid : msg.uuid - }); - }); - }); - - /** - * Compact event in Push Interactor *pipe* - */ - function sendEvent(event, data) { - var packet = { - at : Date.now(), - data : { - data : data.out, - uuid : data.uuid - } - }; - - if (!PushInteractor._packet[event]) - PushInteractor._packet[event] = []; - - PushInteractor._packet[event].push(packet); - - if (process.env.NODE_ENV == 'local_test') - process.send({event : event, data : data}); - }; - - /** - * Processing - */ - function executionBox(msg, cb) { - var action_name = msg.action_name; - var opts = msg.options; - - if (!PM2_REMOTE_METHOD_ALLOWED[action_name]) { - console.error('method %s not allowed', action_name); - return cb(new Error('method ' + action_name + ' not allowed')); - } - - var action_conf = PM2_REMOTE_METHOD_ALLOWED[action_name]; - - /** - * Password checking - */ - if (action_conf.password_required === true) { - if (!msg.password) { - console.error('Missing password in query'); - return cb('Missing password in query'); - } - - var passwd = Conf.getSync('pm2:passwd'); - - if (passwd === null) { - console.error('Password at PM2 level is missing'); - return cb('Password at PM2 level is missing please set password via pm2 set pm2:passwd '); - } - - if (Password.verify(msg.password, passwd) != true) { - console.error('Password does not match'); - return cb('Password does not match'); - } - } - - if (action_conf.lock === false) - opts.lock = false; - - /** - * Fork the remote action in another process - * so we can catch the stdout/stderr and emit it - */ - var fork = require('child_process').fork; - - process.env.fork_params = JSON.stringify({ action : action_name, opts : opts}); - - console.log('Executing: pm2 %s %s', action_name, opts.args ? opts.args.join(' ') : ''); - - var app = fork(__dirname + '/ScopedExecution.js', [], { - silent : true - }); - - app.stdout.on('data', function(dt) { - console.log(dt.toString()); - sendEvent('pm2:scoped:stream', { - at : Date.now(), - out : dt.toString(), - uuid : msg.uuid - }); - }); - - app.once('error', function(dt) { - console.error('Error got?', dt); - sendEvent('pm2:scoped:error', { - at : Date.now(), - out : 'Shit happening ' + JSON.stringify(dt), - msg : msg.uuid - }); - }); - - app.on('message', function(dt) { - var ret = JSON.parse(dt); - if (ret.isFinished != true) return false; - - console.log('Action %s finished (err= %s)', - action_name, ret.err); - return cb(ret.err, ret.dt); - }); - - return false; - } - - } -}; diff --git a/lib/Interactor/RemoteActions/ScopedExecution.js b/lib/Interactor/RemoteActions/ScopedExecution.js deleted file mode 100644 index 08c962e3..00000000 --- a/lib/Interactor/RemoteActions/ScopedExecution.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -var pm2 = require('../../..'); -var domain = require('domain'); -var Utility = require('../../Utility.js'); - -var d = domain.create(); - -d.once('error', function(err) { - process.send(JSON.stringify({err: err.stack, isFinished : true})); -}); - -d.run(function() { - var params = JSON.parse(process.env.fork_params); - - console.log('Executing: pm2 %s %s', - params.action, - params.opts.args ? params.opts.args.join(' ') : ''); - - pm2.connect(function() { - pm2.remoteV2(params.action, params.opts, function(err, dt) { - process.send(JSON.stringify(Utility.clone({ - err: err, - dt: dt, - isFinished : true - }))); - pm2.disconnect(process.exit); - }); - }); -}); diff --git a/lib/Interactor/ReverseInteractor.js b/lib/Interactor/ReverseInteractor.js deleted file mode 100644 index 67673bba..00000000 --- a/lib/Interactor/ReverseInteractor.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -var debug = require('debug')('interface:driver'); -var nssocket = require('nssocket'); -var Url = require('url'); -var Cipher = require('./Cipher.js'); -var util = require('util'); - -var ReverseInteract = { - changeUrl : function(url) { - if (!this.connected) return; - console.log('[REV] Changing URL to %s', url); - - this.network = Url.parse(url); - this.socket.connect(parseInt(this.network.port), this.network.hostname); - this.socket.reconnect(); - }, - destroy : function() { - this.socket.destroy(); - }, - reconnect : function() { - console.log('[REV] Reconnecting to %s', this.network.hostname); - this.socket.reconnect(); - }, - start : function(opts) { - var self = this; - - if (!opts.url) - throw new Error('url not declared'); - if (!opts.conf) - throw new Error('Conf not passed to ReverseInteractor'); - - this.connected = false; - this.conf = opts.conf; - this.network = Url.parse(opts.url); - this.pm2_instance = opts.conf.pm2_instance; - - this.socket = new nssocket.NsSocket({ - type : 'tcp4', - reconnect : true, - retryInterval : 2000, - max : Infinity, - maxListeners : 50 - }); - - this.socket.on('error', function(e) { - self.connected = false; - console.error('[REV] %s', e.message || e); - }); - - this.socket.on('close', function(dt) { - self.connected = false; - }); - - this.socket.on('start', function() { - self.connected = true; - opts.conf.rev_con = true; - console.log('[REV] Connected to %s:%s', self.network.hostname, self.network.port); - }); - - console.log('[REV] Connecting to %s:%s', this.network.hostname, this.network.port); - - this.socket.connect(parseInt(this.network.port), this.network.hostname); - this.onMessage(); - }, - /** - * Listening to remote events from Keymetrics - */ - onMessage : function() { - if (!this.socket) return console.error('Reverse interaction not initialized'); - - /** - * Identify this agent to Keymetrics - * via PUBLIC/PRIVATE key exchange - */ - ReverseInteract.introduceToKeymetrics(); - - ReverseInteract.axmCustomActions(); - - /** - * From Pm2Actions.js - */ - ReverseInteract.pm2Actions(); - - ReverseInteract.pm2ScopedActions(); - - return false; - }, - /** - * First method called to identify this agent - */ - introduceToKeymetrics : function() { - var self = this; - - this.socket.data('ask', function(raw_msg) { - if (process.env.NODE_ENV && process.env.NODE_ENV == 'test') { - // Dont cipher data in test environment - self.socket.send('ask:rep', { - success : true, - machine_name : self.conf.MACHINE_NAME, - public_key : self.conf.PUBLIC_KEY - }); - } else { - var ciphered_data = Cipher.cipherMessage(JSON.stringify({ - machine_name : self.conf.MACHINE_NAME - }), self.conf.SECRET_KEY); - - if (!ciphered_data) - return console.error('Got wrong ciphering data %s %s', self.conf.MACHINE_NAME, self.conf.SECRET_KEY); - - self.socket.send('ask:rep', { - data : ciphered_data, - public_key : self.conf.PUBLIC_KEY - }); - } - return false; - }); - } -}; - -util._extend(ReverseInteract, require('./RemoteActions/Pm2Actions.js')); -util._extend(ReverseInteract, require('./RemoteActions/CustomActions.js')); - -module.exports = ReverseInteract; diff --git a/lib/Interactor/TransactionAggregator.js b/lib/Interactor/TransactionAggregator.js deleted file mode 100644 index d6a46283..00000000 --- a/lib/Interactor/TransactionAggregator.js +++ /dev/null @@ -1,684 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -/** - * Dependencies - */ -var cst = require('../../constants.js'); -var log = require('debug')('pm2:aggregator'); -var async = require('async'); -var Utility = require('./Utility.js'); -var fclone = require('fclone'); -var fs = require('fs'); -var path = require('path'); -var Histogram = require('pmx/lib/utils/probes/Histogram.js'); - -var LABELS = { - "HTTP_RESPONSE_CODE_LABEL_KEY": 'http/status_code', - "HTTP_URL_LABEL_KEY": 'http/url', - "HTTP_METHOD_LABEL_KEY": 'http/method', - "HTTP_RESPONSE_SIZE_LABEL_KEY": 'http/response/size', - "STACK_TRACE_DETAILS_KEY": 'stacktrace', - "ERROR_DETAILS_NAME": 'error/name', - "ERROR_DETAILS_MESSAGE": 'error/message', - "HTTP_SOURCE_IP": 'http/source/ip', - "HTTP_PATH_LABEL_KEY": "http/path" -} -var SPANS_DB = ['redis', 'mysql', 'pg', 'mongo', 'outbound_http']; - -/** - * - * # Data structure sent to interactor - * - * { - * 'process_name': { - * process : {}, // PM2 process meta data - * data : { - * routes : [ // array of all routes ordered by count - * { - * path: '/', // path of the route - * meta: { - * count: 50, // count of this route - * max: 300, // max latency of this route - * min: 50, // min latency of this route - * mean: 120 // mean latency of this route - * } - * variances: [{ // array of variance order by count - * spans : [ - * ... // transactions - * ], - * count: 50, // count of this variance - * max: 300, // max latency of this variance - * min: 50, // min latency of this variance - * mean: 120 // mean latency of this variance - * }] - * } - * ], - * meta : { - * trace_count : 50, // trace number - * mean_latency : 40, // global app latency in ms - * http_meter : 30, // global app req per minutes - * db_meter : 20, // number of database transaction per min - * } - * } - * } - * } - */ - -var TransactionAggregator = module.exports = function (pushInteractor) { - if (!(this instanceof TransactionAggregator)) return new TransactionAggregator(pushInteractor); - - var self = this; - this.processes = {}; - this.stackParser = pushInteractor.stackParser; - - /** - * First method to call in real environment - * - Listen to restart event for initialization period - * - Clear aggregation on process stop - * - Launch worker to attach data to be pushed to KM - */ - this.init = function () { - // New Process Online, reset & wait a bit before processing - pushInteractor.ipm2.bus.on('process:event', function (data) { - if (data.event !== 'online' || !self.processes[data.process.name]) return false; - - var rev = (data.process.versioning && data.process.versioning.revision) - ? data.process.versioning.revision : null; - - self.resetAggregation(data.process.name, { - rev: rev, - server: pushInteractor.conf.MACHINE_NAME - }); - }); - - // Process getting offline, delete aggregation - pushInteractor.ipm2.bus.on('process:event', function (data) { - if (data.event !== 'stop' || !self.processes[data.process.name]) return false; - log('Deleting aggregation for %s', data.process.name); - delete self.processes[data.process.name]; - }); - - self.launchWorker(); - }; - - /** - * Reset aggregation for target app_name - */ - this.resetAggregation = function (app_name, meta) { - log('Reseting agg for app:%s meta:%j', app_name, meta); - - if (self.processes[app_name].initialization_timeout) { - log('Reseting initialization timeout app:%s', app_name); - clearTimeout(self.processes[app_name].initialization_timeout); - clearInterval(self.processes[app_name].notifier); - self.processes[app_name].notifier = null; - } - - self.processes[app_name] = initializeRouteMeta({ - name: app_name, - rev: meta.rev, - server: meta.server - }); - - var start = Date.now(); - self.processes[app_name].notifier = setInterval(function () { - var elapsed = Date.now() - start; - // failsafe - if (elapsed / 1000 > cst.AGGREGATION_DURATION) { - clearInterval(self.processes[app_name].notifier); - self.processes[app_name].notifier = null; - } - - var msg = { - data: { - learning_duration: cst.AGGREGATION_DURATION, - elapsed: elapsed - }, - process: self.processes[app_name].process - }; - pushInteractor && pushInteractor.bufferData('axm:transaction:learning', msg); - }, 5000); - - self.processes[app_name].initialization_timeout = setTimeout(function () { - log('Initialization timeout finished for app:%s', app_name); - clearInterval(self.processes[app_name].notifier); - self.processes[app_name].notifier = null; - self.processes[app_name].initialization_timeout = null; - }, cst.AGGREGATION_DURATION); - }; - - /** - * Clear aggregated data for all process - */ - this.clearData = function () { - var self = this; - Object.keys(this.processes).forEach(function (process) { - self.resetAggregation(process, self.processes[process].process); - }); - }; - - /** - * Generate new entry for application - * - * @param {Object} process process meta - */ - function initializeRouteMeta (process) { - if (process.pm_id) delete process.pm_id; - - return { - routes: {}, - meta: { - trace_count: 0, - http_meter: new Utility.EWMA(), - db_meter: new Utility.EWMA(), - histogram: new Histogram({ measurement: 'median' }), - db_histograms: {} - }, - process: process - }; - } - - this.getAggregation = function () { - return this.processes; - }; - - this.validateData = function (packet) { - if (!packet || !packet.data) { - log('Packet malformated', packet); - return false; - } - - if (!packet.process) { - log('Got packet without process: %j', packet); - return false; - } - - if (!packet.data.spans || !packet.data.spans[0]) { - log('Trace without spans: %s', Object.keys(packet.data)); - return false; - } - - if (!packet.data.spans[0].labels) { - log('Trace spans without labels: %s', Object.keys(packet.data.spans)); - return false; - } - - return true; - } - - /** - * Main method to aggregate and compute stats for traces - * - * @param {Object} packet - * @param {Object} packet.process process metadata - * @param {Object} packet.data trace - */ - this.aggregate = function(packet) { - if (self.validateData(packet) === false) return false; - - var new_trace = packet.data; - var app_name = packet.process.name; - - if (!self.processes[app_name]) { - self.processes[app_name] = initializeRouteMeta(packet.process); - } - - var process = self.processes[app_name]; - - // Get http path of current span - var path = new_trace.spans[0].labels[LABELS.HTTP_PATH_LABEL_KEY]; - - // Cleanup spans - self.censorSpans(new_trace.spans); - - // remove spans with startTime == endTime - new_trace.spans = new_trace.spans.filter(function (span) { - return span.endTime !== span.startTime; - }); - - // compute duration of child spans - new_trace.spans.forEach(function (span) { - span.mean = Math.round(new Date(span.endTime) - new Date(span.startTime)); - delete span.endTime; - }); - - // Update app meta (mean_latency, http_meter, db_meter, trace_count) - new_trace.spans.forEach(function (span) { - if (!span.name || !span.kind) return false; - - if (span.kind === 'RPC_SERVER') { - process.meta.histogram.update(span.mean); - return process.meta.http_meter.update(); - } - - // Override outbount http queries for processing - if (span.labels && span.labels['http/method'] && span.labels['http/status_code']) { - span.labels['service'] = span.name; - span.name = 'outbound_http'; - } - - for (var i = 0, len = SPANS_DB.length; i < len; i++) { - if (span.name.indexOf(SPANS_DB[i]) > -1) { - process.meta.db_meter.update(); - if (!process.meta.db_histograms[SPANS_DB[i]]) { - process.meta.db_histograms[SPANS_DB[i]] = new Histogram({ measurement: 'mean' }); - } - process.meta.db_histograms[SPANS_DB[i]].update(span.mean); - break; - } - } - }); - - process.meta.trace_count++; - - /** - * Handle traces aggregation - */ - if (path[0] === '/' && path !== '/') { - path = path.substr(1, path.length - 1); - } - - var matched = self.matchPath(path, process.routes); - - if (!matched) { - process.routes[path] = []; - self.mergeTrace(process.routes[path], new_trace, process); - } else { - self.mergeTrace(process.routes[matched], new_trace, process); - } - - return self.processes; - }; - - /** - * Merge new trace and compute mean, min, max, count - * - * @param {Object} aggregated previous aggregated route - * @param {Object} trace - */ - this.mergeTrace = function (aggregated, trace, process) { - var self = this; - - if (!aggregated || !trace) return; - - // if the trace doesn't any spans stop aggregation here - if (trace.spans.length === 0) return; - - // create data structure if needed - if (!aggregated.variances) aggregated.variances = []; - if (!aggregated.meta) { - aggregated.meta = { - histogram: new Histogram({ measurement: 'median' }), - meter: new Utility.EWMA() - }; - } - - aggregated.meta.histogram.update(trace.spans[0].mean); - aggregated.meta.meter.update(); - - var merge = function (variance) { - // no variance found so its a new one - if (variance == null) { - delete trace.projectId; - delete trace.traceId; - trace.histogram = new Histogram({ measurement: 'median' }); - trace.histogram.update(trace.spans[0].mean); - - trace.spans.forEach(function (span) { - span.histogram = new Histogram({ measurement: 'median' }); - span.histogram.update(span.mean); - delete span.mean; - }); - - // parse strackrace - self.parseStacktrace(trace.spans); - aggregated.variances.push(trace); - } else { - // check to see if request is anormally slow, if yes send it as inquisitor - if (trace.spans[0].mean > variance.histogram.percentiles([0.95])[0.95] && - typeof pushInteractor !== 'undefined' && !process.initialization_timeout) { - // serialize and add metadata - self.parseStacktrace(trace.spans) - var data = { - trace: fclone(trace.spans), - variance: fclone(variance.spans.map(function (span) { - return { - labels: span.labels, - kind: span.kind, - name: span.name, - startTime: span.startTime, - percentiles: { - p5: variance.histogram.percentiles([0.5])[0.5], - p95: variance.histogram.percentiles([0.95])[0.95] - } - } - })), - meta: { - value: trace.spans[0].mean, - percentiles: { - p5: variance.histogram.percentiles([0.5])[0.5], - p75: variance.histogram.percentiles([0.75])[0.75], - p95: variance.histogram.percentiles([0.95])[0.95], - p99: variance.histogram.percentiles([0.99])[0.99] - }, - min: variance.histogram.getMin(), - max: variance.histogram.getMax(), - count: variance.histogram.getCount() - }, - process: process.process - }; - pushInteractor.bufferData('axm:transaction:outlier', data); - } - - // variance found, merge spans - variance.histogram.update(trace.spans[0].mean); - - // update duration of spans to be mean - self.updateSpanDuration(variance.spans, trace.spans, variance.count); - - // delete stacktrace before merging - trace.spans.forEach(function (span) { - delete span.labels.stacktrace; - }); - } - }; - - // for every variance, check spans same variance - for (var i = 0; i < aggregated.variances.length; i++) { - if (self.compareList(aggregated.variances[i].spans, trace.spans)) { - return merge(aggregated.variances[i]); - } - } - // else its a new variance - return merge(null); - }; - - /** - * Parkour simultaneously both spans list to update value of the first one using value of the second one - * The first should be variance already aggregated for which we want to merge the second one - * The second one is a new trace, so we need to re-compute mean/min/max time for each spans - */ - this.updateSpanDuration = function (spans, newSpans) { - for (var i = 0, len = spans.length; i < len; i++) { - if (!newSpans[i]) continue; - spans[i].histogram.update(newSpans[i].mean); - } - }; - - /** - * Compare two spans list by going down on each span and comparing child and attribute - */ - this.compareList = function (one, two) { - if (one.length !== two.length) return false; - - for (var i = 0, len = one; i < len; i++) { - if (one[i].name !== two[i].name) return false; - if (one[i].kind !== two[i].kind) return false; - if (!one[i].labels && two[i].labels) return false; - if (one[i].labels && !two[i].labels) return false; - if (one[i].labels.length !== two[i].labels.length) return false; - } - return true; - }; - - /** - * Will return the route if we found an already matched route - */ - this.matchPath = function (path, routes) { - // empty route is / without the fist slash - if (!path || !routes) return false; - if (path === '/') return routes[path] ? path : null; - - // remove the last slash if exist - if (path[path.length - 1] === '/') { - path = path.substr(0, path.length - 1); - } - - // split to get array of segment - path = path.split('/'); - - // if the path has only one segment, we just need to compare the key - if (path.length === 1) return routes[path[0]] ? routes[path[0]] : null; - - // check in routes already stored for match - var keys = Object.keys(routes); - for (var i = 0, len = keys.length; i < len; i++) { - var route = keys[i]; - var segments = route.split('/'); - - if (segments.length !== path.length) continue; - - for (var j = path.length - 1; j >= 0; j--) { - // different segment, try to find if new route or not - if (path[j] !== segments[j]) { - // if the aggregator already have matched that segment with a wildcard and the next segment is the same - if (self.isIdentifier(path[j]) && segments[j] === '*' && path[j - 1] === segments[j - 1]) { - return segments.join('/'); - } // case a var in url match, so we continue because they must be other var in url - else if (path[j - 1] !== undefined && path[j - 1] === segments[j - 1] && self.isIdentifier(path[j]) && self.isIdentifier(segments[j])) { - segments[j] = '*'; - // update routes in cache - routes[segments.join('/')] = routes[route]; - delete routes[keys[i]]; - return segments.join('/'); - } else { - break; - } - } - - // if finish to iterate over segment of path, we must be on the same route - if (j === 0) return segments.join('/'); - } - } - }; - - this.launchWorker = function () { - setInterval(function () { - var normalized = self.prepareAggregationforShipping(); - Object.keys(normalized).forEach(function (key) { - pushInteractor.bufferData('axm:transaction', normalized[key]); - }); - }, cst.TRACE_FLUSH_INTERVAL); - }; - - /** - * Normalize aggregation - */ - this.prepareAggregationforShipping = function () { - var normalized = {}; - - // Iterate each applications - Object.keys(self.processes).forEach(function (app_name) { - var process = self.processes[app_name]; - var routes = process.routes; - - if (process.initialization_timeout) { - log('Waiting for app %s to be initialized', app_name); - return null; - } - - normalized[app_name] = { - data: { - routes: [], - meta: fclone({ - trace_count: process.meta.trace_count, - http_meter: Math.round(process.meta.http_meter.rate(1000) * 100) / 100, - db_meter: Math.round(process.meta.db_meter.rate(1000) * 100) / 100, - http_percentiles: { - median: process.meta.histogram.percentiles([0.5])[0.5], - p95: process.meta.histogram.percentiles([0.95])[0.95], - p99: process.meta.histogram.percentiles([0.99])[0.99] - }, - db_percentiles: {} - }) - }, - process: process.process - }; - - // compute percentiles for each db spans if they exist - SPANS_DB.forEach(function (name) { - var histogram = process.meta.db_histograms[name]; - if (!histogram) return; - normalized[app_name].data.meta.db_percentiles[name] = fclone(histogram.percentiles([0.5])[0.5]); - }); - - Object.keys(routes).forEach(function (path) { - var data = routes[path]; - - // hard check for invalid data - if (!data.variances || data.variances.length === 0) return; - - // get top 5 variances of the same route - var variances = data.variances.sort(function (a, b) { - return b.count - a.count; - }).slice(0, 5); - - // create a copy without reference to stored one - var routeCopy = { - path: path === '/' ? '/' : '/' + path, - meta: fclone({ - min: data.meta.histogram.getMin(), - max: data.meta.histogram.getMax(), - count: data.meta.histogram.getCount(), - meter: Math.round(data.meta.meter.rate(1000) * 100) / 100, - median: data.meta.histogram.percentiles([0.5])[0.5], - p95: data.meta.histogram.percentiles([0.95])[0.95] - }), - variances: [] - }; - - variances.forEach(function (variance) { - // hard check for invalid data - if (!variance.spans || variance.spans.length === 0) return; - - // deep copy of variances data - var tmp = fclone({ - spans: [], - count: variance.histogram.getCount(), - min: variance.histogram.getMin(), - max: variance.histogram.getMax(), - median: variance.histogram.percentiles([0.5])[0.5], - p95: variance.histogram.percentiles([0.95])[0.95] - }); - - // get data for each span - variance.spans.forEach(function (span) { - tmp.spans.push(fclone({ - name: span.name, - labels: span.labels, - kind: span.kind, - min: span.histogram.getMin(), - max: span.histogram.getMax(), - median: span.histogram.percentiles([0.5])[0.5] - })); - }); - // push serialized into normalized data - routeCopy.variances.push(tmp); - }); - // push the route into normalized data - normalized[app_name].data.routes.push(routeCopy); - }); - }); - - return normalized; - }; - - /** - * Check if the string can be a id of some sort - * - * @param {String} id - */ - this.isIdentifier = function (id) { - id = typeof (id) !== 'string' ? id + '' : id; - - // uuid v1/v4 with/without dash - if (id.match(/[0-9a-f]{8}-[0-9a-f]{4}-[14][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{12}[14][0-9a-f]{19}/i)) - return true; - // if number - else if (id.match(/\d+/)) - return true; - // if suit of nbr/letters - else if (id.match(/[0-9]+[a-z]+|[a-z]+[0-9]+/)) - return true; - // if match pattern with multiple char spaced by . - _ @ - else if (id.match(/((?:[0-9a-zA-Z]+[@\-_.][0-9a-zA-Z]+|[0-9a-zA-Z]+[@\-_.]|[@\-_.][0-9a-zA-Z]+)+)/)) - return true; - else - return false; - } - - var REGEX_JSON_CLEANUP = /":(?!\[|{)\\"[^"]*\\"|":(["'])(?:(?=(\\?))\2.)*?\1|":(?!\[|{)[^,}\]]*|":\[[^{]*]/g - /** - * Cleanup trace data - * - delete result(s) - * - replace labels value with a question mark - * - * @param {Object} spans list of span for a trace - */ - this.censorSpans = function(spans) { - if (!spans) - return log('spans is null'); - if (cst.DEBUG) return; - - spans.forEach(function(span) { - if (!span.labels) - return; - - delete span.labels.results; - delete span.labels.result; - delete span.spanId; - delete span.parentSpanId; - delete span.labels.values; - - Object.keys(span.labels).forEach(function(key) { - if (typeof(span.labels[key]) === 'string' && key !== 'stacktrace') - span.labels[key] = span.labels[key].replace(REGEX_JSON_CLEANUP, '\": \"?\"'); - }); - }); - } - - /** - * Parse stackrace of spans to extract and normalize data - * - * @param {Object} spans list of span for a trace - */ - this.parseStacktrace = function (spans) { - var self = this; - if (!spans) - return log('spans is null'); - - spans.forEach(function (span) { - // if empty make sure that it doesnt exist - if (!span || - !span.labels || - !span.labels.stacktrace || - typeof(span.labels.stacktrace) !== 'string') - return; - - // you never know what come through that door - try { - span.labels.stacktrace = JSON.parse(span.labels.stacktrace); - } catch (e) { - return ; - } - - if (!span.labels.stacktrace || !(span.labels.stacktrace.stack_frame instanceof Array) ) return ; - // parse the stacktrace - var result = self.stackParser.parse(span.labels.stacktrace.stack_frame); - if (result) { - span.labels['source/file'] = result.callsite || undefined; - span.labels['source/context'] = result.context || undefined; - } - }); - - spans.forEach(function (span) { - if (!span || !span.labels) - return; - delete span.labels.stacktrace; - }) - } -}; diff --git a/lib/Interactor/Utility.js b/lib/Interactor/Utility.js deleted file mode 100644 index 898e734f..00000000 --- a/lib/Interactor/Utility.js +++ /dev/null @@ -1,235 +0,0 @@ -var path = require('path'); -var isAbsolute = require('../tools/IsAbsolute.js'); - -// EWMA = ExponentiallyWeightedMovingAverage from -// https://github.com/felixge/node-measured/blob/master/lib/util/ExponentiallyMovingWeightedAverage.js -// used to compute the nbr of time per minute that a variance is hit by a new trace -function EWMA () { - this._timePeriod = 60000 - this._tickInterval = 5000 - this._alpha = 1 - Math.exp(-this._tickInterval / this._timePeriod) - this._count = 0 - this._rate = 0 - - var self = this - this._interval = setInterval(function () { - self.tick() - }, this._tickInterval) - this._interval.unref() -} - -EWMA.prototype.update = function (n) { - this._count += n || 1 -} - -EWMA.prototype.tick = function () { - var instantRate = this._count / this._tickInterval - this._count = 0 - - this._rate += (this._alpha * (instantRate - this._rate)) -} - -EWMA.prototype.rate = function (timeUnit) { - return (this._rate || 0) * timeUnit -} - -var moment = require('moment'); - -/** - * Simple cache implementation - * - * @param {Object} opts cache options - * @param {Integer} opts.ttl time to live of all the keys - * @param {Function} opts.miss function called when a key isn't found in the cache - */ -function Cache (opts) { - this._cache = {}; - this._miss = opts.miss; - this._ttl_time = opts.ttl; - this._ttl = {}; - - if (opts.ttl) { - setInterval(this._worker.bind(this), 1000); - } -} - -/** - * Task running to check TTL and potentially remove older key - */ -Cache.prototype._worker = function () { - var keys = Object.keys(this._ttl); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var value = this._ttl[key]; - if (moment().isAfter(value)) { - delete this._cache[key]; - delete this._ttl[key]; - } - } -}; - -/** - * Empty the cache - */ -Cache.prototype.reset = function () { - this._cache = null; - this._cache = {}; - this._ttl = null; - this._ttl = {}; -}; - -/** - * Get a value from the cache - * - * @param {String} key - */ -Cache.prototype.get = function (key) { - if (!key) return null; - var value = this._cache[key]; - if (value) return value; - - value = this._miss(key); - - if (value) { - this.set(key, value); - } - return value; -}; - -/** - * Set a value in the cache - * - * @param {String} key - * @param {Mixed} value - */ -Cache.prototype.set = function (key, value) { - if (!key || !value) return false; - this._cache[key] = value; - if (this._ttl_time) { - this._ttl[key] = moment().add(this._ttl_time, 'seconds'); - } - return true; -}; - -/** - * StackTraceParser is used to parse callsite from stacktrace - * and get from FS the context of the error (if available) - * - * @param {Cache} cache cache implementation used to query file from FS and get context - */ -function StackTraceParser (opts) { - this._cache = opts.cache; - this._context_size = opts.context; -} - -StackTraceParser.prototype.attachContext = function (error) { - var self = this; - if (!error) return error; - - // if pmx attached callsites we can parse them to retrieve the context - if (typeof (error.stackframes) === 'object') { - var result = self.parse(error.stackframes); - // no need to send it since there is already the stacktrace - delete error.stackframes; - delete error.__error_callsites; - - if (result) { - error.callsite = result.callsite; - error.context = result.context; - } - } - // if the stack is here we can parse it directly from the stack string - // only if the context has been retrieved from elsewhere - if (typeof error.stack === 'string' && !error.callsite) { - var siteRegex = /(\/[^\\\n]*)/g; - var tmp; - var stack = []; - - // find matching groups - while ((tmp = siteRegex.exec(error.stack))) { - stack.push(tmp[1]); - } - - // parse each callsite to match the format used by the stackParser - stack = stack.map(function (callsite) { - // remove the trailing ) if present - if (callsite[callsite.length - 1] === ')') { - callsite = callsite.substr(0, callsite.length - 1); - } - var location = callsite.split(':'); - - return location.length < 3 ? callsite : { - file_name: location[0], - line_number: parseInt(location[1]) - }; - }); - - var finalCallsite = self.parse(stack); - if (finalCallsite) { - error.callsite = finalCallsite.callsite; - error.context = finalCallsite.context; - } - } - return error; -}; - -/** - * Parse the stacktrace and return callsite + context if available - */ -StackTraceParser.prototype.parse = function (stack) { - var self = this; - if (!stack || stack.length === 0) return false; - - for (var i = 0, len = stack.length; i < len; i++) { - var callsite = stack[i]; - - // avoid null values - if (typeof callsite !== 'object') continue; - if (!callsite.file_name || !callsite.line_number) continue; - - var type = isAbsolute(callsite.file_name) || callsite.file_name[0] === '.' ? 'user' : 'core'; - - // only use the callsite if its inside user space - if (!callsite || type === 'core' || callsite.file_name.indexOf('node_modules') > -1 || - callsite.file_name.indexOf('vxx') > -1) { - continue; - } - - // get the whole context (all lines) and cache them if necessary - var context = self._cache.get(callsite.file_name); - var source = []; - if (context && context.length > 0) { - // get line before the call - var preLine = callsite.line_number - self._context_size - 1; - var pre = context.slice(preLine > 0 ? preLine : 0, callsite.line_number - 1); - if (pre && pre.length > 0) { - pre.forEach(function (line) { - source.push(line.replace(/\t/g, ' ')); - }); - } - // get the line where the call has been made - if (context[callsite.line_number - 1]) { - source.push(context[callsite.line_number - 1].replace(/\t/g, ' ').replace(' ', '>>')); - } - // and get the line after the call - var postLine = callsite.line_number + self._context_size; - var post = context.slice(callsite.line_number, postLine); - if (post && post.length > 0) { - post.forEach(function (line) { - source.push(line.replace(/\t/g, ' ')); - }); - } - } - return { - context: source.length > 0 ? source.join('\n') : 'cannot retrieve source context', - callsite: [ callsite.file_name, callsite.line_number ].join(':') - }; - } - return false; -}; - -module.exports = { - EWMA: EWMA, - Cache: Cache, - StackTraceParser: StackTraceParser -}; diff --git a/lib/Interactor/WatchDog.js b/lib/Interactor/WatchDog.js deleted file mode 100644 index 8ade9c81..00000000 --- a/lib/Interactor/WatchDog.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -var PM2 = require('../..'); -var debug = require('debug')('interface:watchdog'); -var shelljs = require('shelljs'); -var csts = require('../../constants'); -var path = require('path'); - -process.env.PM2_AGENT_ONLINE = true; - -var WatchDog = module.exports = { - start : function(p) { - var self = this; - this.ipm2 = p.conf.ipm2; - this.relaunching = false; - this.pm2_instance = p.conf.pm2_instance; - - /** - * Handle PM2 connection state changes - */ - this.ipm2.on('ready', function() { - console.log('[WATCHDOG] Connected to PM2'); - self.relaunching = false; - self.autoDump(); - }); - - console.log('[WATCHDOG] Launching'); - - this.ipm2.on('reconnecting', function() { - console.log('[WATCHDOG] PM2 is disconnected - Relaunching PM2'); - - if (self.relaunching === true) return console.log('[WATCHDOG] Already relaunching PM2'); - self.relaunching = true; - - if (self.dump_interval) - clearInterval(self.dump_interval); - - return WatchDog.resurrect(); - }); - }, - resurrect : function() { - var self = this; - - console.log('[WATCHDOG] Trying to launch PM2 #1'); - - shelljs.exec('node ' + path.resolve(__dirname, '../../bin/pm2') + ' resurrect', function() { - setTimeout(function() { - self.relaunching = false; - }, 2500); - }); - }, - autoDump : function() { - var self = this; - - this.dump_interval = setInterval(function() { - if (self.relaunching == true) return false; - - self.pm2_instance.dump(function(err) { - if (err) return console.error('[WATCHDOG] Error when dumping'); - debug('PM2 process list dumped'); - return false; - }); - }, 5 * 60 * 1000); - } -}; diff --git a/lib/Interactor/internal-ip.js b/lib/Interactor/internal-ip.js deleted file mode 100644 index b3f1b7c9..00000000 --- a/lib/Interactor/internal-ip.js +++ /dev/null @@ -1,40 +0,0 @@ -var os = require('os'); - -var type = { - v4: { - def: '127.0.0.1', - family: 'IPv4' - }, - v6: { - def: '::1', - family: 'IPv6' - } -}; - -function internalIp(version) { - var options = type[version]; - var ret = options.def; - var interfaces = os.networkInterfaces(); - - Object.keys(interfaces).forEach(function (el) { - interfaces[el].forEach(function (el2) { - if (!el2.internal && el2.family === options.family) { - ret = el2.address; - } - }); - }); - - return ret; -} - -function v4() { - return internalIp('v4'); -} - -function v6() { - return internalIp('v6'); -} - -module.exports = v4; -module.exports.v4 = v4; -module.exports.v6 = v6; diff --git a/lib/Interactor/pm2-interface.js b/lib/Interactor/pm2-interface.js deleted file mode 100644 index 17d40303..00000000 --- a/lib/Interactor/pm2-interface.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2013 the PM2 project authors. All rights reserved. - * Use of this source code is governed by a license that - * can be found in the LICENSE file. - */ - -/** - * Dependencies - */ - -var axon = require('pm2-axon'); -var cst = require('../../constants.js'); -var util = require('util'); -var rpc = require('pm2-axon-rpc'); -var log = require('debug')('pm2:interface'); -var EventEmitter = require('events').EventEmitter; - -/** - * Export with conf - */ -module.exports = function(opts){ - var sub_port = opts && opts.sub_port || cst.DAEMON_PUB_PORT; - var rpc_port = opts && opts.rpc_port || cst.DAEMON_RPC_PORT; - - return new IPM2(sub_port, rpc_port); -}; - -/** - * IPM2, Pm2 Interface - */ - -var IPM2 = function(sub_port, rpc_port) { - if (!(this instanceof IPM2)) return new IPM2(sub_port, rpc_port); - var self = this; - - EventEmitter.call(this); - - this.sub_port = sub_port; - this.rpc_port = rpc_port; - - - var sub = axon.socket('sub-emitter'); - var sub_sock = this.sub_sock = sub.connect(sub_port); - this.bus = sub; - - var req = axon.socket('req'); - var rpc_sock = this.rpc_sock = req.connect(rpc_port); - this.rpc_client = new rpc.Client(req); - - this.rpc = {}; - - rpc_sock.on('connect', function() { - log('rpc_sock:ready'); - self.emit('rpc_sock:ready'); - generateMethods(function() { - self.emit('ready'); - }); - }); - - rpc_sock.on('close', function() { - log('rpc_sock:closed'); - self.emit('close'); - }); - - rpc_sock.on('reconnect attempt', function() { - log('rpc_sock:reconnecting'); - self.emit('reconnecting'); - }); - - sub_sock.on('connect', function() { - log('sub_sock ready'); - self.emit('sub_sock:ready'); - }); - - sub_sock.on('close', function() { - log('sub_sock:closed'); - self.emit('closed'); - }); - - sub_sock.on('reconnect attempt', function() { - log('sub_sock:reconnecting'); - self.emit('reconnecting'); - }); - - /** - * Disconnect socket connections. This will allow Node to exit automatically. - * Further calls to PM2 from this object will throw an error. - */ - this.disconnect = function () { - self.sub_sock.close(); - self.rpc_sock.close(); - }; - - /** - * Generate method by requesting exposed methods by PM2 - * You can now control/interact with PM2 - */ - var generateMethods = function(cb) { - log('Requesting and generating RPC methods'); - self.rpc_client.methods(function(err, methods) { - Object.keys(methods).forEach(function(key) { - var method_signature, md; - method_signature = md = methods[key]; - - log('+-- Creating %s method', md.name); - - (function(name) { - self.rpc[name] = function() { - log(name); - var args = Array.prototype.slice.call(arguments); - args.unshift(name); - self.rpc_client.call.apply(self.rpc_client, args); - }; - })(md.name); - - }); - return cb(); - }); - }; -}; - -util.inherits(IPM2, EventEmitter); diff --git a/lib/ProcessContainer.js b/lib/ProcessContainer.js index db9540dd..0d739bac 100644 --- a/lib/ProcessContainer.js +++ b/lib/ProcessContainer.js @@ -30,15 +30,7 @@ delete process.env.pm2_env; (function ProcessContainer() { var fs = require('fs'); - if (process.env.pmx !== 'false') { - require('pmx').init({ - transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, - http: process.env.km_link === 'true' || false, - v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, - event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, - deep_metrics: process.env.deep_monitoring === 'true' || false - }); - } + require('./ProcessUtils').injectModules(); var stdFile = pm2_env.pm_log_path; var outFile = pm2_env.pm_out_log_path; diff --git a/lib/ProcessContainerFork.js b/lib/ProcessContainerFork.js index 6d1b0ceb..59c45d3f 100644 --- a/lib/ProcessContainerFork.js +++ b/lib/ProcessContainerFork.js @@ -1,18 +1,10 @@ -/** + /** * Copyright 2013 the PM2 project authors. All rights reserved. * Use of this source code is governed by a license that * can be found in the LICENSE file. */ // Inject custom modules -if (process.env.pmx !== 'false') { - require('pmx').init({ - transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, - http: process.env.km_link === 'true' || false, - v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, - event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, - deep_metrics: process.env.deep_monitoring === 'true' || false - }); -} +require('./ProcessUtils').injectModules(); if (typeof(process.env.source_map_support) != "undefined" && process.env.source_map_support !== "false") { diff --git a/lib/ProcessUtils.js b/lib/ProcessUtils.js new file mode 100644 index 00000000..4dad2196 --- /dev/null +++ b/lib/ProcessUtils.js @@ -0,0 +1,43 @@ +'use strict' + +module.exports = { + injectModules: function() { + if (process.env.pmx !== 'false') { + const pmx = require('@pm2/io'); + + let conf = {}; + + if (process.env.io) { + const io = JSON.parse(process.env.io); + conf = io.conf ? io.conf : conf; + } + + let defaultConf = { + transactions: (process.env.trace === 'true' || process.env.deep_monitoring === 'true') || false, + http: process.env.km_link === 'true' || false, + v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, + event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, + deep_metrics: process.env.deep_monitoring === 'true' || false + }; + + const mergedConf = Object.assign(defaultConf, conf); + + pmx.init(mergedConf); + + if(require('semver').satisfies(process.versions.node, '>= 8.0.0')) { + var url = ''; + pmx.action('internal:inspect', function(reply) { + const inspector = require('inspector'); + if(url === '') { + inspector.open(); + url = inspector.url(); + } else { + inspector.close(); + url = ''; + } + reply(url); + }); + } + } + } +}; diff --git a/lib/Satan.js b/lib/Satan.js index e6df06ba..a6c77c26 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -215,7 +215,6 @@ Satan.remoteWrapper = function() { killMe : God.killMe, notifyKillPM2 : God.notifyKillPM2, - forceGc : God.forceGc, findByFullPath : God.findByFullPath, @@ -329,7 +328,7 @@ Satan.launchDaemon = function launchDaemon(cb) { debug('Launching daemon'); var SatanJS = p.resolve(p.dirname(module.filename), 'Satan.js'); - var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js'); + var InteractorDaemonizer = require('@pm2/agent/src/InteractorClient'); var node_args = []; @@ -385,7 +384,7 @@ Satan.launchDaemon = function launchDaemon(cb) { debug('PM2 daemon launched with return message: ', msg); child.removeListener('error', onError); child.disconnect(); - InteractorDaemonizer.launchAndInteract({}, function(err, data) { + InteractorDaemonizer.launchAndInteract({}, {}, function(err, data) { if (data) debug('Interactor launched'); return cb ? cb(null, child) : false; diff --git a/lib/Utility.js b/lib/Utility.js index 71f65b03..f172dc81 100644 --- a/lib/Utility.js +++ b/lib/Utility.js @@ -247,6 +247,18 @@ var Utility = module.exports = { checkPathIsNull: function(path) { return path === 'NULL' || path === '/dev/null'; + }, + + generateUUID: function () { + var s = []; + var hexDigits = "0123456789abcdef"; + for (var i = 0; i < 36; i++) { + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); + } + s[14] = "4"; + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); + s[8] = s[13] = s[18] = s[23] = "-"; + return s.join(""); } }; diff --git a/lib/binaries/Runtime.js b/lib/binaries/Runtime.js index 99c6cb19..0ad94aee 100644 --- a/lib/binaries/Runtime.js +++ b/lib/binaries/Runtime.js @@ -20,9 +20,9 @@ commander.version(pkg.version) .option('--auto-manage', 'keep application online after command exit') .option('--fast-boot', 'boot app faster by keeping pm2 runtime online in background (effective at second exit/start)') .option('--web [port]', 'launch process web api on [port] default to 9615') - .option('--secret [key]', 'keymetrics secret key') - .option('--public [key]', 'keymetrics public key') - .option('--machine-name [name]', 'keymetrics machine name') + .option('--secret [key]', 'PM2 plus secret key') + .option('--public [key]', 'PM2 plus public key') + .option('--machine-name [name]', 'PM2 plus machine name') .option('--env [name]', 'select env_[name] env variables in process config file') .option('--watch', 'Watch and Restart') .option('-i --instances ', 'launch [number] instances with load-balancer') @@ -32,9 +32,9 @@ commander.command('*') .action(function(cmd){ pm2 = new PM2.custom({ pm2_home : path.join(process.env.HOME, '.pm3'), - secret_key : process.env.KEYMETRICS_SECRET || commander.secret, - public_key : process.env.KEYMETRICS_PUBLIC || commander.public, - machine_name : process.env.INSTANCE_NAME || commander.machineName + secret_key : cst.SECRET_KEY || commander.secret, + public_key : cst.PUBLIC_KEY || commander.public, + machine_name : cst.MACHINE_NAME || commander.machineName }); pm2.connect(function() { diff --git a/lib/binaries/Runtime4Docker.js b/lib/binaries/Runtime4Docker.js index ee956bfe..837daa4b 100644 --- a/lib/binaries/Runtime4Docker.js +++ b/lib/binaries/Runtime4Docker.js @@ -17,15 +17,15 @@ process.env.PM2_DISCRETE_MODE = true; commander.version(pkg.version) .description('pm2-runtime is a drop-in replacement Node.js binary for containers') .option('-i --instances ', 'launch [number] of processes automatically load-balanced. Increase overall performances and performance stability.') - .option('--secret [key]', '[MONITORING] keymetrics secret key') + .option('--secret [key]', '[MONITORING] PM2 plus secret key') .option('--no-autorestart', 'start an app without automatic restart') .option('--node-args ', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"') .option('-n --name ', 'set a for script') .option('--max-memory-restart ', 'specify max memory amount used to autorestart (in octet or use syntax like 100M)') .option('-c --cron ', 'restart a running process based on a cron pattern') .option('--interpreter ', 'the interpreter pm2 should use for executing app (bash, python...)') - .option('--public [key]', '[MONITORING] keymetrics public key') - .option('--machine-name [name]', '[MONITORING] keymetrics machine name') + .option('--public [key]', '[MONITORING] PM2 plus public key') + .option('--machine-name [name]', '[MONITORING] PM2 plus machine name') .option('--trace', 'enable transaction tracing with km') .option('--v8', 'enable v8 data collecting') .option('--format', 'output logs formated like key=val') @@ -65,9 +65,9 @@ var Runtime = { instanciate : function(cmd) { this.pm2 = new PM2.custom({ pm2_home : process.env.PM2_HOME || path.join(process.env.HOME, '.pm2'), - secret_key : process.env.KEYMETRICS_SECRET || commander.secret, - public_key : process.env.KEYMETRICS_PUBLIC || commander.public, - machine_name : process.env.INSTANCE_NAME || commander.machineName, + secret_key : cst.SECRET_KEY || commander.secret, + public_key : cst.PUBLIC_KEY || commander.public, + machine_name : cst.MACHINE_NAME || commander.machineName, daemon_mode : process.env.PM2_RUNTIME_DEBUG || false }); diff --git a/lib/motd b/lib/motd index e2001d75..37e0505b 100644 --- a/lib/motd +++ b/lib/motd @@ -12,12 +12,11 @@ __/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____ _\///______________\///______________\///__\///////////////__ - Community Edition + Runtime Edition - Production Process Manager for Node.js applications + PM2 is a Production Process Manager for Node.js applications with a built-in Load Balancer. - Start and Daemonize any application: $ pm2 start app.js diff --git a/lib/templates/ecosystem.tpl b/lib/templates/ecosystem.tpl index a5455ea4..9ad0995a 100644 --- a/lib/templates/ecosystem.tpl +++ b/lib/templates/ecosystem.tpl @@ -1,33 +1,15 @@ module.exports = { - /** - * Application configuration section - * http://pm2.keymetrics.io/docs/usage/application-declaration/ - */ - apps : [ - - // First application - { - name : 'API', - script : 'app.js', - env: { - COMMON_VARIABLE: 'true' - }, - env_production : { - NODE_ENV: 'production' - } + apps : [{ + name : 'API', + script : 'app.js', + env: { + NODE_ENV: 'development' }, - - // Second application - { - name : 'WEB', - script : 'web.js' + env_production : { + NODE_ENV: 'production' } - ], + }], - /** - * Deployment section - * http://pm2.keymetrics.io/docs/usage/deployment/ - */ deploy : { production : { user : 'node', @@ -36,17 +18,6 @@ module.exports = { repo : 'git@github.com:repo.git', path : '/var/www/production', 'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production' - }, - dev : { - user : 'node', - host : '212.83.163.1', - ref : 'origin/master', - repo : 'git@github.com:repo.git', - path : '/var/www/development', - 'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env dev', - env : { - NODE_ENV: 'dev' - } } } }; diff --git a/lib/templates/init-scripts/rcd-openbsd.tpl b/lib/templates/init-scripts/rcd-openbsd.tpl new file mode 100644 index 00000000..590a995e --- /dev/null +++ b/lib/templates/init-scripts/rcd-openbsd.tpl @@ -0,0 +1,41 @@ +#!/bin/sh +# +# from /usr/ports/infrastructure/templates/rc.template + +daemon="/usr/local/bin/pm2" +#daemon_flags= +#daemon_rtable=0 +#daemon_timeout="30" +daemon_user="%USER%" + +. /etc/rc.d/rc.subr + +pexp="node: PM2.*God Daemon.*" +#rc_bg= # (undefined) +#rc_reload= # (undefined) +#rc_usercheck=YES + +#rc_pre() { +#} + +rc_start() { + ${rcexec} "${daemon} ${daemon_flags} resurrect" +} + +#rc_check() { +# pgrep -T "${daemon_rtable}" -q -xf "${pexp}" +#} + +rc_reload() { + ${rcexec} "${daemon} reload all" + #pkill -HUP -T "${daemon_rtable}" -xf "${pexp}" +} + +#rc_stop() { +# pkill -T "${daemon_rtable}" -xf "${pexp}" +#} + +#rc_post() { +#} + +rc_cmd $1 diff --git a/lib/tools/isbinaryfile.js b/lib/tools/isbinaryfile.js index 65f37fdf..7b0135d0 100644 --- a/lib/tools/isbinaryfile.js +++ b/lib/tools/isbinaryfile.js @@ -18,7 +18,7 @@ module.exports = function(bytes, size) { } var descriptor = fs.openSync(file, 'r'); try { - bytes = new Buffer(max_bytes); + bytes = Buffer.alloc(max_bytes); size = fs.readSync(descriptor, bytes, 0, bytes.length, 0); } finally { fs.closeSync(descriptor); @@ -32,7 +32,7 @@ module.exports = function(bytes, size) { fs.open(file, 'r', function(err, descriptor){ if (err) return callback(err); - var bytes = new Buffer(max_bytes); + var bytes = Buffer.alloc(max_bytes); // Read the file with no encoding for raw buffer access. fs.read(descriptor, bytes, 0, bytes.length, 0, function(err, size, bytes){ fs.close(descriptor, function(err2){ diff --git a/package.json b/package.json index 0a27c968..526aa66b 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "pm2", "preferGlobal": true, - "version": "2.10.4", + "version": "3.0.0", "engines": { - "node": ">=0.12" + "node": ">=4.0.0" }, "directories": { "bin": "./bin", @@ -12,25 +12,25 @@ }, "author": { "name": "Strzelewicz Alexandre", - "email": "alexandre@keymetrics.io", - "url": "https://keymetrics.io" + "email": "alexandre@pm2.io", + "url": "https://pm2.io" }, "maintainers": [ { - "name": "tknew", - "email": "strzelewicz.alexandre@gmail.com" + "name": "Alexandre Strzelewicz", + "email": "alexandre@pm2.io" }, { - "name": "soyuka", - "email": "soyuka@gmail.com" + "name": "Antoine Bluchet", + "email": "antoine@pm2.io" }, { - "name": "wallet77", - "email": "wallet77@gmail.com" + "name": "Vincent Vallet", + "email": "vincent@pm2.io" }, { - "name": "vmarchaud", - "email": "contact@vmarchaud.fr" + "name": "Valentin Marchaud", + "email": "valentin@pm2.io" } ], "contributors": [ @@ -91,8 +91,7 @@ "main": "index.js", "types": "types/index.d.ts", "scripts": { - "test": "NODE_ENV=test bash test/pm2_check_dependencies.sh && NODE_ENV=test bash test/pm2_programmatic_tests.sh && NODE_ENV=test bash test/pm2_behavior_tests.sh", - "bench-pmx": "pm2 delete all; pm2 install pm2-probe; node examples/pmx/app.js; pm2 ls" + "test": "bash test/e2e.sh && bash test/unit.sh" }, "keywords": [ "cli", @@ -159,38 +158,41 @@ "pm2-runtime": "./bin/pm2-runtime" }, "dependencies": { - "async": "^2.5", + "@pm2/agent": "^0.5.4", + "@pm2/io": "^2.0.2", + "@pm2/js-api": "^0.5.15", + "async": "^2.6.1", "blessed": "^0.1.81", - "chalk": "^1.1", - "chokidar": "^2", + "chalk": "^2.4.1", + "chokidar": "^2.0.4", "cli-table-redemption": "^1.0.0", - "commander": "2.13.0", + "coffee-script": "^1.12.7", + "commander": "2.15.1", "cron": "^1.3", - "debug": "^3.0", - "eventemitter2": "1.0.5", + "debug": "^3.1", + "eventemitter2": "5.0.1", "fclone": "1.0.11", "mkdirp": "0.5.1", - "moment": "^2.19", - "needle": "^2.1.0", + "moment": "^2.22.2", + "needle": "^2.2.1", "nssocket": "0.6.0", - "pidusage": "^1.2.0", - "pm2-axon": "3.1.0", + "pidusage": "^2.0.6", + "pm2-axon": "3.3.0", "pm2-axon-rpc": "^0.5.1", "pm2-deploy": "^0.3.9", "pm2-multimeter": "^0.1.2", - "pmx": "^1.6", - "promptly": "2.2.0", - "semver": "^5.3", - "shelljs": "0.7.8", - "source-map-support": "^0.5", + "promptly": "3.0.3", + "semver": "^5.5", + "shelljs": "~0.8.2", + "source-map-support": "^0.5.6", "sprintf-js": "1.1.1", - "v8-compile-cache": "^1.1.0", - "vizion": "^0.2", + "v8-compile-cache": "^2.0.0", + "vizion": "~0.2.0", "yamljs": "^0.3.0" }, "devDependencies": { - "mocha": "^3.5", - "should": "^11" + "mocha": "^5.2.0", + "should": "^13" }, "optionalDependencies": { "gkt": "https://tgz.pm2.io/gkt-1.0.0.tgz" diff --git a/paths.js b/paths.js index c8bccb3e..8830a7e0 100644 --- a/paths.js +++ b/paths.js @@ -45,7 +45,7 @@ module.exports = function(PM2_HOME) { DEFAULT_PID_PATH : p.resolve(PM2_HOME, 'pids'), DEFAULT_LOG_PATH : p.resolve(PM2_HOME, 'logs'), DEFAULT_MODULE_PATH : p.resolve(PM2_HOME, 'modules'), - KM_ACCESS_TOKEN : p.resolve(PM2_HOME, 'km-access-token'), + PM2_IO_ACCESS_TOKEN : p.resolve(PM2_HOME, 'pm2-io-token'), DUMP_FILE_PATH : p.resolve(PM2_HOME, 'dump.pm2'), DUMP_BACKUP_FILE_PATH : p.resolve(PM2_HOME, 'dump.pm2.bak'), diff --git a/test/Dockerfile b/test/Dockerfile index 4c5cd64f..70a27f30 100644 --- a/test/Dockerfile +++ b/test/Dockerfile @@ -1,9 +1,14 @@ -FROM node:9 +FROM node:alpine RUN mkdir -p /var/pm2 WORKDIR /var/pm2 +ENV NODE_ENV test +ENV PM2_DISCRETE_MODE true + +RUN apk update && apk add bash git curl python python3 php5 && rm -rf /var/cache/apk/* +RUN ln -s /usr/bin/php5 /usr/bin/php RUN npm install -g mocha@3.5 CMD ["mocha", "./test/programmatic/api.mocha.js"] diff --git a/test/bash/cli-ux.sh b/test/bash/cli-ux.sh deleted file mode 100644 index 8f3ddc7e..00000000 --- a/test/bash/cli-ux.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - - -echo -e "\033[1mRunning tests:\033[0m" - -which wrk -spec "You should have wrk benchmark in your /usr/bin" - -killall node - -cd $file_path -$pm2 start cluster-pm2.json -$pm2 start cluster-pm2.json -f -$pm2 start cluster-pm2.json -f -$pm2 start cluster-pm2.json -f -spec "start cluster" - -wrk -c 500 -t 500 -d 8 http://localhost:8020 &> /dev/null & -$pm2 monit -$pm2 list -$pm2 stop diff --git a/test/bash/file-descriptor.sh b/test/bash/file-descriptor.sh deleted file mode 100644 index 1d3aa419..00000000 --- a/test/bash/file-descriptor.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash - -# -# LSOF check -# - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - -cd $file_path - -echo "################## RELOAD ###################" - -lsof -c PM2 > /tmp/no_pm2_out.dat - -$pm2 list - -sleep 3 -lsof -c PM2 > /tmp/empty_pm2_out.dat - -$pm2 start echo.js -i 3 -$pm2 start killtoofast.js -i 3 -$pm2 delete all - -sleep 3 -lsof -c PM2 > /tmp/empty_pm2_out2.dat - -OUT1=`cat /tmp/empty_pm2_out.dat | wc -l` -OUT2=`cat /tmp/empty_pm2_out2.dat | wc -l` - -if [ $OUT1 -eq $OUT2 ]; then - success "All file descriptors have been closed" -else - fail "Some file descriptors are still open" -fi - -$pm2 start killtoofast.js -i 6 -$pm2 kill - -sleep 3 -lsof -c PM2 > /tmp/no_pm2_out2.dat -diff /tmp/no_pm2_out.dat /tmp/no_pm2_out2.dat - -if [ $? == "0" ]; then - success "All file descriptors have been closed" -else - fail "Some file descriptors are still open" -fi - -rm /tmp/no_pm2_out.dat -rm /tmp/no_pm2_out2.dat -rm /tmp/empty_pm2_out.dat -rm /tmp/empty_pm2_out2.dat diff --git a/test/bash/gracefulReload.sh b/test/bash/gracefulReload.sh deleted file mode 100644 index e7ed9fce..00000000 --- a/test/bash/gracefulReload.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - -cd $file_path - -echo "################## GRACEFUL RELOAD ###################" - -############### - -echo "Launching" -$pm2 start graceful-exit.js -i 4 --name="graceful" -o "grace.log" -e "grace-err.log" -should 'should start processes' 'online' 4 - -OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` -cat /dev/null > $OUT_LOG - -#### Graceful reload all - -$pm2 gracefulReload all - -OUT=`grep "Finished closing connections" "$OUT_LOG" | wc -l` -[ $OUT -eq 1 ] || fail "Process not restarted gracefuly" -success "Process restarted gracefuly" - - -cat /dev/null > $OUT_LOG - -#### Graceful reload name -$pm2 gracefulReload graceful - -OUT=`grep "Finished closing connections" "$OUT_LOG" | wc -l` -[ $OUT -eq 1 ] || fail "Process not restarted gracefuly" -success "Process restarted gracefuly" diff --git a/test/bash/gracefulReload2.sh b/test/bash/gracefulReload2.sh deleted file mode 100644 index b7c25fdd..00000000 --- a/test/bash/gracefulReload2.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - -cd $file_path - -echo "################## GRACEFUL RELOAD 2 ###################" - -echo "Launching" -$pm2 start graceful-exit-no-listen.js -i 2 --name="graceful2" -o "grace2.log" -e "grace-err2.log" -should 'should start processes' 'online' 2 - -OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` -cat /dev/null > $OUT_LOG - -#### Graceful reload name -$pm2 gracefulReload graceful2 - -echo "PATH: $OUT_LOG" - -TEXT=$(cat $OUT_LOG) - -echo "TEXT: $TEXT" - -OUT=`grep "Finished closing connections" "$OUT_LOG" | wc -l` -[ $OUT -eq 1 ] || fail "Non-listening process not restarted gracefuly" -success "Non-listening process restarted gracefuly" diff --git a/test/bash/gracefulReload3.sh b/test/bash/gracefulReload3.sh deleted file mode 100644 index 307d74f6..00000000 --- a/test/bash/gracefulReload3.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - -cd $file_path - -echo "################## GRACEFUL RELOAD 3 ###################" - -echo "Launching" -$pm2 start graceful-exit-send.js -i 2 --name="graceful3" -o "grace3.log" -e "grace-err3.log" -should 'should start processes' 'online' 2 - -OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` -cat /dev/null > $OUT_LOG - -#### Graceful reload name -$pm2 gracefulReload graceful3 - -OUT=`grep "Finished closing connections" "$OUT_LOG" | wc -l` -[ $OUT -eq 1 ] || fail "Process that sends 'online' not restarted gracefuly" -success "Process that sends 'online' restarted gracefuly" diff --git a/test/bash/interact.sh b/test/bash/interact.sh deleted file mode 100644 index 1fc1049c..00000000 --- a/test/bash/interact.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" -cd $file_path - -echo -e "\033[1mRunning Interaction tests:\033[0m" - -$pm2 interact stop - -$pm2 interact - -$pm2 interact XXX2 XXX3 homeloc - -$pm2 updatePM2 - -$pm2 interact stop - -$pm2 interact - -$pm2 interact info - -$pm2 interact info | grep "XXX2" -spec "Should have XXX2 has public key" - -$pm2 interact info | grep "XXX3" -spec "Should have XXX3 has public key" - -$pm2 list - -$pm2 interact stop -$pm2 kill diff --git a/test/bash/issues/2337.sh b/test/bash/issues/2337.sh deleted file mode 100644 index 67159ad1..00000000 --- a/test/bash/issues/2337.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/../include.sh" -cd $file_path - -echo -e "\033[1mRunning tests for json files :\033[0m" - -$pm2 start echo.js --name zero -f -$pm2 start echo.js --name one -f -$pm2 start echo.js --name two -f -should 'should have 3 processes online' 'online' 3 -$pm2 stop 0 -$pm2 stop 2 -$pm2 start echo.js --name three -f -$pm2 ls -should 'should have 2 processes online' 'online' 2 -should 'should have 2 processes stopped' 'stopped' 2 diff --git a/test/docker_parallel_test.sh b/test/docker_parallel_test.sh index 4b2b73a4..b46304bc 100644 --- a/test/docker_parallel_test.sh +++ b/test/docker_parallel_test.sh @@ -1,3 +1,10 @@ -# docker build -t pm2-test -f test/Dockerfile . -docker run -v `pwd`:/var/pm2 pm2-test mocha ./test/programmatic/api.mocha.js -docker run -v `pwd`:/var/pm2 pm2-test mocha ./test/programmatic/client.mocha.js +set -e + +docker build -t pm2-test -f test/Dockerfile . + +JOBS=2 +OPTS="--jobs $JOBS --joblog joblog-X docker run -v `pwd`:/var/pm2 pm2-test" + +ls test/e2e/cli/* | parallel $OPTS bash + +#ls test/e2e/binaries/* test/e2e/modules/* test/e2e/internal/* test/e2e/process-file/* test/e2e/cli/* test/e2e/logs/* | parallel $OPTS bash diff --git a/test/e2e.sh b/test/e2e.sh new file mode 100644 index 00000000..1b73924a --- /dev/null +++ b/test/e2e.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash + +SRC=$(cd $(dirname "$0"); pwd) +source "${SRC}/e2e/include.sh" + +# Abort script at first error +set -e +set -o verbose + +# MODULES +bash ./test/e2e/modules/get-set.sh +spec "Configuration system working" +bash ./test/e2e/modules/module.sh +spec "module system" +bash ./test/e2e/modules/module-safeguard.sh +spec "module safeguard system (--safe)" + +# CLI +bash ./test/e2e/cli/reload.sh +spec "Reload" +bash ./test/e2e/cli/start-app.sh +spec "Command line passing" +bash ./test/e2e/cli/operate-regex.sh +spec "Operate process that match regex" +bash ./test/e2e/cli/interpreter.sh +spec "Javascript transpilers tests" +bash ./test/e2e/cli/app-configuration.sh +spec "App configuration" +bash ./test/e2e/cli/binary.sh +spec "binary test" +bash ./test/e2e/cli/startOrX.sh +spec "startOrX commands" +bash ./test/e2e/cli/reset.sh +spec "Reset meta" +bash ./test/e2e/cli/env-refresh.sh +spec "Environment refresh on restart" +bash ./test/e2e/cli/extra-lang.sh +spec "Various programming languages checks (Python, PHP)" +bash ./test/e2e/cli/multiparam.sh +spec "Multiparam process management" +bash ./test/e2e/cli/smart-start.sh +spec "smart start test" +bash ./test/e2e/cli/args.sh +spec "check arguments passing" +bash ./test/e2e/cli/attach.sh +spec "pm2 attach method" +bash ./test/e2e/cli/serve.sh +spec "pm2 serve CLI method" +bash ./test/e2e/cli/monit.sh +spec "km selective monitoring " +bash ./test/e2e/cli/cli-actions-1.sh +spec "CLI basic test" +bash ./test/e2e/cli/cli-actions-2.sh +spec "Second hard cli tests" +bash ./test/e2e/cli/dump.sh +spec "dump test" +bash ./test/e2e/cli/resurrect.sh +spec "resurrect test" +bash ./test/e2e/cli/mjs.sh +spec "Test import syntax" +bash ./test/e2e/cli/watch.sh +spec "watch system tests" +bash ./test/e2e/cli/right-exit-code.sh +spec "Verification exit code" +bash ./test/e2e/cli/harmony.sh +spec "Harmony test" +bash ./test/e2e/cli/fork.sh +spec "Fork system working" +bash ./test/e2e/cli/piped-config.sh +spec "Piped JSON file test" + +# PROCESS FILES +bash ./test/e2e/process-file/json-file.sh +spec "JSON file test" +bash ./test/e2e/process-file/yaml-configuration.sh +spec "YAML configuration support" +bash ./test/e2e/process-file/json-reload.sh +spec "JSON reload test" +bash ./test/e2e/process-file/homogen-json-action.sh +spec "Homogen json actions" +bash ./test/e2e/process-file/app-config-update.sh +spec "CLI/JSON argument reload" +bash ./test/e2e/process-file/js-configuration.sh +spec "js configuration support" + +# BINARIES +bash ./test/e2e/binaries/pm2-dev.sh +spec "pm2-dev" +bash ./test/e2e/binaries/pm2-runtime.sh +spec "pm2-runtime" + +# INTERNALS +bash ./test/e2e/internals/wait-ready-event.sh +spec "Wait for application ready event" +bash ./test/e2e/internals/daemon-paths-override.sh +spec "Override daemon configuration paths" +bash ./test/e2e/internals/source_map.sh +spec "Source map resolution on exception" +bash ./test/e2e/internals/wrapped-fork.sh +spec "wrapped fork" +bash ./test/e2e/internals/infinite-loop.sh +spec "Infinite loop stop" +bash ./test/e2e/internals/options-via-env.sh +spec "set option via environment" +bash ./test/e2e/internals/promise.sh +spec "Promise warning message tests" +bash ./test/e2e/internals/increment-var.sh +spec "Increment env variables" +bash ./test/e2e/internals/start-consistency.sh +spec "Consistency between a JSON an CLI start" + +# MISC +bash ./test/e2e/misc/inside-pm2.sh +spec "Starting a process inside a PM2 process" +bash ./test/e2e/misc/vizion.sh +spec "vizion features (versioning control)" +bash ./test/e2e/misc/misc.sh +spec "MISC features" +bash ./test/e2e/misc/versioning-cmd.sh +spec "versioning system tests" +bash ./test/e2e/misc/instance-number.sh +spec "Negative instance number spawn one worker" +bash ./test/e2e/misc/startup.sh +spec "upstart startup test" +bash ./test/e2e/misc/nvm-node-version.sh +spec "NVM node version setting" +bash ./test/e2e/misc/cron-system.sh +spec "Cron system tests" + +# LOGS +bash ./test/e2e/logs/log-timestamp.sh +spec "timestamp prefix of pm2.log" +bash ./test/e2e/logs/log-custom.sh +spec "Custom log timestamp" +bash ./test/e2e/logs/log-reload.sh +spec "Log reload" +bash ./test/e2e/logs/log-entire.sh +spec "merge stdout && stderr" +bash ./test/e2e/logs/log-null.sh +spec "Logging path set to null" +bash ./test/e2e/logs/log-json.sh +spec "Logging directly to file in json" + +$pm2 kill diff --git a/test/bash/pm2-dev.sh b/test/e2e/binaries/pm2-dev.sh similarity index 72% rename from test/bash/pm2-dev.sh rename to test/e2e/binaries/pm2-dev.sh index 748dc7b7..6839550d 100644 --- a/test/bash/pm2-dev.sh +++ b/test/e2e/binaries/pm2-dev.sh @@ -1,9 +1,21 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" -pm2dev="`type -P node` `pwd`/bin/pm2-dev" + +pm2_path=`pwd`/bin/pm2-dev + +if [ ! -f $pm2_path ]; +then + pm2_path=`pwd`/../bin/pm2-dev + if [ ! -f $pm2_path ]; + then + pm2_path=`pwd`/../../bin/pm2-dev + fi +fi + +pm2dev="`type -P node` $pm2_path" export PM2_HOME=$HOME'/.pm2-dev' diff --git a/test/bash/pm2-runtime.sh b/test/e2e/binaries/pm2-runtime.sh similarity index 80% rename from test/bash/pm2-runtime.sh rename to test/e2e/binaries/pm2-runtime.sh index da29b919..8cfe8c38 100644 --- a/test/bash/pm2-runtime.sh +++ b/test/e2e/binaries/pm2-runtime.sh @@ -1,9 +1,21 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" -pm2_runtime="`type -P node` `pwd`/bin/pm2-runtime" + +pm2_path=`pwd`/bin/pm2-runtime + +if [ ! -f $pm2_path ]; +then + pm2_path=`pwd`/../bin/pm2-runtime + if [ ! -f $pm2_path ]; + then + pm2_path=`pwd`/../../bin/pm2-runtime + fi +fi + +pm2_runtime="`type -P node` $pm2_path" export PM2_RUNTIME_DEBUG='true' diff --git a/test/bash/app-configuration.sh b/test/e2e/cli/app-configuration.sh similarity index 84% rename from test/bash/app-configuration.sh rename to test/e2e/cli/app-configuration.sh index ab741c98..35c8a74f 100644 --- a/test/bash/app-configuration.sh +++ b/test/e2e/cli/app-configuration.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mRunning tests:\033[0m" @@ -59,7 +59,6 @@ exists 'probe test-probe exist' "test-probe" exists 'probe Event Loop Latency exist' "Loop delay" exists 'probe Event Loop Latency default value' "agg_type: 'avg'" -exists 'probe Event Loop Latency default value' "alert: {}" # Set new value for alert probe # $pm2 set probe-test.probes.Event\ Loop\ Latency.value 25 @@ -68,12 +67,5 @@ exists 'probe Event Loop Latency default value' "alert: {}" # exists 'probe Event Loop Latency alerted' "alert: { cmp: '>', value: 25, mode: 'threshold'" # Override value for test-probe -$pm2 set probe-test.probes.test-probe.value 30 -sleep 2 - -exists 'probe Event Loop Latency alerted' "value: 30" - -$pm2 restart all -sleep 1 - -exists 'probe Event Loop Latency alerted' "value: 30" +# $pm2 set probe-test.probes.test-probe.value 30 +# sleep 1 diff --git a/test/bash/args.sh b/test/e2e/cli/args.sh similarity index 97% rename from test/bash/args.sh rename to test/e2e/cli/args.sh index 0bfce30a..01a0529c 100644 --- a/test/bash/args.sh +++ b/test/e2e/cli/args.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/args diff --git a/test/bash/attach.sh b/test/e2e/cli/attach.sh similarity index 91% rename from test/bash/attach.sh rename to test/e2e/cli/attach.sh index fed9ac70..4a32e147 100644 --- a/test/bash/attach.sh +++ b/test/e2e/cli/attach.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mRunning tests:\033[0m" diff --git a/test/bash/binary.sh b/test/e2e/cli/binary.sh similarity index 96% rename from test/bash/binary.sh rename to test/e2e/cli/binary.sh index 13054c4f..e970a8ec 100644 --- a/test/bash/binary.sh +++ b/test/e2e/cli/binary.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mRunning tests:\033[0m" diff --git a/test/bash/cli-actions-1.sh b/test/e2e/cli/cli-actions-1.sh similarity index 99% rename from test/bash/cli-actions-1.sh rename to test/e2e/cli/cli-actions-1.sh index 8d393f52..1cbe7697 100644 --- a/test/bash/cli-actions-1.sh +++ b/test/e2e/cli/cli-actions-1.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/cli-actions-2.sh b/test/e2e/cli/cli-actions-2.sh similarity index 95% rename from test/bash/cli-actions-2.sh rename to test/e2e/cli/cli-actions-2.sh index 02b50fc5..46ef2c49 100644 --- a/test/bash/cli-actions-2.sh +++ b/test/e2e/cli/cli-actions-2.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path -############# TEST +############# Start / Stop / Restart echo -e "\033[1mRunning tests:\033[0m" @@ -17,7 +17,8 @@ spec "Should stop an app by script.js" $pm2 restart echo.js spec "Should restart an app by script.js (TRANSITIONAL STATE)" -############### +############### Start edge case + $pm2 delete all echo "Start application with filename starting with a numeric" @@ -28,28 +29,29 @@ should 'should app be stopped' 'stopped' 1 $pm2 restart 001-test should 'should app be online once restart called' 'online' 1 -$pm2 delete all ############## PID + +$pm2 delete all $pm2 start 001-test.js --name "test" should 'should app be online' 'online' 1 $pm2 pid > /tmp/pid-tmp $pm2 pid test -$pm2 delete all ############### +$pm2 delete all echo "Start application with filename starting with a numeric" $pm2 start throw-string.js -l err-string.log --merge-logs --no-automation >err-string.log -sleep 2 +sleep 1 grep 'throw-string.js' err-string.log spec "Should have written raw stack when throwing a string" -$pm2 delete all - #### +$pm2 delete all + $pm2 start echo.js --name gege should 'should app be online' 'online' 1 $pm2 stop gege @@ -78,7 +80,7 @@ $pm2 start echo.js ispec "Should not re start app" ########### DELETED STUFF BY ID -$pm2 kill +$pm2 delete all $pm2 start echo.js $pm2 delete 0 @@ -100,7 +102,7 @@ $pm2 list should 'should has been deleted process by script' "name: 'echo'" 0 ######## Actions on app name as number (#1937) -$pm2 kill +$pm2 delete all $pm2 start echo.js --name "455" should 'should restart processes' 'restart_time: 0' 1 $pm2 restart 455 @@ -113,9 +115,9 @@ $pm2 delete 455 should 'should has been deleted process by id' "name: '455'" 0 ########### OPTIONS OUTPUT FILES -$pm2 kill +$pm2 delete all -$pm2 start echo.js -o outech.log -e errech.log --name gmail -i 10 +$pm2 start echo.js -o outech.log -e errech.log --name gmail -i 1 sleep 1 cat outech-0.log > /dev/null spec "file outech-0.log exist" diff --git a/test/bash/dump.sh b/test/e2e/cli/dump.sh similarity index 94% rename from test/bash/dump.sh rename to test/e2e/cli/dump.sh index 3f9a9973..62ed1a58 100644 --- a/test/bash/dump.sh +++ b/test/e2e/cli/dump.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path $pm2 start echo.js -i 4 diff --git a/test/bash/env-refresh.sh b/test/e2e/cli/env-refresh.sh similarity index 98% rename from test/bash/env-refresh.sh rename to test/e2e/cli/env-refresh.sh index b49c6bf8..418c9e2f 100644 --- a/test/bash/env-refresh.sh +++ b/test/e2e/cli/env-refresh.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path echo -e "\033[1mENV REFRESH\033[0m" diff --git a/test/bash/extra-lang.sh b/test/e2e/cli/extra-lang.sh similarity index 97% rename from test/bash/extra-lang.sh rename to test/e2e/cli/extra-lang.sh index e3f22eb1..8761cd34 100644 --- a/test/bash/extra-lang.sh +++ b/test/e2e/cli/extra-lang.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/extra-lang diff --git a/test/bash/fork.sh b/test/e2e/cli/fork.sh similarity index 95% rename from test/bash/fork.sh rename to test/e2e/cli/fork.sh index e6091ce4..f641d23e 100644 --- a/test/bash/fork.sh +++ b/test/e2e/cli/fork.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/harmony.sh b/test/e2e/cli/harmony.sh similarity index 96% rename from test/bash/harmony.sh rename to test/e2e/cli/harmony.sh index 03094ea0..bba81985 100644 --- a/test/bash/harmony.sh +++ b/test/e2e/cli/harmony.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/interpreter.sh b/test/e2e/cli/interpreter.sh similarity index 60% rename from test/bash/interpreter.sh rename to test/e2e/cli/interpreter.sh index 3257e85c..e7379c29 100644 --- a/test/bash/interpreter.sh +++ b/test/e2e/cli/interpreter.sh @@ -1,12 +1,13 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/interpreter rm -rf ../../../node_modules/coffee-script/ rm -rf ../../../node_modules/livescript/ +rm -rf ../../../node_modules/ts-node/ ########### coffee @@ -41,49 +42,45 @@ should 'process should be online' "status: 'online'" 1 ########## LIVESCRIPT -$pm2 delete all -$pm2 start echo.ls -sleep 1 -should 'process should be errored without livescript installed' "status: 'errored'" 1 +# $pm2 delete all +# $pm2 start echo.ls +# sleep 1 +# should 'process should be errored without livescript installed' "status: 'errored'" 1 -########### Install +# ########### Install -$pm2 install livescript +# $pm2 install livescript -########### livescript fork test -$pm2 delete all +# ########### livescript fork test +# $pm2 delete all ->livescript.log +# >livescript.log -$pm2 start echo.ls -o livescript.log --merge-logs +# $pm2 start echo.ls -o livescript.log --merge-logs -sleep 1.5 -grep "Hello Livescript!" livescript.log -spec "Should work on Livescript files in fork mode" +# sleep 1.5 +# grep "Hello Livescript!" livescript.log +# spec "Should work on Livescript files in fork mode" -########### livescript cluster test -$pm2 delete all +# ########### livescript cluster test +# $pm2 delete all ->livescript.log +# >livescript.log -$pm2 start echo.ls -i 1 -o livescript.log --merge-logs +# $pm2 start echo.ls -i 1 -o livescript.log --merge-logs -sleep 1.5 -grep "Hello Livescript!" livescript.log -spec "Should work on Livescript files in cluster mode" +# sleep 1.5 +# grep "Hello Livescript!" livescript.log +# spec "Should work on Livescript files in cluster mode" ########### TYPESCRIPT -$pm2 delete all -$pm2 start echo.ts -sleep 1 -should 'process should be errored without typescript installed' "status: 'errored'" 1 - ########### Install + # $pm2 install typescript -########### typescript fork test +# ########### typescript fork test # $pm2 delete all # >typescript.log @@ -95,7 +92,7 @@ should 'process should be errored without typescript installed' "status: 'errore # grep "Hello Typescript!" typescript.log # spec "Should work on Typescript files in fork mode" -########### typescript cluster test +# ########### typescript cluster test # $pm2 delete all # >typescript.log diff --git a/test/bash/mjs.sh b/test/e2e/cli/mjs.sh similarity index 95% rename from test/bash/mjs.sh rename to test/e2e/cli/mjs.sh index 817ea2dd..662ef43c 100644 --- a/test/bash/mjs.sh +++ b/test/e2e/cli/mjs.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/mjs diff --git a/test/bash/monit.sh b/test/e2e/cli/monit.sh similarity index 95% rename from test/bash/monit.sh rename to test/e2e/cli/monit.sh index 668a46cd..14cf954d 100644 --- a/test/bash/monit.sh +++ b/test/e2e/cli/monit.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/multiparam.sh b/test/e2e/cli/multiparam.sh similarity index 94% rename from test/bash/multiparam.sh rename to test/e2e/cli/multiparam.sh index c71212e5..c3207862 100644 --- a/test/bash/multiparam.sh +++ b/test/e2e/cli/multiparam.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/operate-regex.sh b/test/e2e/cli/operate-regex.sh similarity index 94% rename from test/bash/operate-regex.sh rename to test/e2e/cli/operate-regex.sh index 86e59fa8..d2e0fd92 100644 --- a/test/bash/operate-regex.sh +++ b/test/e2e/cli/operate-regex.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path @@ -24,4 +24,3 @@ should 'should have stopped 1 apps' 'online' 0 $pm2 restart /echo-[1,2]/ should 'should have restarted 2 apps' 'online' 2 - diff --git a/test/bash/piped-config.sh b/test/e2e/cli/piped-config.sh similarity index 90% rename from test/bash/piped-config.sh rename to test/e2e/cli/piped-config.sh index 416cec35..83fa2725 100644 --- a/test/bash/piped-config.sh +++ b/test/e2e/cli/piped-config.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/reload.sh b/test/e2e/cli/reload.sh similarity index 80% rename from test/bash/reload.sh rename to test/e2e/cli/reload.sh index d659a1b6..a7248427 100644 --- a/test/bash/reload.sh +++ b/test/e2e/cli/reload.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path @@ -39,22 +39,23 @@ sleep 3 should 'should restart processes' 'restart_time: 1' 1 $pm2 kill -$pm2 start delayed_exit.js -i 2 -should 'should start processes' 'online' 2 -$pm2 stop delayed_exit.js -sleep 3 -should 'should stop processes' 'stopped' 2 -$pm2 restart delayed_exit.js -should 'should restart processes' 'restart_time: 0' 2 -$pm2 restart delayed_exit.js -should 'should restart processes' 'restart_time: 1' 2 -$pm2 reload delayed_exit.js -should 'should restart processes' 'restart_time: 2' 2 -$pm2 gracefulReload delayed_exit.js -should 'should restart processes' 'restart_time: 3' 2 -$pm2 kill +# $pm2 start delayed_exit.js -i 2 +# should 'should start processes' 'online' 2 +# $pm2 stop delayed_exit.js +# sleep 3 +# should 'should stop processes' 'stopped' 2 +# $pm2 restart delayed_exit.js +# should 'should restart processes' 'restart_time: 0' 2 +# $pm2 restart delayed_exit.js +# should 'should restart processes' 'restart_time: 1' 2 +# $pm2 reload delayed_exit.js +# should 'should restart processes' 'restart_time: 2' 2 +# $pm2 gracefulReload delayed_exit.js +# should 'should restart processes' 'restart_time: 3' 2 +# $pm2 kill $pm2 start child.js -i 4 +sleep 0.5 should 'should start processes' 'online' 4 $pm2 restart all should 'should restarted be one for all' 'restart_time' 4 diff --git a/test/bash/reset.sh b/test/e2e/cli/reset.sh similarity index 96% rename from test/bash/reset.sh rename to test/e2e/cli/reset.sh index a8f425bf..1a192f47 100644 --- a/test/bash/reset.sh +++ b/test/e2e/cli/reset.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/resurrect.sh b/test/e2e/cli/resurrect.sh similarity index 95% rename from test/bash/resurrect.sh rename to test/e2e/cli/resurrect.sh index b37abf58..ff83b513 100644 --- a/test/bash/resurrect.sh +++ b/test/e2e/cli/resurrect.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path $pm2 start echo.js -i 4 diff --git a/test/bash/right-exit-code.sh b/test/e2e/cli/right-exit-code.sh similarity index 93% rename from test/bash/right-exit-code.sh rename to test/e2e/cli/right-exit-code.sh index 38ec3c13..3f5e99a1 100644 --- a/test/bash/right-exit-code.sh +++ b/test/e2e/cli/right-exit-code.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/serve.sh b/test/e2e/cli/serve.sh similarity index 69% rename from test/bash/serve.sh rename to test/e2e/cli/serve.sh index 8989efda..e4179af0 100644 --- a/test/bash/serve.sh +++ b/test/e2e/cli/serve.sh @@ -1,21 +1,22 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/serve - +PORT=8081 +PORT_2=8082 echo "################## PM2 SERVE ###################" -$pm2 serve +$pm2 serve --port $PORT should 'should have started serving dir' 'online' 1 -curl http://localhost:8080/ > /tmp/tmp_out.txt +curl http://localhost:$PORT/ > /tmp/tmp_out.txt OUT=`cat /tmp/tmp_out.txt | grep -o "good shit" | wc -l` [ $OUT -eq 1 ] || fail "should have served index file under /" success "should have served index file under /" -curl http://localhost:8080/index.html > /tmp/tmp_out.txt +curl http://localhost:$PORT/index.html > /tmp/tmp_out.txt OUT=`cat /tmp/tmp_out.txt | grep -o "good shit" | wc -l` [ $OUT -eq 1 ] || fail "should have served index file under /index.html" success "should have served index file under /index.html" @@ -23,31 +24,31 @@ success "should have served index file under /index.html" echo "Shutting down the server" $pm2 delete all -curl http://localhost:8080/index.html > /tmp/tmp_out.txt +curl http://localhost:$PORT/index.html > /tmp/tmp_out.txt OUT=`cat /tmp/tmp_out.txt | grep -o "good shit" | wc -l` [ $OUT -eq 0 ] || fail "should be offline" success "should be offline" -$pm2 serve . 8000 +$pm2 serve . $PORT_2 should 'should have started serving dir' 'online' 1 -curl http://localhost:8000/index.html > /tmp/tmp_out.txt +curl http://localhost:$PORT_2/index.html > /tmp/tmp_out.txt OUT=`cat /tmp/tmp_out.txt | grep -o "good shit" | wc -l` -[ $OUT -eq 1 ] || fail "should be listening on port 8000" -success "should be listening on port 8000" +[ $OUT -eq 1 ] || fail "should be listening on port $PORT_2" +success "should be listening on port $PORT_2" $pm2 delete all -$pm2 serve . 8000 --name frontend +$pm2 serve . $PORT_2 --name frontend should 'should have started serving dir' 'online' 1 should 'should have custom name' 'frontend' 7 -curl http://localhost:8000/index.html > /tmp/tmp_out.txt +curl http://localhost:$PORT_2/index.html > /tmp/tmp_out.txt OUT=`cat /tmp/tmp_out.txt | grep -o "good shit" | wc -l` -[ $OUT -eq 1 ] || fail "should be listening on port 8000" -success "should be listening on port 8000" +[ $OUT -eq 1 ] || fail "should be listening on port $PORT_2" +success "should be listening on port $PORT_2" -curl http://localhost:8000/yolo.html > /tmp/tmp_out.txt +curl http://localhost:$PORT_2/yolo.html > /tmp/tmp_out.txt OUT=`cat /tmp/tmp_out.txt | grep -o "your file doesnt exist" | wc -l` [ $OUT -eq 1 ] || fail "should have served custom 404 file" success "should have served custom 404 file" @@ -67,4 +68,4 @@ $pm2 stop ecosystem.json curl http://localhost:8081/index.html > /tmp/tmp_out.txt OUT=`cat /tmp/tmp_out.txt | grep -o "good shit" | wc -l` [ $OUT -eq 0 ] || fail "should be offline" -success "should be offline" \ No newline at end of file +success "should be offline" diff --git a/test/bash/smart-start.sh b/test/e2e/cli/smart-start.sh similarity index 96% rename from test/bash/smart-start.sh rename to test/e2e/cli/smart-start.sh index a09513f1..70880b6b 100644 --- a/test/bash/smart-start.sh +++ b/test/e2e/cli/smart-start.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/sort.sh b/test/e2e/cli/sort.sh similarity index 97% rename from test/bash/sort.sh rename to test/e2e/cli/sort.sh index 995a49cb..bac3f8ba 100644 --- a/test/bash/sort.sh +++ b/test/e2e/cli/sort.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/sort diff --git a/test/e2e/cli/start-app.sh b/test/e2e/cli/start-app.sh new file mode 100644 index 00000000..70fa7442 --- /dev/null +++ b/test/e2e/cli/start-app.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +SRC=$(cd $(dirname "$0"); pwd) +source "${SRC}/../include.sh" + +echo -e "\033[1mRunning tests:\033[0m" + +cd $file_path/start-app + +# +# Direct command +# +$pm2 delete all + +$pm2 start "node -e 'setTimeout(function() { }, 100000); console.log(process.env.TEST)'" -l test.log --merge-logs +should 'should have started command' 'online' 1 +should 'should have not been restarted' 'restart_time: 0' 1 + +cat test.log | grep "undefined" +spec "should have printed undefined env var" + +TEST='ok' $pm2 restart 0 --update-env +cat test.log | grep "ok" + +should 'should have started command' 'online' 1 +should 'should have not been restarted' 'restart_time: 1' 1 +spec "should have printed undefined env var" + +# +# Direct command via Conf file +# +$pm2 delete all + +$pm2 start ecosystem.config.js +should 'should have started command' 'online' 1 +should 'should have not been restarted' 'restart_time: 0' 1 +cat test-conf.log | grep "test_val" +spec "should have printed the test_val" diff --git a/test/bash/startOrX.sh b/test/e2e/cli/startOrX.sh similarity index 93% rename from test/bash/startOrX.sh rename to test/e2e/cli/startOrX.sh index dd714e3d..ef985cba 100644 --- a/test/bash/startOrX.sh +++ b/test/e2e/cli/startOrX.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mStartOrX.sh:\033[0m" diff --git a/test/bash/watch.sh b/test/e2e/cli/watch.sh similarity index 98% rename from test/bash/watch.sh rename to test/e2e/cli/watch.sh index a47ec8a2..4a42c229 100644 --- a/test/bash/watch.sh +++ b/test/e2e/cli/watch.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/watch diff --git a/test/bash/docker.sh b/test/e2e/docker.sh similarity index 98% rename from test/bash/docker.sh rename to test/e2e/docker.sh index dcbeed6d..09174b73 100644 --- a/test/bash/docker.sh +++ b/test/e2e/docker.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" # Docker should function dshould { diff --git a/test/e2e/file-descriptor.sh b/test/e2e/file-descriptor.sh new file mode 100644 index 00000000..05df60ac --- /dev/null +++ b/test/e2e/file-descriptor.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +# +# LSOF check +# + +SRC=$(cd $(dirname "$0"); pwd) +source "${SRC}/../include.sh" + +cd $file_path + +echo "################## RELOAD ###################" + +# lsof -c PM2 > /tmp/no_pm2_out.dat + +# $pm2 list + +# sleep 1 +# lsof -c PM2 > /tmp/empty_pm2_out.dat + +# $pm2 start echo.js -i 3 +# $pm2 start killtoofast.js -i 3 +# $pm2 delete all + +# sleep 3 +# lsof -c PM2 > /tmp/empty_pm2_out2.dat + +# OUT1=`cat /tmp/empty_pm2_out.dat | wc -l` +# OUT2=`cat /tmp/empty_pm2_out2.dat | wc -l` + +# if [ $OUT1 -eq $OUT2 ]; then +# success "All file descriptors have been closed" +# else +# fail "Some file descriptors are still open" +# fi + +# $pm2 start killtoofast.js -i 6 +# $pm2 kill + +# rm /tmp/no_pm2_out.dat +# rm /tmp/no_pm2_out2.dat +# rm /tmp/empty_pm2_out.dat +# rm /tmp/empty_pm2_out2.dat + +# sleep 6 +> /tmp/no_pm_pm2_out.dat +> /tmp/no_pm_pm2_out2.dat + +lsof -c PM2 > /tmp/no_pm2_out2.dat +diff /tmp/no_pm2_out.dat /tmp/no_pm2_out2.dat + +if [ $? == "0" ]; then + success "All file descriptors have been closed" +else + fail "Some file descriptors are still open" +fi + +rm /tmp/no_pm2_out.dat +rm /tmp/no_pm2_out2.dat +rm /tmp/empty_pm2_out.dat +rm /tmp/empty_pm2_out2.dat diff --git a/test/bash/include.sh b/test/e2e/include.sh similarity index 71% rename from test/bash/include.sh rename to test/e2e/include.sh index 1abf9199..af6f9f79 100644 --- a/test/bash/include.sh +++ b/test/e2e/include.sh @@ -7,11 +7,30 @@ node="`type -P node`" -pm2="`type -P node` `pwd`/bin/pm2" +pm2_path=`pwd`/bin/pm2 -file_path="test/fixtures" +if [ ! -f $pm2_path ]; +then + pm2_path=`pwd`/../bin/pm2 + if [ ! -f $pm2_path ]; + then + pm2_path=`pwd`/../../bin/pm2 + fi +fi -#set -o verbose +pm2="$node $pm2_path" + +SRC=$(cd $(dirname "$0"); pwd) +file_path="${SRC}/../fixtures" + +if [ ! -d $file_path ]; +then + file_path="${SRC}/../../fixtures" + if [ ! -d $file_path ]; + then + file_path="${SRC}/../../../fixtures" + fi +fi $pm2 kill $pm2 link delete diff --git a/test/bash/daemon-paths-override.sh b/test/e2e/internals/daemon-paths-override.sh similarity index 63% rename from test/bash/daemon-paths-override.sh rename to test/e2e/internals/daemon-paths-override.sh index 31b65b2d..43b6b164 100644 --- a/test/bash/daemon-paths-override.sh +++ b/test/e2e/internals/daemon-paths-override.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" $pm2 kill rm /tmp/.toto.pid @@ -10,6 +10,6 @@ rm /tmp/.toto.pid PM2_PID_FILE_PATH=/tmp/.toto.pid $pm2 ls sleep 2 -test -f /tmp/.toto.pid +test -f /tmp/.toto.pid -spec 'should have picked the pm2 pid path' \ No newline at end of file +spec 'should have picked the pm2 pid path' diff --git a/test/bash/increment-var.sh b/test/e2e/internals/increment-var.sh similarity index 98% rename from test/bash/increment-var.sh rename to test/e2e/internals/increment-var.sh index 7deb2377..ad4c3f3e 100644 --- a/test/bash/increment-var.sh +++ b/test/e2e/internals/increment-var.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/increment-var/ @@ -120,4 +120,4 @@ should "start 2 processes" "online" 2 should "start one process with NODE_APP_INSTANCE at 0" "NODE_APP_INSTANCE: 0" 1 should "start one process with NODE_APP_INSTANCE at 1" "NODE_APP_INSTANCE: 1" 1 should "start one process with PORT at 3000" "PORT: 3000" 2 -should "start one process with PORT at 3001" "PORT: 3001" 2 \ No newline at end of file +should "start one process with PORT at 3001" "PORT: 3001" 2 diff --git a/test/bash/infinite-loop.sh b/test/e2e/internals/infinite-loop.sh similarity index 96% rename from test/bash/infinite-loop.sh rename to test/e2e/internals/infinite-loop.sh index 809c71ae..f0528251 100644 --- a/test/bash/infinite-loop.sh +++ b/test/e2e/internals/infinite-loop.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/listen-timeout.sh b/test/e2e/internals/listen-timeout.sh similarity index 86% rename from test/bash/listen-timeout.sh rename to test/e2e/internals/listen-timeout.sh index d9d9cc40..4d874e3d 100644 --- a/test/bash/listen-timeout.sh +++ b/test/e2e/internals/listen-timeout.sh @@ -3,13 +3,14 @@ #export PM2_GRACEFUL_LISTEN_TIMEOUT=1000 SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/listen-timeout/ echo -e "\033[1mENV REFRESH\033[0m" $pm2 start wait-ready.js -i 1 --wait-ready --listen-timeout 5000 -timeout 2 $pm2 reload all +$pm2 reload all & +sleep 2 should 'should have started 1 clustered app' 'online' 1 should 'should restart processes with new name' 'restart_time: 1' 1 diff --git a/test/bash/options-via-env.sh b/test/e2e/internals/options-via-env.sh similarity index 93% rename from test/bash/options-via-env.sh rename to test/e2e/internals/options-via-env.sh index c2049f50..bd644bf5 100644 --- a/test/bash/options-via-env.sh +++ b/test/e2e/internals/options-via-env.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/promise.sh b/test/e2e/internals/promise.sh similarity index 98% rename from test/bash/promise.sh rename to test/e2e/internals/promise.sh index a4c1b6cb..fb36c2e1 100644 --- a/test/bash/promise.sh +++ b/test/e2e/internals/promise.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/promise/ diff --git a/test/bash/signal.sh b/test/e2e/internals/signal.sh similarity index 93% rename from test/bash/signal.sh rename to test/e2e/internals/signal.sh index 3fb15af9..2182440b 100644 --- a/test/bash/signal.sh +++ b/test/e2e/internals/signal.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path @@ -15,7 +15,7 @@ $pm2 start signal.js -i 2 OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` cat /dev/null > $OUT_LOG -$pm2 sendSignal SIGUSR2 signal.js +$pm2 sendSignal SIGUSR2 signal sleep 1 OUT=`grep "SIGUSR2" "$OUT_LOG" | wc -l` diff --git a/test/bash/source_map.sh b/test/e2e/internals/source_map.sh similarity index 97% rename from test/bash/source_map.sh rename to test/e2e/internals/source_map.sh index 896f6b75..368deee4 100644 --- a/test/bash/source_map.sh +++ b/test/e2e/internals/source_map.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/start-consistency.sh b/test/e2e/internals/start-consistency.sh similarity index 94% rename from test/bash/start-consistency.sh rename to test/e2e/internals/start-consistency.sh index f7a9c682..ada5d84a 100644 --- a/test/bash/start-consistency.sh +++ b/test/e2e/internals/start-consistency.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/wait-ready-event.sh b/test/e2e/internals/wait-ready-event.sh similarity index 88% rename from test/bash/wait-ready-event.sh rename to test/e2e/internals/wait-ready-event.sh index c4a326c6..f6c19d1b 100644 --- a/test/bash/wait-ready-event.sh +++ b/test/e2e/internals/wait-ready-event.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/wait_ready_event @@ -21,7 +21,8 @@ should 'should have started 1 forked app ' 'online' 1 $pm2 delete all ##### start without sending event and ask to wait (fork mode) -timeout 5 $pm2 start http-wait-start_nocb.js --wait-ready --listen-timeout=8000 +$pm2 start http-wait-start_nocb.js --wait-ready --listen-timeout=8000 & +sleep 5 should 'should be 1 forked launching state app waiting for ready event' 'launching' 1 $pm2 delete all @@ -41,6 +42,7 @@ should 'should have started 1 clustered app' 'online' 1 $pm2 delete all ##### start without sending event and ask to wait (cluster mode) -timeout 5 $pm2 start http-wait-start_nocb.js -i 1 --wait-ready --listen-timeout=8000 +$pm2 start http-wait-start_nocb.js -i 1 --wait-ready --listen-timeout=8000 & +sleep 5 should 'should be 1 clustered launching state app waiting for ready event' 'launching' 1 $pm2 delete all diff --git a/test/bash/wrapped-fork.sh b/test/e2e/internals/wrapped-fork.sh similarity index 94% rename from test/bash/wrapped-fork.sh rename to test/e2e/internals/wrapped-fork.sh index 0731031c..475ea06f 100644 --- a/test/bash/wrapped-fork.sh +++ b/test/e2e/internals/wrapped-fork.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/log-custom.sh b/test/e2e/logs/log-custom.sh similarity index 96% rename from test/bash/log-custom.sh rename to test/e2e/logs/log-custom.sh index 8017f3c6..6a8ca50a 100644 --- a/test/bash/log-custom.sh +++ b/test/e2e/logs/log-custom.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/log-entire.sh b/test/e2e/logs/log-entire.sh similarity index 99% rename from test/bash/log-entire.sh rename to test/e2e/logs/log-entire.sh index 50150ac4..d09804bc 100644 --- a/test/bash/log-entire.sh +++ b/test/e2e/logs/log-entire.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" function head { echo -e "\x1B[1;35m$1\x1B[0m" @@ -17,7 +17,7 @@ function test_dir { echo "$result" } function test { - sleep 3 + sleep 5 out_file=$(test_dir "out") err_file=$(test_dir "err") diff --git a/test/bash/log-json.sh b/test/e2e/logs/log-json.sh similarity index 90% rename from test/bash/log-json.sh rename to test/e2e/logs/log-json.sh index d602cc8f..14cf9af7 100644 --- a/test/bash/log-json.sh +++ b/test/e2e/logs/log-json.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/log-json/ @@ -11,7 +11,7 @@ rm output.log # fork mode json logs $pm2 start ecosystem.json --only one-echo -! test -f output.log +! test -f output.log sleep 2 @@ -24,7 +24,7 @@ rm output.log # cluster mode json logs $pm2 start ecosystem.json -i 2 --only one-echo-cluster -! test -f output.log +! test -f output.log sleep 2 @@ -39,7 +39,7 @@ CURRENT_YEAR=`date +"%Y"` $pm2 start ecosystem.json --only one-echo-date -! test -f output.log +! test -f output.log sleep 2 @@ -57,7 +57,7 @@ rm output.log $pm2 start ecosystem.json --only one-echo-cluster-date -! test -f output.log +! test -f output.log sleep 2 @@ -69,4 +69,4 @@ OUT=`cat output.log | grep -o "$CURRENT_YEAR" | wc -l` success "should contains custom timestamp in cluster mode" $pm2 delete all -rm output.log \ No newline at end of file +rm output.log diff --git a/test/bash/log-null.sh b/test/e2e/logs/log-null.sh similarity index 98% rename from test/bash/log-null.sh rename to test/e2e/logs/log-null.sh index ed8c6b2a..5007d248 100644 --- a/test/bash/log-null.sh +++ b/test/e2e/logs/log-null.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path @@ -166,4 +166,4 @@ spec "out log shouldnt exist with /dev/null in fork mode" ! test -f ~/.pm2/logs/echo-error.log spec "error log shouldnt exist with /dev/null in fork mode" -$pm2 delete all \ No newline at end of file +$pm2 delete all diff --git a/test/bash/log-reload.sh b/test/e2e/logs/log-reload.sh similarity index 96% rename from test/bash/log-reload.sh rename to test/e2e/logs/log-reload.sh index 0227a6e8..ce64f5c6 100644 --- a/test/bash/log-reload.sh +++ b/test/e2e/logs/log-reload.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/log-timestamp.sh b/test/e2e/logs/log-timestamp.sh similarity index 98% rename from test/bash/log-timestamp.sh rename to test/e2e/logs/log-timestamp.sh index 1a4d065f..d5308cbd 100644 --- a/test/bash/log-timestamp.sh +++ b/test/e2e/logs/log-timestamp.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" function head { echo -e "\x1B[1;35m$1\x1B[0m" diff --git a/test/bash/cron-system.sh b/test/e2e/misc/cron-system.sh similarity index 85% rename from test/bash/cron-system.sh rename to test/e2e/misc/cron-system.sh index 96b05b89..d9dbbe0e 100644 --- a/test/bash/cron-system.sh +++ b/test/e2e/misc/cron-system.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path @@ -16,12 +16,12 @@ spec "Should cron restart echo.js" $pm2 delete all -> mock-0.log +> mock.log $pm2 start cron/mock-cron.js -o mock.log sleep 3 should 'should app been restarted when cron in fork mode' 'restart_time: 0' 0 -cat mock-0.log | grep "SIGINT" +cat mock.log | grep "SIGINT" spec "1# Should cron exit call SIGINT handler" $pm2 delete all @@ -29,7 +29,7 @@ $pm2 delete all $pm2 start cron/mock-cron.js -o mock.log -i 1 sleep 3 should 'should app been restarted when cron in cluster mode' 'restart_time: 0' 0 -cat mock-0.log | grep "SIGINT" +cat mock.log | grep "SIGINT" spec "2# Should cron exit call SIGINT handler" $pm2 delete all @@ -38,7 +38,7 @@ $pm2 delete all $pm2 start cron/mock-cron-no-exit.js -o mock.log sleep 3 should 'should app been restarted' 'restart_time: 0' 0 -cat mock-0.log | grep "SIGINT" +cat mock.log | grep "SIGINT" spec "3# Should cron exit call SIGINT handler" @@ -52,7 +52,7 @@ spec "Should cron restart delayed sigint" sleep 100 -cat cron-0.log | grep "SIGINT cb called" +cat cron.log | grep "SIGINT cb called" spec "Should cron exit call SIGINT handler" should 'should app been restarted' 'restart_time: 1' 1 diff --git a/test/bash/inside-pm2.sh b/test/e2e/misc/inside-pm2.sh similarity index 87% rename from test/bash/inside-pm2.sh rename to test/e2e/misc/inside-pm2.sh index bfb8c45e..6646e8fb 100644 --- a/test/bash/inside-pm2.sh +++ b/test/e2e/misc/inside-pm2.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path @@ -12,7 +12,7 @@ echo -e "\033[1mRunning tests:\033[0m" #################################################################### TEST_VARIABLE='hello1' $pm2 start startProcessInsidePm2.json ->inside-out-1.log +>inside-out.log sleep 1 @@ -25,12 +25,12 @@ $pm2 list should 'child process should be started' 'pm_id: 1' 2 should 'restarted status should be zero' "restart_time: 0" 2 -grep "hello1" inside-out-1.log &> /dev/null +grep "hello1" inside-out.log &> /dev/null spec "Child should have hello1 variable" TEST_VARIABLE='hello2' $pm2 restart "insideProcess" --update-env sleep 1 -grep "hello2" inside-out-1.log &> /dev/null +grep "hello2" inside-out.log &> /dev/null spec "Child should have hello2 variable after restart" # Call bash script that restarts app diff --git a/test/bash/instance-number.sh b/test/e2e/misc/instance-number.sh similarity index 76% rename from test/bash/instance-number.sh rename to test/e2e/misc/instance-number.sh index 61b46e9b..fe2ba630 100644 --- a/test/bash/instance-number.sh +++ b/test/e2e/misc/instance-number.sh @@ -1,10 +1,10 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path $pm2 start server.js -i -100 should 'should have started 1 processes' 'online' 1 -$pm2 delete all \ No newline at end of file +$pm2 delete all diff --git a/test/bash/misc.sh b/test/e2e/misc/misc.sh similarity index 98% rename from test/bash/misc.sh rename to test/e2e/misc/misc.sh index 42dbb45a..939e7ae4 100644 --- a/test/bash/misc.sh +++ b/test/e2e/misc/misc.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/nvm-node-version.sh b/test/e2e/misc/nvm-node-version.sh similarity index 97% rename from test/bash/nvm-node-version.sh rename to test/e2e/misc/nvm-node-version.sh index cb39919e..d9436f41 100644 --- a/test/bash/nvm-node-version.sh +++ b/test/e2e/misc/nvm-node-version.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mRunning tests:\033[0m" diff --git a/test/bash/startup.sh b/test/e2e/misc/startup.sh similarity index 96% rename from test/bash/startup.sh rename to test/e2e/misc/startup.sh index 84d43b5d..f34ea214 100644 --- a/test/bash/startup.sh +++ b/test/e2e/misc/startup.sh @@ -7,7 +7,7 @@ then fi SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path $pm2 startup upstart -u $USER --hp $HOME --service-name abcdef diff --git a/test/bash/versioning-cmd.sh b/test/e2e/misc/versioning-cmd.sh similarity index 97% rename from test/bash/versioning-cmd.sh rename to test/e2e/misc/versioning-cmd.sh index 487a227b..f1bdad07 100644 --- a/test/bash/versioning-cmd.sh +++ b/test/e2e/misc/versioning-cmd.sh @@ -1,10 +1,12 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path +rm -rf app-playground + git clone https://github.com/keymetrics/app-playground.git cd app-playground diff --git a/test/bash/vizion.sh b/test/e2e/misc/vizion.sh similarity index 94% rename from test/bash/vizion.sh rename to test/e2e/misc/vizion.sh index d7e896d1..b65b1942 100644 --- a/test/bash/vizion.sh +++ b/test/e2e/misc/vizion.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/get-set.sh b/test/e2e/modules/get-set.sh similarity index 92% rename from test/bash/get-set.sh rename to test/e2e/modules/get-set.sh index 76cfdc47..c1a64984 100644 --- a/test/bash/get-set.sh +++ b/test/e2e/modules/get-set.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/module-safeguard.sh b/test/e2e/modules/module-safeguard.sh similarity index 97% rename from test/bash/module-safeguard.sh rename to test/e2e/modules/module-safeguard.sh index 4dcc8221..6b677879 100644 --- a/test/bash/module-safeguard.sh +++ b/test/e2e/modules/module-safeguard.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mRunning tests:\033[0m" diff --git a/test/bash/module.sh b/test/e2e/modules/module.sh similarity index 70% rename from test/bash/module.sh rename to test/e2e/modules/module.sh index 6a7a0a44..715abb2d 100644 --- a/test/bash/module.sh +++ b/test/e2e/modules/module.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mRunning tests:\033[0m" @@ -16,24 +16,30 @@ $pm2 kill # # +$pm2 unset pm2-probe + $pm2 set 'pm2-probe:config1xxx' true -$pm2 install pm2-probe +$pm2 install pm2-probe@latest spec "Should install a module" should 'should app be online' 'online' 1 -$pm2 install pm2-probe +$pm2 install pm2-probe@latest spec "Should update a module" should 'should app be online' 'online' 1 ls ~/.pm2/modules/pm2-probe spec "Module should be installed" + +# Default configuration variable in package.json (under "config" attribute) +should 'should have default config variable via package.json' "var2: false" 4 + # # Should configuration variable be present two times # one time in the raw env, and a second time prefixed with the module name # -exists '1# should have config variable' "config1xxx: 'true'" 4 +exists '1# should have config variable' "config1xxx: 'true'" 6 # # Change variable value @@ -98,21 +104,18 @@ $pm2 install . sleep 0.5 spec 'Should have installed module' -# Default configuration variable in package.json (under "config" attribute) -# Only 2 occurences because this is the first start -should 'should have config variable' "var2: false" 2 -# Override environment variable -$pm2 set example-module:var2 true -sleep 0.5 -should 'should module been restarted after setting variable' 'restart_time: 1' 1 +# # Override environment variable +# $pm2 set example-module:var2 true +# sleep 0.5 +# should 'should module been restarted after setting variable' 'restart_time: 1' 1 -# 4 occurences because of a restart -should 'should have config variable modified' "var2: 'true'" 4 +# # 4 occurences because of a restart +# should 'should have config variable modified' "var2: 'true'" 4 -$pm2 set example-module:newvar true -sleep 0.5 -should 'should module been restarted after setting variable' 'restart_time: 2' 1 +# $pm2 set example-module:newvar true +# sleep 0.5 +# should 'should module been restarted after setting variable' 'restart_time: 2' 1 -# 4 occurences because of a restart -should 'should have config variable modified' "newvar: 'true'" 4 +# # 4 occurences because of a restart +# should 'should have config variable modified' "newvar: 'true'" 4 diff --git a/test/bash/app-config-update.sh b/test/e2e/process-file/app-config-update.sh similarity index 82% rename from test/bash/app-config-update.sh rename to test/e2e/process-file/app-config-update.sh index 8ef0a532..1859007f 100644 --- a/test/bash/app-config-update.sh +++ b/test/e2e/process-file/app-config-update.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" export PM2_GRACEFUL_TIMEOUT=1000 export PM2_GRACEFUL_LISTEN_TIMEOUT=1000 @@ -58,14 +58,10 @@ $pm2 reload app-config-update/echo.js --node-args="--harmony" $pm2 prettylist | grep "node_args: \[ '--harmony' \]" spec "Should application have one node argument" -$pm2 gracefulReload app-config-update/echo.js --node-args="--harmony" -$pm2 prettylist | grep "node_args: \[ '--harmony' \]" -spec "Should application have two node arguments" - $pm2 prettylist | grep "node_args" spec "Should have found parameter" # Now set node-args to null -$pm2 gracefulReload app-config-update/echo.js --node-args=null +$pm2 reload app-config-update/echo.js --node-args=null # Should not find node_args anymore $pm2 prettylist | grep "node_args" ispec "Should have deleted cli parameter when passing null" @@ -74,8 +70,3 @@ $pm2 reload echo --name="new-name" $pm2 reset all $pm2 restart new-name should 'should reload processes with new name' 'restart_time: 1' 1 - -$pm2 gracefulReload new-name --name="new-name-2" -$pm2 reset all -$pm2 restart new-name-2 -should 'should graceful reload processes with new name' 'restart_time: 1' 1 diff --git a/test/bash/append-env-to-name.sh b/test/e2e/process-file/append-env-to-name.sh similarity index 88% rename from test/bash/append-env-to-name.sh rename to test/e2e/process-file/append-env-to-name.sh index cbecbd15..9acb8760 100644 --- a/test/bash/append-env-to-name.sh +++ b/test/e2e/process-file/append-env-to-name.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path @@ -11,4 +11,4 @@ $pm2 start append-env-to-name.json --env dev should 'have started app with name web-dev' "name: 'web-dev'" 3 $pm2 start append-env-to-name.json --env prod -should 'have started same app with name : web-prod' "name: 'web-prod'" 3 \ No newline at end of file +should 'have started same app with name : web-prod' "name: 'web-prod'" 3 diff --git a/test/bash/homogen-json-action.sh b/test/e2e/process-file/homogen-json-action.sh similarity index 97% rename from test/bash/homogen-json-action.sh rename to test/e2e/process-file/homogen-json-action.sh index 81c5b306..2111f839 100644 --- a/test/bash/homogen-json-action.sh +++ b/test/e2e/process-file/homogen-json-action.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mRunning tests:\033[0m" diff --git a/test/bash/js-configuration.sh b/test/e2e/process-file/js-configuration.sh similarity index 58% rename from test/bash/js-configuration.sh rename to test/e2e/process-file/js-configuration.sh index 6888ddd2..b5de850d 100644 --- a/test/bash/js-configuration.sh +++ b/test/e2e/process-file/js-configuration.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/js-configuration $pm2 start ecosystem.config.js -should 'should have started 1 processes' 'online' 1 \ No newline at end of file +should 'should have started 1 processes' 'online' 1 diff --git a/test/bash/json-file.sh b/test/e2e/process-file/json-file.sh similarity index 93% rename from test/bash/json-file.sh rename to test/e2e/process-file/json-file.sh index 50c3ff35..c6c984f5 100644 --- a/test/bash/json-file.sh +++ b/test/e2e/process-file/json-file.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path echo -e "\033[1mRunning tests for json files :\033[0m" @@ -36,18 +36,13 @@ sleep 1 should 'should reload processes' 'online' 6 should 'should all script been restarted one time' 'restart_time: 2' 6 -$pm2 gracefulReload all.json -sleep 1 -should 'should graceful reload processes' 'online' 6 -should 'should all script been restarted one time' 'restart_time: 3' 6 - ## ## Smart restart ## $pm2 start all.json sleep 1 should 'should smart restart processes' 'online' 6 -should 'should all script been restarted one time' 'restart_time: 4' 6 +should 'should all script been restarted one time' 'restart_time: 3' 6 $pm2 stop all.json sleep 1 diff --git a/test/bash/json-reload.sh b/test/e2e/process-file/json-reload.sh similarity index 98% rename from test/bash/json-reload.sh rename to test/e2e/process-file/json-reload.sh index 64841bb9..2698fa87 100644 --- a/test/bash/json-reload.sh +++ b/test/e2e/process-file/json-reload.sh @@ -2,7 +2,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path diff --git a/test/bash/yaml-configuration.sh b/test/e2e/process-file/yaml-configuration.sh similarity index 96% rename from test/bash/yaml-configuration.sh rename to test/e2e/process-file/yaml-configuration.sh index 4286309c..51c4e71a 100644 --- a/test/bash/yaml-configuration.sh +++ b/test/e2e/process-file/yaml-configuration.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" cd $file_path/yaml-configuration $pm2 start non-existent.yaml diff --git a/test/bash/pull.sh b/test/e2e/pull.sh similarity index 98% rename from test/bash/pull.sh rename to test/e2e/pull.sh index 54e279f6..d12e68ea 100644 --- a/test/bash/pull.sh +++ b/test/e2e/pull.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" +source "${SRC}/../include.sh" echo -e "\033[1mRunning tests:\033[0m" diff --git a/test/fixtures/child_no_http.js b/test/fixtures/child_no_http.js index 83ec9517..f38d18c4 100644 --- a/test/fixtures/child_no_http.js +++ b/test/fixtures/child_no_http.js @@ -1,4 +1,4 @@ -var pmx = require('pmx').init({ +var pmx = require('@pm2/io').init({ http: false }); diff --git a/test/fixtures/custom_actions/index.js b/test/fixtures/custom_actions/index.js index 1efbe259..b68bdd9e 100644 --- a/test/fixtures/custom_actions/index.js +++ b/test/fixtures/custom_actions/index.js @@ -1,5 +1,5 @@ -var pmx = require('pmx'); +var pmx = require('@pm2/io'); pmx.action('ping', function(reply) { return reply({ 'pong' : 'hehe' }) diff --git a/test/fixtures/events/custom_action.js b/test/fixtures/events/custom_action.js index 443b26bf..693d8b9a 100755 --- a/test/fixtures/events/custom_action.js +++ b/test/fixtures/events/custom_action.js @@ -1,5 +1,5 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); axm.action('refresh:db', function(reply) { console.log('Refreshing'); diff --git a/test/fixtures/events/custom_action_with_params.js b/test/fixtures/events/custom_action_with_params.js index 8d0b6630..023c75e7 100755 --- a/test/fixtures/events/custom_action_with_params.js +++ b/test/fixtures/events/custom_action_with_params.js @@ -1,5 +1,5 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); axm.action('refresh:db', { comment : 'Refresh the database' }, function(reply) { console.log('Refreshing'); diff --git a/test/fixtures/events/own_event.js b/test/fixtures/events/own_event.js index c4959741..a0df61bc 100644 --- a/test/fixtures/events/own_event.js +++ b/test/fixtures/events/own_event.js @@ -1,5 +1,5 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); setInterval(function() { axm.emit('user:register', { diff --git a/test/fixtures/graceful-exit-no-listen.js b/test/fixtures/graceful-exit-no-listen.js index 37cfba2d..814d3845 100644 --- a/test/fixtures/graceful-exit-no-listen.js +++ b/test/fixtures/graceful-exit-no-listen.js @@ -2,7 +2,7 @@ /* * Example of graceful exit that does not listen * - * $ pm2 gracefulReload all + * $ pm2 reload all */ process.on('message', function(msg) { diff --git a/test/fixtures/graceful-exit-send.js b/test/fixtures/graceful-exit-send.js index 7142c3f5..94d461f0 100644 --- a/test/fixtures/graceful-exit-send.js +++ b/test/fixtures/graceful-exit-send.js @@ -2,7 +2,7 @@ /* * Example of graceful exit that does not listen but sends 'online' * - * $ pm2 gracefulReload all + * $ pm2 reload all */ process.on('message', function(msg) { diff --git a/test/fixtures/graceful-exit.js b/test/fixtures/graceful-exit.js index 43e8212a..5b1461a1 100644 --- a/test/fixtures/graceful-exit.js +++ b/test/fixtures/graceful-exit.js @@ -2,7 +2,7 @@ /* * Example of graceful exit * - * $ pm2 gracefulReload all + * $ pm2 reload all */ process.on('message', function(msg) { diff --git a/test/fixtures/homogen-json-action/http.js b/test/fixtures/homogen-json-action/http.js index 61d823c7..221eafbe 100644 --- a/test/fixtures/homogen-json-action/http.js +++ b/test/fixtures/homogen-json-action/http.js @@ -1,4 +1,4 @@ -var pmx = require('pmx').init({ +var pmx = require('@pm2/io').init({ http : true }); diff --git a/test/fixtures/interface/http_transaction.js b/test/fixtures/interface/http_transaction.js index 17a91be5..1a3d9a69 100644 --- a/test/fixtures/interface/http_transaction.js +++ b/test/fixtures/interface/http_transaction.js @@ -1,6 +1,9 @@ -var axm = require('pmx'); -axm.http(); +var axm = require('@pm2/io'); + +axm.init({ + http: true +}) var http = require('http'); diff --git a/test/fixtures/interface/human_event.js b/test/fixtures/interface/human_event.js index 925a72a3..e1807256 100644 --- a/test/fixtures/interface/human_event.js +++ b/test/fixtures/interface/human_event.js @@ -1,5 +1,5 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); setInterval(function() { axm.emit('content:page:created', { diff --git a/test/fixtures/interface/process_exception.js b/test/fixtures/interface/process_exception.js index d4b0c937..23575d2b 100644 --- a/test/fixtures/interface/process_exception.js +++ b/test/fixtures/interface/process_exception.js @@ -1,5 +1,5 @@ -var axm = require('pmx'); +var axm = require('@pm2/io'); axm.catchAll(); diff --git a/test/fixtures/interface/process_exception_with_logs.js b/test/fixtures/interface/process_exception_with_logs.js index 1200055b..da8e43e7 100644 --- a/test/fixtures/interface/process_exception_with_logs.js +++ b/test/fixtures/interface/process_exception_with_logs.js @@ -1,5 +1,5 @@ -var pmx = require('pmx'); +var pmx = require('@pm2/io'); pmx.action('exception', function(reply) { console.log('Im going to crash'); diff --git a/test/fixtures/module-fixture/node_modules/pmx/.npmignore b/test/fixtures/module-fixture/node_modules/pmx/.npmignore deleted file mode 100644 index 3fcc6400..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/.npmignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules -*.log -*.log -test/child -*.iml -.idea/** -sample/pmx-server-stats diff --git a/test/fixtures/module-fixture/node_modules/pmx/.travis.yml b/test/fixtures/module-fixture/node_modules/pmx/.travis.yml deleted file mode 100644 index 7e7d37b1..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: node_js -branches: - only: - - master - - development -node_js: - - "iojs" - - "0.12" - - "0.11" - - "0.10" diff --git a/test/fixtures/module-fixture/node_modules/pmx/CHANGELOG.md b/test/fixtures/module-fixture/node_modules/pmx/CHANGELOG.md deleted file mode 100644 index e89f1868..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/CHANGELOG.md +++ /dev/null @@ -1,18 +0,0 @@ - -# 0.2.27 - -- Remove debug message -- Rename module -- Auto instanciation - -# 0.2.25 - -- Add ip address on each transaction - -# 0.2.24 - -- Add unit option for Histogram and Meter - -# 0.2.23 - -- Include Counter, Meter, Metric and Histogram diff --git a/test/fixtures/module-fixture/node_modules/pmx/README.md b/test/fixtures/module-fixture/node_modules/pmx/README.md deleted file mode 100644 index 1e4a60b0..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/README.md +++ /dev/null @@ -1,303 +0,0 @@ - -# Driver for Keymetrics - -![Keymetrics](https://keymetrics.io/assets/images/application-demo.png) - -PMX is a module that allows you to create advanced interactions with Keymetrics. - -With it you can: -- Trigger remote actions / functions -- Analyze custom metrics / variables (with utilities like Histogram/Counter/Metric/Meters) -- Report errors (uncaught exceptions and custom errors) -- Emit events -- Analyze HTTP latency - -# Installation - -![Build Status](https://api.travis-ci.org/keymetrics/pmx.png?branch=master) - -Install PMX and add it to your package.json via: - -```bash -$ npm install pmx --save -``` - -Then init the module to monitor HTTP, Errors and diverse metrics. -```javascript -var pmx = require('pmx').init(); // By default everything is enabled and ignore_routes is empty -``` -Or choose what to monitor. -```javascript -var pmx = require('pmx').init({ - http : true, // HTTP routes logging (default: true) - ignore_routes : [/socket\.io/, /notFound/], // Ignore http routes with this pattern (Default: []) - errors : true, // Exceptions loggin (default: true) - custom_probes : true, // Custom probes (default: true) - network : true, // Traffic usage monitoring (default: false) - ports : true // Shows which ports your app is listening on (default: false) -}); -``` - -# Custom monitoring - -## Emit Events - -Emit events and get historical and statistics: - -```javascript -var pmx = require('pmx'); - -pmx.emit('user:register', { - user : 'Alex registered', - email : 'thorustor@gmail.com' -}); -``` - -## Custom Action - -Trigger function from Keymetrics - -### Long running - -```javascript -var pmx = require('pmx'); - -pmx.action('db:clean', { comment : 'Description for this action' }, function(reply) { - clean.db(function() { - /** - * reply() must be called at the end of the action - */ - reply({success : true}); - }); -}); -``` - -## Errors - -Catch uncaught exceptions: -```javascript -var pmx = require('pmx').init(); -``` - -Attach more data from errors that happens in Express: -```javascript -var pmx = require('pmx'); - -app.get('/' ...); -app.post(...); - -app.use(pmx.expressErrorHandler()); -``` - -Trigger custom errors: -```javascript -var pmx = require('pmx'); - -pmx.notify({ success : false }); - -pmx.notify('This is an error'); - -pmx.notify(new Error('This is an error')); -``` - -## TCP network usage monitoring - -If you enable the flag `network: true` when you init pmx it will show network usage datas (download and upload) in realtime. - -If you enable the flag `ports: true` when you init pmx it will show which ports your app is listenting on. - - -## HTTP latency analysis - -Monitor routes, latency and codes. REST compliant. - -```javascript -pmx.http(); // You must do this BEFORE any require('http') -``` -Ignore some routes by passing a list of regular expressions. -```javascript -pmx.http({ - http : true, // (Default: true) - ignore_routes : [/socket\.io/, /notFound/] // Ignore http routes with this pattern (Default: []) -}); -``` -This can also be done via pmx.init() -```javascript -pmx.init({ - http : true, // (Default: true) - ignore_routes : [/socket\.io/, /notFound/] // Ignore http routes with this pattern (Default: []) -}); -``` - -**This module is enabled by default if you called pmx with the init() function.** - -## Measure - -Measure critical segments of you code thanks to 4 kind of probes: - -- Simple metrics: Values that can be read instantly - - Monitor variable value -- Counter: Things that increment or decrement - - Downloads being processed, user connected -- Meter: Things that are measured as events / interval - - Request per minute for a http server -- Histogram: Keeps a resevoir of statistically relevant values biased towards the last 5 minutes to explore their distribution - - Monitor the mean of execution of a query into database - -#### Common options - -- `name` : The probe name as is will be displayed on the **Keymetrics** dashboard -- `agg_type` : This param is optional, it can be `sum`, `max`, `min`, `avg` (default) or `none`. It will impact the way the probe data are aggregated within the **Keymetrics** backend. Use `none` if this is irrelevant (eg: constant or string value). - - -### Metric - -Values that can be read instantly. - -```javascript -var probe = pmx.probe(); - -var metric = probe.metric({ - name : 'Realtime user', - agg_type: 'max', - value : function() { - return Object.keys(users).length; - } -}); -``` - -### Counter - -Things that increment or decrement. - -```javascript -var probe = pmx.probe(); - -var counter = probe.counter({ - name : 'Downloads', - agg_type: 'sum' -}); - -http.createServer(function(req, res) { - counter.inc(); - req.on('end', function() { - counter.dec(); - }); -}); -``` - -### Meter - -Things that are measured as events / interval. - -```javascript -var probe = pmx.probe(); - -var meter = probe.meter({ - name : 'req/sec', - samples : 1, - timeframe : 60 -}); - -http.createServer(function(req, res) { - meter.mark(); - res.end({success:true}); -}); -``` -#### Options - -**samples** option is the rate unit. Defaults to **1** sec. - -**timeframe** option is the timeframe over which events will be analyzed. Defaults to **60** sec. - -### Histogram - -Keeps a resevoir of statistically relevant values biased towards the last 5 minutes to explore their distribution. - -```javascript -var probe = pmx.probe(); - -var histogram = probe.histogram({ - name : 'latency', - measurement : 'mean' -}); - -var latency = 0; - -setInterval(function() { - latency = Math.round(Math.random() * 100); - histogram.update(latency); -}, 100); -``` - -#### Options - -**measurement** option can be: - -- min: The lowest observed value. -- max: The highest observed value. -- sum: The sum of all observed values. -- variance: The variance of all observed values. -- mean: The average of all observed values. -- stddev: The stddev of all observed values. -- count: The number of observed values. -- median: 50% of all values in the resevoir are at or below this value. -- p75: See median, 75% percentile. -- p95: See median, 95% percentile. -- p99: See median, 99% percentile. -- p999: See median, 99.9% percentile. - -## Expose data (JSON object) - -```javascript -pmx.transpose('variable name', function() { return my_data }); - -// or - -pmx.tranpose({ - name : 'variable name', - value : function() { return my_data; } -}); -``` - -## Modules - -### Simple app - -``` -process.env.MODULE_DEBUG = true; - -var pmx = require('pmx'); - -var conf = pmx.initModule(); -``` - -# Beta - -### Long running with data emitter (scoped action) - -A scoped action is an action that can emit logs related to this action. - -```javascript -var pmx = require('pmx'); - -pmx.scopedAction('scoped:action', function(options, res) { - var i = setInterval(function() { - // Emit progress data - if (error) - res.error('oops'); - else - res.send('this is a chunk of data'); - }, 1000); - - setTimeout(function() { - clearInterval(i); - return res.end(); - }, 8000); -}); -``` - - -# License - -MIT diff --git a/test/fixtures/module-fixture/node_modules/pmx/examples/package.json b/test/fixtures/module-fixture/node_modules/pmx/examples/package.json deleted file mode 100644 index acac5aa1..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/examples/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "example-module", - "version": "0.3.21", - "description": "Keymetrics++ and PM2 adapter", - "main": "scoped-action.js", - "dependencies": { - }, - "scripts": { - "test": "DEBUG='axm:*' mocha test/*.mocha.js" - }, - "repository": { - "type": "git", - "url": "https://github.com/keymetrics/pmx.git" - }, - "config" : { - "aconfig-var" : true, - "var2" : false - }, - "author": "Keymetrics I/O", - "license": "MIT" -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/examples/scoped-action.js b/test/fixtures/module-fixture/node_modules/pmx/examples/scoped-action.js deleted file mode 100644 index c6280c1b..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/examples/scoped-action.js +++ /dev/null @@ -1,70 +0,0 @@ - -var pmx = require('..'); - - -var conf = pmx.initModule({ - - widget : { - type : 'generic', - logo : 'https://app.keymetrics.io/img/logo/keymetrics-300.png', - - // 0 = main element - // 1 = secondary - // 2 = main border - // 3 = secondary border - theme : ['#141A1F', '#222222', '#3ff', '#3ff'], - - el : { - probes : true, - actions : true - }, - - block : { - actions : true, - issues : true, - meta : true - } - - // Status - // Green / Yellow / Red - } -}); - - -pmx.scopedAction('testo', function(data, emitter) { - var i = setInterval(function() { - emitter.send('datard'); - }, 100); - - setTimeout(function() { - - emitter.end('end'); - clearInterval(i); - }, 3000); -}); - -var spawn = require('child_process').spawn; - -pmx.scopedAction('long running lsof', function(data, res) { - var child = spawn('lsof', []); - - child.stdout.on('data', function(chunk) { - chunk.toString().split('\n').forEach(function(line) { - res.send(line); - }); - }); - - child.stdout.on('end', function(chunk) { - res.end('end'); - }); - -}); - - -pmx.action('simple action', function(reply) { - return reply({success:true}); -}); - -pmx.action('simple with arg', function(opts,reply) { - return reply(opts); -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/index.js b/test/fixtures/module-fixture/node_modules/pmx/index.js deleted file mode 100644 index 055544a9..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/index.js +++ /dev/null @@ -1,2 +0,0 @@ - -module.exports = exports = require("./lib"); diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/Probe.js b/test/fixtures/module-fixture/node_modules/pmx/lib/Probe.js deleted file mode 100644 index d5f73c4f..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/Probe.js +++ /dev/null @@ -1,180 +0,0 @@ - -var Counter = require('./utils/probes/Counter.js'); -var Histogram = require('./utils/probes/Histogram.js'); -var Meter = require('./utils/probes/Meter.js'); - -var Transport = require('./utils/transport.js'); - -var debug = require('debug')('axm:probe'); -var Probe = {}; - -Probe._started = false; -Probe._var = {}; - -Probe.AVAILABLE_AGG_TYPES = ['avg', 'min', 'max', 'sum', 'none']; -Probe.AVAILABLE_MEASUREMENTS = [ - 'min', - 'max', - 'sum', - 'count', - 'variance', - 'mean', - 'stddev', - 'median', - 'p75', - 'p95', - 'p99', - 'p999' -]; -Probe.default_aggregation = 'avg'; - -function cookData(data) { - var cooked_data = {}; - - Object.keys(data).forEach(function(probe_name) { - var value = data[probe_name].value; - - if (typeof(value) == 'function') - value = value(); - else - value = value; - - cooked_data[probe_name] = { - value: value - }; - - if (data[probe_name].agg_type && - data[probe_name].agg_type != 'none') - cooked_data[probe_name].agg_type = data[probe_name].agg_type; - - }); - return cooked_data; -}; - -Probe.probe = function() { - - if (Probe._started == false) { - Probe._started = true; - - setInterval(function() { - Transport.send({ - type : 'axm:monitor', - data : cookData(Probe._var) - }); - }, 990); - } - - return { - /** - * This reflect data to keymetrics - * pmx.transpose('prop name', fn) - * - * or - * - * pmx.transpose({ - * name : 'variable name', - * data : function() { return value } - * }); - */ - transpose : function(variable_name, reporter) { - if (typeof variable_name === 'object') { - reporter = variable_name.data; - variable_name = variable_name.name; - } - - if (typeof reporter !== 'function') { - return console.error('[PMX] reporter is not a function'); - } - - Probe._var[variable_name] = { - value: reporter - }; - }, - metric : function(opts) { - var agg_type = opts.agg_type || Probe.default_aggregation; - - if (!opts.name) - return console.error('[Probe][Metric] Name not defined'); - if (typeof(opts.value) === 'undefined') - return console.error('[Probe][Metric] Value not defined'); - if (Probe.AVAILABLE_AGG_TYPES.indexOf(agg_type) == -1) - return console.error("[Probe][Metric] Unknown agg_type: %s", agg_type); - - if (opts.value) - Probe._var[opts.name] = { - value: opts.value, - agg_type: agg_type - }; - - return { - val : function() { - var value = Probe._var[opts.name].value; - - if (typeof(value) == 'function') - value = value(); - - return value; - }, - set : function(dt) { Probe._var[opts.name].value = dt } - } - }, - histogram : function(opts) { - if (!opts.name) - return console.error('[Probe][Histogram] Name not defined'); - opts.measurement = opts.measurement || 'mean'; - opts.unit = opts.unit || ''; - var agg_type = opts.agg_type || Probe.default_aggregation; - - if (Probe.AVAILABLE_MEASUREMENTS.indexOf(opts.measurement) == -1) - return console.error('[Probe][Histogram] Measure type %s does not exists', opts.measurement); - if (Probe.AVAILABLE_AGG_TYPES.indexOf(agg_type) == -1) - return console.error("[Probe][Metric] Unknown agg_type: %s", agg_type); - - var histogram = new Histogram(opts); - - Probe._var[opts.name] = { - value: function() { return (Math.round(histogram.val() * 100) / 100) + '' + opts.unit }, - agg_type: agg_type - }; - - return histogram; - }, - meter : function(opts) { - var agg_type = opts.agg_type || Probe.default_aggregation; - - if (!opts.name) - return console.error('[Probe][Meter] Name not defined'); - if (Probe.AVAILABLE_AGG_TYPES.indexOf(agg_type) == -1) - return console.error("[Probe][Metric] Unknown agg_type: %s", agg_type); - - opts.unit = opts.unit || ''; - - var meter = new Meter(opts); - - Probe._var[opts.name] = { - value: function() { return meter.val() + '' + opts.unit }, - agg_type: agg_type - }; - - return meter; - }, - counter : function(opts) { - var agg_type = opts.agg_type || Probe.default_aggregation; - - if (!opts.name) - return console.error('[Probe][Counter] Name not defined'); - if (Probe.AVAILABLE_AGG_TYPES.indexOf(agg_type) == -1) - return console.error("[Probe][Metric] Unknown agg_type: %s", agg_type); - - var counter = new Counter(); - - Probe._var[opts.name] = { - value: function() { return counter.val() }, - agg_type: agg_type - }; - return counter; - }, - } -}; - -module.exports = Probe; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/actions.js b/test/fixtures/module-fixture/node_modules/pmx/lib/actions.js deleted file mode 100644 index dd807d58..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/actions.js +++ /dev/null @@ -1,147 +0,0 @@ -var domain = require('domain'); -var debug = require('debug')('axm:events'); -var Common = require('./common.js'); -var Transport = require('./utils/transport.js'); - -var Actions = {}; - -Actions.action = function(action_name, opts, fn) { - if (!fn) { - fn = opts; - opts = null; - } - - if (!action_name) - return console.error('[PMX] action.action_name is missing'); - if (!fn) - return console.error('[PMX] emit.data is mission'); - - if (!process.send) { - debug('Process not running within PM2'); - return false; - } - - // Notify the action - Transport.send({ - type : 'axm:action', - data : { - action_name : action_name, - opts : opts, - arity : fn.length - } - }); - - function reply(data) { - Transport.send({ - type : 'axm:reply', - data : { - return : data, - action_name : action_name - } - }); - } - - process.on('message', function(data) { - if (!data) return false; - - // In case 2 arguments has been set but no options has been transmitted - if (fn.length === 2 && typeof(data) === 'string' && data === action_name) - return fn({}, reply); - - // In case 1 arguments has been set but options has been transmitted - if (fn.length === 1 && typeof(data) === 'object' && data.msg === action_name) - return fn(reply); - - /** - * Classical call - */ - if (typeof(data) === 'string' && data === action_name) - return fn(reply); - - /** - * If data is an object == v2 protocol - * Pass the opts as first argument - */ - if (typeof(data) === 'object' && data.msg === action_name) - return fn(data.opts, reply); - }); -}; - -Actions.scopedAction = function(action_name, fn) { - - if (!action_name) - return console.error('[PMX] action.action_name is missing'); - if (!fn) - return console.error('[PMX] callback is missing'); - - if (!process.send) { - debug('Process not running within PM2'); - return false; - } - - // Notify the action - Transport.send({ - type : 'axm:action', - data : { - action_name : action_name, - action_type : 'scoped' - } - }); - - process.on('message', function(data) { - if (!data - || data.uuid === undefined - || data.action_name === undefined) - return false; - - if (data.action_name === action_name) { - var res = { - send : function(dt) { - Transport.send({ - type : 'axm:scoped_action:stream', - data : { - data : dt, - uuid : data.uuid, - action_name : action_name - } - }); - }, - error : function(dt) { - Transport.send({ - type : 'axm:scoped_action:error', - data : { - data : dt, - uuid : data.uuid, - action_name : action_name - } - }); - }, - end : function(dt) { - Transport.send({ - type : 'axm:scoped_action:end', - data : { - data : dt, - uuid : data.uuid, - action_name : action_name - } - }); - } - }; - - var d = domain.create(); - - d.on('error', function(err) { - res.error({error : err}); - setTimeout(function() { - process.exit(1); - }, 300); - }); - - d.run(function() { - fn(data.opts || null, res); - }); - } - }); -}; - -module.exports = Actions; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/common.js b/test/fixtures/module-fixture/node_modules/pmx/lib/common.js deleted file mode 100644 index c1bce910..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/common.js +++ /dev/null @@ -1,6 +0,0 @@ - -var Common = module.exports = {}; - -Common.getDate = function getDate() { - return Math.round(Date.now() / 1000); -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/events.js b/test/fixtures/module-fixture/node_modules/pmx/lib/events.js deleted file mode 100644 index f09496bc..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/events.js +++ /dev/null @@ -1,26 +0,0 @@ - -var debug = require('debug')('axm:events'); -var Transport = require('./utils/transport.js'); -var Common = require('./common.js'); -var stringify = require('json-stringify-safe'); - -var Events = {}; - -Events.emit = function(name, data) { - if (!name) - return console.error('[AXM] emit.name is missing'); - if (!data) - return console.error('[AXM] emit.data is missing'); - - var inflight_obj = JSON.parse(stringify(data)); - - inflight_obj.__name = name; - - Transport.send({ - type : 'human:event', - data : inflight_obj - }, true); - return false; -}; - -module.exports = Events; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/index.js b/test/fixtures/module-fixture/node_modules/pmx/lib/index.js deleted file mode 100644 index 55803e4a..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/index.js +++ /dev/null @@ -1,66 +0,0 @@ - -var Events = require('./events.js'); -var Actions = require('./actions.js'); -var Notify = require('./notify.js'); -var Transaction = require('./transaction.js'); -var Network = require('./network.js'); -var Monitor = require('./monitor.js'); -var Profiling = require('./probes/profiling.js'); -var Probe = require('./Probe.js'); - -var Pm2Module = require('./pm2_module.js'); - -var util = require('util'); - -var Export = {}; - -/** - * Flatten API - */ -util._extend(Export, Events); -util._extend(Export, Actions); -util._extend(Export, Notify); -util._extend(Export, Monitor); -util._extend(Export, Pm2Module); -util._extend(Export, Probe); -util._extend(Export, Transaction); -util._extend(Export, Network); -util._extend(Export, Profiling); - -Export.init = function(opts) { - if (!opts) opts = {}; - - opts = util._extend({ - http : true, - http_latency : 200, - http_code : 500, - ignore_routes : [], - profiling : true, - errors : true, - custom_probes : true, - network : false, - ports : false - }, opts); - - if (opts.ports) - Export.catchPorts(); - if (opts.network) - Export.catchTraffic(); - Export.http(opts); - Export.catchAll(opts); - - if (opts.profiling) - Profiling.v8Profiling(Export); - - if (opts.custom_probes) { - // Event loop monitoring - require('./probes/pacemaker.js')(Export); - } - return this; -}; - -/** - * Export - */ - -module.exports = Export; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/monitor.js b/test/fixtures/module-fixture/node_modules/pmx/lib/monitor.js deleted file mode 100644 index 7c519506..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/monitor.js +++ /dev/null @@ -1,49 +0,0 @@ - -var Transport = require('./utils/transport.js'); -var debug = require('debug')('axm:monitor'); - -var Monitor = {}; - -function cookData(data) { - var cooked_data = {}; - - Object.keys(data).forEach(function(probe_name) { - if (typeof(data[probe_name]) == 'function') - cooked_data[probe_name] = data[probe_name](); - else - cooked_data[probe_name] = data[probe_name]; - }); - return cooked_data; -}; - -function enableProbes(custom_namespace) { - if (!custom_namespace) - custom_namespace = 'axm'; - - if (!global[custom_namespace]) - global[custom_namespace] = {}; - - if (this.interval) - return global[custom_namespace]; - - this.interval = setInterval(function() { - Transport.send({ - type : 'axm:monitor', - data : cookData(global[custom_namespace]) - }); - }, 990); - - return global[custom_namespace]; -}; - -function stopProbing() { - clearInterval(this.interval); -} - -Monitor.enableProbes = enableProbes; -Monitor.enableProbe = enableProbes; - -Monitor.stopProbe = stopProbing; -Monitor.stopProbes = stopProbing; - -module.exports = Monitor; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/network.js b/test/fixtures/module-fixture/node_modules/pmx/lib/network.js deleted file mode 100644 index c83e513e..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/network.js +++ /dev/null @@ -1,116 +0,0 @@ -var net_module = require('net'); -var Probe = require('./Probe.js'); - -var Network = module.exports = {}; - -Network.catchPorts = function() { - var ports_list = []; - var opened_ports = 'N/A'; - - Probe.probe().metric({ - name : 'Open ports', - value : function() { return opened_ports; } - }); - - var original_listen = net_module.Server.prototype.listen; - - net_module.Server.prototype.listen = function() { - var port = parseInt(arguments[0]); - - if (!isNaN(port) && ports_list.indexOf(port) === -1) { - ports_list.push(port); - opened_ports = ports_list.sort().join(); - } - - this.once('close', function() { - if (ports_list.indexOf(port) > -1) { - ports_list.splice(ports_list.indexOf(port), 1); - opened_ports = ports_list.sort().join(); - } - }); - - return original_listen.apply(this, arguments); - }; -}; - -Network.catchTraffic = function() { - var download = 0; - var upload = 0; - var up = '0 B/sec'; - var down = '0 B/sec'; - - var filter = function(bytes) { - var to_fixed = 0; - - if (bytes === 0) - ; - else if (bytes < 1024) - to_fixed = 6; - else if (bytes < (1024 * 1024)) - to_fixed = 3; - else - to_fixed = 2; - - bytes = (bytes / (1024 * 1024)).toFixed(to_fixed); - - var cut_zeros = 0; - - for (var i = (bytes.length - 1); i > 0; --i) { - if (bytes[i] === '.') { - ++cut_zeros; - break; - } - if (bytes[i] !== '0') - break; - ++cut_zeros; - } - - if (cut_zeros > 0) - bytes = bytes.slice(0, -(cut_zeros)); - - return (bytes + ' MB/s'); - }; - - setInterval(function() { - up = filter(upload); - down = filter(download); - upload = 0; - download = 0; - }, 999); - - Probe.probe().metric({ - name : 'Network Download', - agg_type : 'sum', - value : function() { return down; } - }); - - Probe.probe().metric({ - name : 'Network Upload', - agg_type : 'sum', - value : function() { return up; } - }); - - var original_write = net_module.Socket.prototype.write; - - net_module.Socket.prototype.write = function(data) { - if (data.length) - upload += data.length; - return original_write.apply(this, arguments); - }; - - var original_read = net_module.Socket.prototype.read; - - net_module.Socket.prototype.read = function() { - - if (!this.monitored) { - this.monitored = true; - - this.on('data', function(data) { - if (data.length) - download += data.length; - }); - } - - return original_read.apply(this, arguments); - }; -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/notify.js b/test/fixtures/module-fixture/node_modules/pmx/lib/notify.js deleted file mode 100644 index 49bd5a78..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/notify.js +++ /dev/null @@ -1,117 +0,0 @@ - -var debug = require('debug')('axm:notify'); -var util = require('util'); -var Common = require('./common.js'); - -var Options = require('./pm2_module.js'); - -var Transport = require('./utils/transport.js'); - -var Notify = {}; - - -var jsonize = function(err, filter, space) { - if (typeof(err) != 'object') - return err; - - var plainObject = {}; - - Object.getOwnPropertyNames(err).forEach(function(key) { - plainObject[key] = err[key]; - }); - return plainObject; -}; - -Notify.catchAll = function(opts) { - - if (opts === undefined) - opts = { errors : true }; - - Options.configureModule({ - error : opts.errors - }); - - if (process.env.exec_mode == 'cluster_mode') - return false; - - var catchException = function(err) { - //debug(err.stack || err); - Transport.send({ - type : 'process:exception', - data : jsonize(err) - }, true); - console.error(err.stack || err); - process.exit(255); - }; - - if (opts.errors === true - && util.inspect(process.listeners('uncaughtException')).length === 2) { - process.once('uncaughtException', catchException); - } - else if (opts.errors === false - && util.inspect(process.listeners('uncaughtException')).length !== 2) { - process.removeAllListeners('uncaughtException'); - } -}; - -Notify._interpretError = function(err) { - var s_err = {}; - - if (typeof(err) === 'string') { - // Simple string processing - s_err = new Error(err); - } - else if (!(err instanceof Error) && typeof(err) === 'object') { - // JSON processing - s_err = new Error(JSON.stringify(err)); - s_err.data = err; - } - else if (err instanceof Error) { - // Error object type processing - s_err = err; - } - - return jsonize(s_err); -}; - -Notify.notify = function(err) { - var ret_err = this._interpretError(err); - - // full_err - //debug(ret_err); - - Transport.send({ - type : 'process:exception', - data : ret_err - }, true); - - return ret_err; -}; - -Notify.expressErrorHandler = function() { - var self = this; - - Options.configureModule({ - error : true - }); - - return function errorHandler(err, req, res, next) { - if (res.statusCode < 400) res.statusCode = 500; - - //debug(err.stack || err); - - err.url = req.url; - err.component = req.url; - err.action = req.method; - err.params = req.body; - err.session = req.session; - - Transport.send({ - type : 'process:exception', - data : jsonize(err) - }, true); - return next(err); - }; -}; - -module.exports = Notify; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/pm2_module.js b/test/fixtures/module-fixture/node_modules/pmx/lib/pm2_module.js deleted file mode 100644 index 7b3b8aae..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/pm2_module.js +++ /dev/null @@ -1,110 +0,0 @@ - -var debug = require('debug')('axm:events'); -var Transport = require('./utils/transport.js'); -var path = require('path'); -var fs = require('fs'); -var util = require('util'); - -var Options = {}; - -Options.configureModule = function(opts) { - if (!this.running) { - this.running = true; - /* Avoid automatic exit of the script */ - - setInterval(function() {}, 1000); - } - - Transport.send({ - type : 'axm:option:configuration', - data : opts - }, false); -}; - -/** - * Load config and merge with data from package.json - */ -Options.loadConfig = Options.initModule = function(conf) { - var package_filepath = path.resolve(path.dirname(require.main.filename), 'package.json'); - - if (!conf) - conf = {}; - - if (!conf.module_conf) - conf.module_conf = {}; - - conf = util._extend(conf, { - errors : false, - latency : false, - versioning : false, - show_module_meta : false - }); - - /** - * Merge package.json metadata - */ - try { - var package_json = require(package_filepath); - - conf.module_version = package_json.version; - conf.module_name = package_json.name; - conf.description = package_json.description; - - if (package_json.config) { - conf = util._extend(conf, package_json.config); - conf.module_conf = package_json.config; - } - } catch(e) { - throw new Error('[PMX] package.json problem (not found or mal formated', e); - } - - /** - * If custom variables has been set, merge with returned configuration - */ - try { - if (process.env[package_json.name]) { - conf = util._extend(conf, JSON.parse(process.env[package_json.name])); - conf.module_conf = util._extend(conf.module_conf, JSON.parse(process.env[package_json.name])); - } - } catch(e) { - console.error(e); - console.error('Ezssadrror while parsing configuration in environment (%s)', package_json.name); - } - - Options.configureModule(conf); - - return conf; -}; - -Options.getPID = function(file) { - if (typeof(file) === 'number') - return file; - return parseInt(fs.readFileSync(file).toString()); -}; - -Options.resolvePidPaths = function(filepaths) { - if (typeof(filepaths) === 'number') - return filepaths; - - function detect(filepaths) { - var content = ''; - - filepaths.some(function(filepath) { - try { - content = fs.readFileSync(filepath); - } catch(e) { - return false; - } - return true; - }); - - return content.toString().trim(); - } - - var ret = parseInt(detect(filepaths)); - - return isNaN(ret) ? null : ret; -}; - - -module.exports = Options; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/probes/pacemaker.js b/test/fixtures/module-fixture/node_modules/pmx/lib/probes/pacemaker.js deleted file mode 100644 index f16ceda6..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/probes/pacemaker.js +++ /dev/null @@ -1,19 +0,0 @@ - -module.exports = function(pmx) { - var TIME_INTERVAL = 1000; - - var oldTime = process.hrtime(); - - var histogram = pmx.probe().histogram({ - name : 'Loop delay', - measurement : 'mean', - unit : 'ms' - }); - - setInterval(function() { - var newTime = process.hrtime(); - var delay = (newTime[0] - oldTime[0]) * 1e3 + (newTime[1] - oldTime[1]) / 1e6 - TIME_INTERVAL; - oldTime = newTime; - histogram.update(delay); - }, TIME_INTERVAL); -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/probes/profiling.js b/test/fixtures/module-fixture/node_modules/pmx/lib/probes/profiling.js deleted file mode 100644 index 806aad00..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/probes/profiling.js +++ /dev/null @@ -1,121 +0,0 @@ - -var debug = require('debug')('axm:profiling'); -var os = require('os'); -var path = require('path'); -var fs = require('fs'); - -var Options = require('../pm2_module.js'); - -var Profiling = module.exports = {}; - -Profiling.exposeProfiling = function(pmx, profiler_path) { - try { - var profiler = require(profiler_path); - } catch(e) { - debug('v8-profiler module not installed', e); - return false; - } - - debug('v8-profiler sucessfully enabled'); - - /** - * Tell Keymetrics that profiling is possible - * (flag available in axm_options object) - */ - Options.configureModule({ - heapdump : true - }); - - /** - * Heap snapshot - */ - pmx.action('km:heapdump', function(reply) { - var dump_file = path.join(os.tmpDir(), Date.now() + '.heapsnapshot'); - - var snapshot = profiler.takeSnapshot('km-heap-snapshot'); - var buffer = ''; - - snapshot.serialize( - function iterator(data, length) { - buffer += data; - }, function complete() { - fs.writeFile(dump_file, buffer, function (err) { - debug('Heap dump file flushed (e=', err); - - if (err) { - return reply({ - success : false, - err : err - }); - } - return reply({ - success : true, - heapdump : true, - dump_file : dump_file - }); - }); - } - ); - }); - - /** - * CPU profiling snapshot - */ - var ns_cpu_profiling = 'km-cpu-profiling'; - var cpu_dump_file = path.join(os.tmpDir(), Date.now() + '.cpuprofile'); - - pmx.action('km:cpu:profiling:start', function(reply) { - profiler.startProfiling(ns_cpu_profiling); - return reply({ success : true }); - }); - - pmx.action('km:cpu:profiling:stop', function(reply) { - var cpu = profiler.stopProfiling(ns_cpu_profiling); - - fs.writeFile(cpu_dump_file, JSON.stringify(cpu), function(err) { - if (err) { - return reply({ - success : false, - err : err - }); - } - return reply({ - success : true, - cpuprofile : true, - dump_file : cpu_dump_file - }); - }); - }); - -}; - -/** - * Discover v8-profiler - */ -Profiling.detectV8Profiler = function(cb) { - var require_paths = require.main.paths; - - (function look_for_profiler(require_paths) { - if (!require_paths[0]) - return cb(new Error('Module not found')); - - var profiler_path = path.join(require_paths[0], 'v8-profiler'); - - fs.exists(profiler_path, function(exist) { - if (exist) - return cb(null, profiler_path); - - require_paths.shift(); - return look_for_profiler(require_paths); - }); - return false; - })(require_paths); -}; - -Profiling.v8Profiling = function(pmx) { - Profiling.detectV8Profiler(function(err, profiler_path) { - if (err) - return false; - return Profiling.exposeProfiling(pmx, profiler_path); - }); -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/transaction.js b/test/fixtures/module-fixture/node_modules/pmx/lib/transaction.js deleted file mode 100644 index d076f154..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/transaction.js +++ /dev/null @@ -1,84 +0,0 @@ - -var util = require('util'); -var Proxy = require('./utils/proxy.js'); -var SimpleHttpWrap = require('./wrapper/simple_http.js'); -var Options = require('./pm2_module.js'); - -var debug = require('debug')('axm:patch'); - -var Transaction = module.exports = {}; - -Transaction.http = function(opts) { - var Module = require('module'); - - debug('Wrapping HTTP routes'); - - if (Array.isArray(opts)) { - var routes = JSON.parse(JSON.stringify(opts)); - opts = { - http : true, - http_latency : 200, - http_code : 500, - ignore_routes : routes - }; - } - opts = util._extend({ - http : true, - http_latency : 200, - http_code : 500, - ignore_routes : [], - }, opts); - - Proxy.wrap(Module, '_load', function(load) { - if (load.__axm_original) { - debug('HTTP routes have already been wrapped before'); - - Options.configureModule({ - latency : opts.http - }); - - if (opts.http === false) { - return function(file) { return load.__axm_original.apply(this, arguments) }; - } else { - return function(file) { - if (file === 'http' || file === 'https') - return SimpleHttpWrap(opts, load.__axm_original.apply(this, arguments)); - else - return load.__axm_original.apply(this, arguments); - }; - } - } - - return function(file) { - if (opts.http && - (file === 'http' || file === 'https')) { - debug('http module being required'); - Options.configureModule({ - latency : true - }); - return SimpleHttpWrap(opts, load.apply(this, arguments)); - } - else - return load.apply(this, arguments); - }; - }); -}; - -// Transaction.patch = function() { -// var Module = require('module'); - -// debug('Patching'); - -// Proxy.wrap(Module, '_load', function(load) { -// return function(file) { -// if (file == 'redis') { -// return RedisWrap(load.apply(this, arguments)); -// } -// else if (file == 'http') { -// return HttpWrap(load.apply(this, arguments)); -// } -// else -// return load.apply(this, arguments); -// }; -// }); -// }; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/BinaryHeap.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/BinaryHeap.js deleted file mode 100644 index 384464b6..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/BinaryHeap.js +++ /dev/null @@ -1,135 +0,0 @@ - -// Hacked https://github.com/felixge/node-measured - -// Based on http://en.wikipedia.org/wiki/Binary_Heap -// as well as http://eloquentjavascript.net/appendix2.html -module.exports = BinaryHeap; -function BinaryHeap(options) { - options = options || {}; - - this._elements = options.elements || []; - this._score = options.score || this._score; -} - -BinaryHeap.prototype.add = function(/* elements */) { - for (var i = 0; i < arguments.length; i++) { - var element = arguments[i]; - - this._elements.push(element); - this._bubble(this._elements.length - 1); - } -}; - -BinaryHeap.prototype.first = function() { - return this._elements[0]; -}; - -BinaryHeap.prototype.removeFirst = function() { - var root = this._elements[0]; - var last = this._elements.pop(); - - if (this._elements.length > 0) { - this._elements[0] = last; - this._sink(0); - } - - return root; -}; - -BinaryHeap.prototype.clone = function() { - return new BinaryHeap({ - elements: this.toArray(), - score: this._score, - }); -}; - -BinaryHeap.prototype.toSortedArray = function() { - var array = []; - var clone = this.clone(); - - while (true) { - var element = clone.removeFirst(); - if (element === undefined) break; - - array.push(element); - } - - return array; -}; - -BinaryHeap.prototype.toArray = function() { - return [].concat(this._elements); -}; - -BinaryHeap.prototype.size = function() { - return this._elements.length; -}; - -BinaryHeap.prototype._bubble = function(bubbleIndex) { - var bubbleElement = this._elements[bubbleIndex]; - var bubbleScore = this._score(bubbleElement); - - while (bubbleIndex > 0) { - var parentIndex = this._parentIndex(bubbleIndex); - var parentElement = this._elements[parentIndex]; - var parentScore = this._score(parentElement); - - if (bubbleScore <= parentScore) break; - - this._elements[parentIndex] = bubbleElement; - this._elements[bubbleIndex] = parentElement; - bubbleIndex = parentIndex; - } -}; - -BinaryHeap.prototype._sink = function(sinkIndex) { - var sinkElement = this._elements[sinkIndex]; - var sinkScore = this._score(sinkElement); - var length = this._elements.length; - - while (true) { - var swapIndex = null; - var swapScore = null; - var swapElement = null; - var childIndexes = this._childIndexes(sinkIndex); - - for (var i = 0; i < childIndexes.length; i++) { - var childIndex = childIndexes[i]; - - if (childIndex >= length) break; - - var childElement = this._elements[childIndex]; - var childScore = this._score(childElement); - - if (childScore > sinkScore) { - if (swapScore === null || swapScore < childScore) { - swapIndex = childIndex; - swapScore = childScore; - swapElement = childElement; - } - } - } - - if (swapIndex === null) break; - - this._elements[swapIndex] = sinkElement; - this._elements[sinkIndex] = swapElement; - sinkIndex = swapIndex; - } -}; - -BinaryHeap.prototype._parentIndex = function(index) { - return Math.floor((index - 1) / 2); -}; - -BinaryHeap.prototype._childIndexes = function(index) { - return [ - 2 * index + 1, - 2 * index + 2, - ]; - return ; -}; - -BinaryHeap.prototype._score = function(element) { - return element.valueOf(); -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EDS.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EDS.js deleted file mode 100644 index 4389e27c..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EDS.js +++ /dev/null @@ -1,101 +0,0 @@ - -// Hacked https://github.com/felixge/node-measured - -var BinaryHeap = require('./BinaryHeap'); -var units = require('./units'); - -module.exports = ExponentiallyDecayingSample; -function ExponentiallyDecayingSample(options) { - options = options || {}; - - this._elements = new BinaryHeap({ - score: function(element) { - return -element.priority; - } - }); - - this._rescaleInterval = options.rescaleInterval || ExponentiallyDecayingSample.RESCALE_INTERVAL; - this._alpha = options.alpha || ExponentiallyDecayingSample.ALPHA; - this._size = options.size || ExponentiallyDecayingSample.SIZE; - this._random = options.random || this._random; - this._landmark = null; - this._nextRescale = null; -} - -ExponentiallyDecayingSample.RESCALE_INTERVAL = 1 * units.HOURS; -ExponentiallyDecayingSample.ALPHA = 0.015; -ExponentiallyDecayingSample.SIZE = 1028; - -ExponentiallyDecayingSample.prototype.update = function(value, timestamp) { - var now = Date.now(); - if (!this._landmark) { - this._landmark = now; - this._nextRescale = this._landmark + this._rescaleInterval; - } - - timestamp = timestamp || now; - - var newSize = this._elements.size() + 1; - - var element = { - priority: this._priority(timestamp - this._landmark), - value: value - }; - - if (newSize <= this._size) { - this._elements.add(element); - } else if (element.priority > this._elements.first().priority) { - this._elements.removeFirst(); - this._elements.add(element); - } - - if (now >= this._nextRescale) this._rescale(now); -}; - -ExponentiallyDecayingSample.prototype.toSortedArray = function() { - return this._elements - .toSortedArray() - .map(function(element) { - return element.value; - }); -}; - - -ExponentiallyDecayingSample.prototype.toArray = function() { - return this._elements - .toArray() - .map(function(element) { - return element.value; - }); -}; - -ExponentiallyDecayingSample.prototype._weight = function(age) { - // We divide by 1000 to not run into huge numbers before reaching a - // rescale event. - return Math.exp(this._alpha * (age / 1000)); -}; - -ExponentiallyDecayingSample.prototype._priority = function(age) { - return this._weight(age) / this._random(); -}; - -ExponentiallyDecayingSample.prototype._random = function() { - return Math.random(); -}; - -ExponentiallyDecayingSample.prototype._rescale = function(now) { - now = now || Date.now(); - - var self = this; - var oldLandmark = this._landmark; - this._landmark = now || Date.now(); - this._nextRescale = now + this._rescaleInterval; - - var factor = self._priority(-(self._landmark - oldLandmark)); - - this._elements - .toArray() - .forEach(function(element) { - element.priority *= factor; - }); -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EWMA.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EWMA.js deleted file mode 100644 index dec164e9..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EWMA.js +++ /dev/null @@ -1,31 +0,0 @@ - -// Hacked https://github.com/felixge/node-measured - -var units = require('./units'); - -module.exports = ExponentiallyWeightedMovingAverage; - -function ExponentiallyWeightedMovingAverage(timePeriod, tickInterval) { - this._timePeriod = timePeriod || 1 * units.MINUTE; - this._tickInterval = tickInterval || ExponentiallyWeightedMovingAverage.TICK_INTERVAL; - this._alpha = 1 - Math.exp(-this._tickInterval / this._timePeriod); - this._count = 0; - this._rate = 0; -}; - -ExponentiallyWeightedMovingAverage.TICK_INTERVAL = 5 * units.SECONDS; - -ExponentiallyWeightedMovingAverage.prototype.update = function(n) { - this._count += n; -}; - -ExponentiallyWeightedMovingAverage.prototype.tick = function() { - var instantRate = this._count / this._tickInterval; - this._count = 0; - - this._rate += (this._alpha * (instantRate - this._rate)); -}; - -ExponentiallyWeightedMovingAverage.prototype.rate = function(timeUnit) { - return (this._rate || 0) * timeUnit; -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/fixedQueue.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/fixedQueue.js deleted file mode 100644 index 86059a5f..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/fixedQueue.js +++ /dev/null @@ -1,115 +0,0 @@ -function FixedQueue( size, initialValues ){ - - // If there are no initial arguments, default it to - // an empty value so we can call the constructor in - // a uniform way. - initialValues = (initialValues || []); - - // Create the fixed queue array value. - var queue = Array.apply( null, initialValues ); - - // Store the fixed size in the queue. - queue.fixedSize = size; - - // Add the class methods to the queue. Some of these have - // to override the native Array methods in order to make - // sure the queue lenght is maintained. - queue.push = FixedQueue.push; - queue.splice = FixedQueue.splice; - queue.unshift = FixedQueue.unshift; - - // Trim any initial excess from the queue. - FixedQueue.trimTail.call( queue ); - - // Return the new queue. - return( queue ); - -} - - -// I trim the queue down to the appropriate size, removing -// items from the beginning of the internal array. -FixedQueue.trimHead = function(){ - - // Check to see if any trimming needs to be performed. - if (this.length <= this.fixedSize){ - - // No trimming, return out. - return; - - } - - // Trim whatever is beyond the fixed size. - Array.prototype.splice.call( - this, - 0, - (this.length - this.fixedSize) - ); - -}; - - -// I trim the queue down to the appropriate size, removing -// items from the end of the internal array. -FixedQueue.trimTail = function(){ - - // Check to see if any trimming needs to be performed. - if (this.length <= this.fixedSize){ - - // No trimming, return out. - return; - - } - - // Trim whatever is beyond the fixed size. - Array.prototype.splice.call( - this, - this.fixedSize, - (this.length - this.fixedSize) - ); - -}; - - -// I synthesize wrapper methods that call the native Array -// methods followed by a trimming method. -FixedQueue.wrapMethod = function( methodName, trimMethod ){ - - // Create a wrapper that calls the given method. - var wrapper = function(){ - - // Get the native Array method. - var method = Array.prototype[ methodName ]; - - // Call the native method first. - var result = method.apply( this, arguments ); - - // Trim the queue now that it's been augmented. - trimMethod.call( this ); - - // Return the original value. - return( result ); - - }; - - // Return the wrapper method. - return( wrapper ); - -}; - - -// Wrap the native methods. -FixedQueue.push = FixedQueue.wrapMethod( - "push", - FixedQueue.trimHead -); - -FixedQueue.splice = FixedQueue.wrapMethod( - "splice", - FixedQueue.trimTail -); - -FixedQueue.unshift = FixedQueue.wrapMethod( - "unshift", - FixedQueue.trimTail -); diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Counter.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Counter.js deleted file mode 100644 index b7e9e7b3..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Counter.js +++ /dev/null @@ -1,26 +0,0 @@ - -// Hacked from https://github.com/felixge/node-measured - -module.exports = Counter; - -function Counter(opts) { - opts = opts || {}; - - this._count = opts.count || 0; -} - -Counter.prototype.val = function() { - return this._count; -}; - -Counter.prototype.inc = function(n) { - this._count += (n || 1); -}; - -Counter.prototype.dec = function(n) { - this._count -= (n || 1); -}; - -Counter.prototype.reset = function(count) { - this._count = count || 0; -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Histogram.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Histogram.js deleted file mode 100644 index affba16a..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Histogram.js +++ /dev/null @@ -1,185 +0,0 @@ - -// Hacked from https://github.com/felixge/node-measured - -var EDS = require('../EDS.js'); - -function Histogram(opts) { - var self = this; - - opts = opts || {}; - - this._measurement = opts.measurement; - this._call_fn = null; - - var methods = { - min : this.getMin, - max : this.getMax, - sum : this.getSum, - count : this.getCount, - variance : this._calculateVariance, - mean : this._calculateMean, - stddev : this._calculateStddev - }; - - if (methods[this._measurement]) - this._call_fn = methods[this._measurement]; - else { - this._call_fn = function() { - var percentiles = this.percentiles([0.5, 0.75, 0.95, 0.99, 0.999]); - - var medians = { - median : percentiles[0.5], - p75 : percentiles[0.75], - p95 : percentiles[0.95], - p99 : percentiles[0.99], - p999 : percentiles[0.999] - }; - - return medians[this._measurement]; - } - } - this._sample = new EDS(); - this._min = null; - this._max = null; - this._count = 0; - this._sum = 0; - - // These are for the Welford algorithm for calculating running variance - // without floating-point doom. - this._varianceM = 0; - this._varianceS = 0; -} - -Histogram.prototype.update = function(value) { - this._count++; - this._sum += value; - - this._sample.update(value); - this._updateMin(value); - this._updateMax(value); - this._updateVariance(value); -}; - -Histogram.prototype.percentiles = function(percentiles) { - var values = this._sample - .toArray() - .sort(function(a, b) { - return (a === b) - ? 0 - : a - b; - }); - - var results = {}; - - for (var i = 0; i < percentiles.length; i++) { - var percentile = percentiles[i]; - if (!values.length) { - results[percentile] = null; - continue; - } - - var pos = percentile * (values.length + 1); - - if (pos < 1) { - results[percentile] = values[0]; - } else if (pos >= values.length) { - results[percentile] = values[values.length - 1]; - } else { - var lower = values[Math.floor(pos) - 1]; - var upper = values[Math.ceil(pos) - 1]; - - results[percentile] = lower + (pos - Math.floor(pos)) * (upper - lower); - } - } - - return results; -}; - -Histogram.prototype.reset = function() { - this.constructor.call(this); -}; - -Histogram.prototype.val = function() { - if (typeof(this._call_fn) === 'function') - return this._call_fn(); - else - return this._call_fn; -}; - -Histogram.prototype.getMin = function() { - return this._min; -}; - -Histogram.prototype.getMax = function() { - return this._max; -}; - -Histogram.prototype.getSum = function() { - return this._sum; -}; - -Histogram.prototype.getCount = function() { - return this._count; -}; - - -Histogram.prototype.fullResults = function() { - var percentiles = this.percentiles([0.5, 0.75, 0.95, 0.99, 0.999]); - - return { - min : this._min, - max : this._max, - sum : this._sum, - variance : this._calculateVariance(), - mean : this._calculateMean(), - stddev : this._calculateStddev(), - count : this._count, - median : percentiles[0.5], - p75 : percentiles[0.75], - p95 : percentiles[0.95], - p99 : percentiles[0.99], - p999 : percentiles[0.999] - }; -}; - -Histogram.prototype._updateMin = function(value) { - if (this._min === null || value < this._min) { - this._min = value; - //console.log(value); - } -}; - -Histogram.prototype._updateMax = function(value) { - if (this._max === null || value > this._max) { - this._max = value; - } -}; - -Histogram.prototype._updateVariance = function(value) { - if (this._count === 1) return this._varianceM = value; - - var oldM = this._varianceM; - - this._varianceM += ((value - oldM) / this._count); - this._varianceS += ((value - oldM) * (value - this._varianceM)); -}; - -Histogram.prototype._calculateMean = function() { - return (this._count === 0) - ? 0 - : this._sum / this._count; -}; - -Histogram.prototype._calculateVariance = function() { - return (this._count <= 1) - ? null - : this._varianceS / (this._count - 1); -}; - -Histogram.prototype._calculateStddev = function() { - return (this._count < 1) - ? null - : Math.sqrt(this._calculateVariance()); -}; - -module.exports = Histogram; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Meter.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Meter.js deleted file mode 100644 index f436af40..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Meter.js +++ /dev/null @@ -1,36 +0,0 @@ - -// Hacked from https://github.com/felixge/node-measured - -var units = require('../units'); -var EWMA = require('../EWMA'); - -function Meter(opts) { - var self = this; - - this._tickInterval = 5 * units.SECONDS; - this._samples = opts.samples || 1; - this._timeframe = opts.timeframe || 60; - - this._rate = new EWMA(this._timeframe * units.SECONDS, this._tickInterval); - - if (opts.debug && opts.debug == true) - return false; - - this._interval = setInterval(function() { - self._rate.tick(); - }, this._tickInterval); -} - -Meter.RATE_UNIT = units.SECONDS; - -Meter.prototype.mark = function(n) { - n = n || 1; - - this._rate.update(n); -}; - -Meter.prototype.val = function() { - return Math.round(this._rate.rate(this._samples * Meter.RATE_UNIT) * 100 ) / 100; -}; - -module.exports = Meter; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/proxy.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/proxy.js deleted file mode 100644 index 830a30fe..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/proxy.js +++ /dev/null @@ -1,34 +0,0 @@ - -var debug = require('debug')('axm:proxy'); - -// var cls = require('continuation-local-storage'); -// var ns = cls.createNamespace('namespace'); - -var Proxy = module.exports = { - wrap : function(object, methods, hook) { - var self = this; - - if (!Array.isArray(methods)) methods = [methods]; - - for (var i = 0; i < methods.length; ++i) { - debug('Wrapping method:', methods[i]); - var original = object[methods[i]]; - if (!original) return debug('Method %s unknown', methods[i]); - if (original.__axm_original) { - debug('Already wrapped', methods[i]); - if (methods[i] != '_load') - return; - } - var hooked = hook(original); - - if (original.__axm_original) { - hooked.__axm_original = original.__axm_original; - } - else { - hooked.__axm_original = original; - } - object[methods[i]] = hooked; - //debug('Method proxified'); - } - } -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/transport.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/transport.js deleted file mode 100644 index b7485e4b..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/transport.js +++ /dev/null @@ -1,34 +0,0 @@ - -var debug = require('debug')('axm:transport'); -var stringify = require('json-stringify-safe'); - -var Transport = module.exports = {}; - -function ipcSend(args, print) { - /** - * For debug purpose - */ - if (process.env.MODULE_DEBUG) - console.log(args); - - if (!process.send) { - var output = args.data; - delete output.__name; - return false; - } - - - try { - process.send(JSON.parse(stringify(args))); - } catch(e) { - console.error('Process disconnected from parent !'); - console.error(e.stack || e); - process.exit(1); - } -}; - -Transport.send = function(args, print) { - if (!print) print = false; - - ipcSend(args, print); -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/units.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/units.js deleted file mode 100644 index b1e16a24..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/units.js +++ /dev/null @@ -1,9 +0,0 @@ -// Time units, as found in Java: -// see: http://download.oracle.com/javase/6/docs/api/java/util/concurrent/TimeUnit.html -exports.NANOSECONDS = 1 / (1000 * 1000); -exports.MICROSECONDS = 1 / 1000; -exports.MILLISECONDS = 1; -exports.SECONDS = 1000 * exports.MILLISECONDS; -exports.MINUTES = 60 * exports.SECONDS; -exports.HOURS = 60 * exports.MINUTES; -exports.DAYS = 24 * exports.HOURS; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/wrapper/simple_http.js b/test/fixtures/module-fixture/node_modules/pmx/lib/wrapper/simple_http.js deleted file mode 100644 index ca6d65a9..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/lib/wrapper/simple_http.js +++ /dev/null @@ -1,98 +0,0 @@ - -var Proxy = require('../utils/proxy.js'); -var Transport = require('../utils/transport.js'); -var Probe = require('../Probe.js'); - -var gl_meter, gl_latency; - -var HttpWrap = module.exports = function(opts, http) { - - gl_meter = Probe.probe().meter({ - name : 'HTTP', - seconds : 60, - unit : 'req/s' - }); - - gl_latency = Probe.probe().histogram({ - measurement : 'mean', - name : 'pmx:http:latency', - unit : 'ms' - }); - - var ignoreRoutes = function(url) { - for (var i = 0; i < opts.ignore_routes.length; ++i) { - if (url.match(opts.ignore_routes[i]) != null) { - return true; - } - } - return false; - }; - - Proxy.wrap(http.Server.prototype, ['on', 'addListener'], function(addListener) { - return function(event, listener) { - var self = this; - - var overloaded_function = function(request, response) { - gl_meter.mark(); - - var http_start = { - url : request.url, - method : request.method, - start : Date.now(), - ip : request.headers['x-forwarded-for'] || - (request.connection ? request.connection.remoteAddress : false) || - (request.socket ? request.socket.remoteAddress : false) || - ((request.connection && request.connection.socket) ? request.connection.socket.remoteAddress : false) || '' - }; - - response.once('finish', function() { - - if (!ignoreRoutes(http_start.url)) - gl_latency.update(Date.now() - http_start.start); - - if (((Date.now() - http_start.start) >= opts.http_latency - || response.statusCode >= opts.http_code) - && !ignoreRoutes(http_start.url)) { - - Transport.send({ - type : 'http:transaction', - data : { - url : http_start.url, - method : http_start.method, - time : Date.now() - http_start.start, - code : response.statusCode, - ip : http_start.ip, - size : response.getHeader('Content-Length') || null - } - }); - } - - http_start = null; - }); - }; - - if (!(event === 'request' && typeof listener === 'function')) - return addListener.apply(self, arguments); - - if (self.__overloaded !== true) { - - self.on('removeListener', function onRemoveListener() { - setTimeout(function() { - if (self.listeners('request').length === 1) { - self.removeListener('request', overloaded_function); - self.removeListener('removeListener', onRemoveListener); - self.__overloaded = false; - } - }, 200); - }); - - addListener.call(self, event, overloaded_function); - - self.__overloaded = true; - } - - return addListener.apply(self, arguments); - }; - }); - return http; -}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.jshintrc b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.jshintrc deleted file mode 100644 index 299877f2..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.jshintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "laxbreak": true -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.npmignore b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.npmignore deleted file mode 100644 index 7e6163db..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -support -test -examples -example -*.sock -dist diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/History.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/History.md deleted file mode 100644 index 854c9711..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/History.md +++ /dev/null @@ -1,195 +0,0 @@ - -2.2.0 / 2015-05-09 -================== - - * package: update "ms" to v0.7.1 (#202, @dougwilson) - * README: add logging to file example (#193, @DanielOchoa) - * README: fixed a typo (#191, @amir-s) - * browser: expose `storage` (#190, @stephenmathieson) - * Makefile: add a `distclean` target (#189, @stephenmathieson) - -2.1.3 / 2015-03-13 -================== - - * Updated stdout/stderr example (#186) - * Updated example/stdout.js to match debug current behaviour - * Renamed example/stderr.js to stdout.js - * Update Readme.md (#184) - * replace high intensity foreground color for bold (#182, #183) - -2.1.2 / 2015-03-01 -================== - - * dist: recompile - * update "ms" to v0.7.0 - * package: update "browserify" to v9.0.3 - * component: fix "ms.js" repo location - * changed bower package name - * updated documentation about using debug in a browser - * fix: security error on safari (#167, #168, @yields) - -2.1.1 / 2014-12-29 -================== - - * browser: use `typeof` to check for `console` existence - * browser: check for `console.log` truthiness (fix IE 8/9) - * browser: add support for Chrome apps - * Readme: added Windows usage remarks - * Add `bower.json` to properly support bower install - -2.1.0 / 2014-10-15 -================== - - * node: implement `DEBUG_FD` env variable support - * package: update "browserify" to v6.1.0 - * package: add "license" field to package.json (#135, @panuhorsmalahti) - -2.0.0 / 2014-09-01 -================== - - * package: update "browserify" to v5.11.0 - * node: use stderr rather than stdout for logging (#29, @stephenmathieson) - -1.0.4 / 2014-07-15 -================== - - * dist: recompile - * example: remove `console.info()` log usage - * example: add "Content-Type" UTF-8 header to browser example - * browser: place %c marker after the space character - * browser: reset the "content" color via `color: inherit` - * browser: add colors support for Firefox >= v31 - * debug: prefer an instance `log()` function over the global one (#119) - * Readme: update documentation about styled console logs for FF v31 (#116, @wryk) - -1.0.3 / 2014-07-09 -================== - - * Add support for multiple wildcards in namespaces (#122, @seegno) - * browser: fix lint - -1.0.2 / 2014-06-10 -================== - - * browser: update color palette (#113, @gscottolson) - * common: make console logging function configurable (#108, @timoxley) - * node: fix %o colors on old node <= 0.8.x - * Makefile: find node path using shell/which (#109, @timoxley) - -1.0.1 / 2014-06-06 -================== - - * browser: use `removeItem()` to clear localStorage - * browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777) - * package: add "contributors" section - * node: fix comment typo - * README: list authors - -1.0.0 / 2014-06-04 -================== - - * make ms diff be global, not be scope - * debug: ignore empty strings in enable() - * node: make DEBUG_COLORS able to disable coloring - * *: export the `colors` array - * npmignore: don't publish the `dist` dir - * Makefile: refactor to use browserify - * package: add "browserify" as a dev dependency - * Readme: add Web Inspector Colors section - * node: reset terminal color for the debug content - * node: map "%o" to `util.inspect()` - * browser: map "%j" to `JSON.stringify()` - * debug: add custom "formatters" - * debug: use "ms" module for humanizing the diff - * Readme: add "bash" syntax highlighting - * browser: add Firebug color support - * browser: add colors for WebKit browsers - * node: apply log to `console` - * rewrite: abstract common logic for Node & browsers - * add .jshintrc file - -0.8.1 / 2014-04-14 -================== - - * package: re-add the "component" section - -0.8.0 / 2014-03-30 -================== - - * add `enable()` method for nodejs. Closes #27 - * change from stderr to stdout - * remove unnecessary index.js file - -0.7.4 / 2013-11-13 -================== - - * remove "browserify" key from package.json (fixes something in browserify) - -0.7.3 / 2013-10-30 -================== - - * fix: catch localStorage security error when cookies are blocked (Chrome) - * add debug(err) support. Closes #46 - * add .browser prop to package.json. Closes #42 - -0.7.2 / 2013-02-06 -================== - - * fix package.json - * fix: Mobile Safari (private mode) is broken with debug - * fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript - -0.7.1 / 2013-02-05 -================== - - * add repository URL to package.json - * add DEBUG_COLORED to force colored output - * add browserify support - * fix component. Closes #24 - -0.7.0 / 2012-05-04 -================== - - * Added .component to package.json - * Added debug.component.js build - -0.6.0 / 2012-03-16 -================== - - * Added support for "-" prefix in DEBUG [Vinay Pulim] - * Added `.enabled` flag to the node version [TooTallNate] - -0.5.0 / 2012-02-02 -================== - - * Added: humanize diffs. Closes #8 - * Added `debug.disable()` to the CS variant - * Removed padding. Closes #10 - * Fixed: persist client-side variant again. Closes #9 - -0.4.0 / 2012-02-01 -================== - - * Added browser variant support for older browsers [TooTallNate] - * Added `debug.enable('project:*')` to browser variant [TooTallNate] - * Added padding to diff (moved it to the right) - -0.3.0 / 2012-01-26 -================== - - * Added millisecond diff when isatty, otherwise UTC string - -0.2.0 / 2012-01-22 -================== - - * Added wildcard support - -0.1.0 / 2011-12-02 -================== - - * Added: remove colors unless stderr isatty [TooTallNate] - -0.0.1 / 2010-01-03 -================== - - * Initial release diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Makefile b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Makefile deleted file mode 100644 index 5cf4a596..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Makefile +++ /dev/null @@ -1,36 +0,0 @@ - -# get Makefile directory name: http://stackoverflow.com/a/5982798/376773 -THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) -THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) - -# BIN directory -BIN := $(THIS_DIR)/node_modules/.bin - -# applications -NODE ?= $(shell which node) -NPM ?= $(NODE) $(shell which npm) -BROWSERIFY ?= $(NODE) $(BIN)/browserify - -all: dist/debug.js - -install: node_modules - -clean: - @rm -rf dist - -dist: - @mkdir -p $@ - -dist/debug.js: node_modules browser.js debug.js dist - @$(BROWSERIFY) \ - --standalone debug \ - . > $@ - -distclean: clean - @rm -rf node_modules - -node_modules: package.json - @NODE_ENV= $(NPM) install - @touch node_modules - -.PHONY: all install clean distclean diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Readme.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Readme.md deleted file mode 100644 index b4f45e3c..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Readme.md +++ /dev/null @@ -1,188 +0,0 @@ -# debug - - tiny node.js debugging utility modelled after node core's debugging technique. - -## Installation - -```bash -$ npm install debug -``` - -## Usage - - With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility. - -Example _app.js_: - -```js -var debug = require('debug')('http') - , http = require('http') - , name = 'My App'; - -// fake app - -debug('booting %s', name); - -http.createServer(function(req, res){ - debug(req.method + ' ' + req.url); - res.end('hello\n'); -}).listen(3000, function(){ - debug('listening'); -}); - -// fake worker of some kind - -require('./worker'); -``` - -Example _worker.js_: - -```js -var debug = require('debug')('worker'); - -setInterval(function(){ - debug('doing some work'); -}, 1000); -``` - - The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples: - - ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png) - - ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png) - -#### Windows note - - On Windows the environment variable is set using the `set` command. - - ```cmd - set DEBUG=*,-not_this - ``` - -Then, run the program to be debugged as usual. - -## Millisecond diff - - When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. - - ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png) - - When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below: - - ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png) - -## Conventions - - If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". - -## Wildcards - - The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. - - You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:". - -## Browser support - - Debug works in the browser as well, currently persisted by `localStorage`. Consider the situation shown below where you have `worker:a` and `worker:b`, and wish to debug both. Somewhere in the code on your page, include: - -```js -window.myDebug = require("debug"); -``` - - ("debug" is a global object in the browser so we give this object a different name.) When your page is open in the browser, type the following in the console: - -```js -myDebug.enable("worker:*") -``` - - Refresh the page. Debug output will continue to be sent to the console until it is disabled by typing `myDebug.disable()` in the console. - -```js -a = debug('worker:a'); -b = debug('worker:b'); - -setInterval(function(){ - a('doing some work'); -}, 1000); - -setInterval(function(){ - b('doing some work'); -}, 1200); -``` - -#### Web Inspector Colors - - Colors are also enabled on "Web Inspectors" that understand the `%c` formatting - option. These are WebKit web inspectors, Firefox ([since version - 31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/)) - and the Firebug plugin for Firefox (any version). - - Colored output looks something like: - - ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png) - -### stderr vs stdout - -You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally: - -Example _stdout.js_: - -```js -var debug = require('debug'); -var error = debug('app:error'); - -// by default stderr is used -error('goes to stderr!'); - -var log = debug('app:log'); -// set this namespace to log via console.log -log.log = console.log.bind(console); // don't forget to bind to console! -log('goes to stdout'); -error('still goes to stderr!'); - -// set all output to go via console.info -// overrides all per-namespace log settings -debug.log = console.info.bind(console); -error('now goes to stdout via console.info'); -log('still goes to stdout, but via console.info now'); -``` - -### Save debug output to a file - -You can save all debug statements to a file by piping them. - -Example: - -```bash -$ DEBUG_FD=3 node your-app.js 3> whatever.log -``` - -## Authors - - - TJ Holowaychuk - - Nathan Rajlich - -## License - -(The MIT License) - -Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca> - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/bower.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/bower.json deleted file mode 100644 index 6af573ff..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/bower.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "visionmedia-debug", - "main": "dist/debug.js", - "version": "2.2.0", - "homepage": "https://github.com/visionmedia/debug", - "authors": [ - "TJ Holowaychuk " - ], - "description": "visionmedia-debug", - "moduleType": [ - "amd", - "es6", - "globals", - "node" - ], - "keywords": [ - "visionmedia", - "debug" - ], - "license": "MIT", - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ] -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/browser.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/browser.js deleted file mode 100644 index 7c764522..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/browser.js +++ /dev/null @@ -1,168 +0,0 @@ - -/** - * This is the web browser implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = require('./debug'); -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); - -/** - * Colors. - */ - -exports.colors = [ - 'lightseagreen', - 'forestgreen', - 'goldenrod', - 'dodgerblue', - 'darkorchid', - 'crimson' -]; - -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - -function useColors() { - // is webkit? http://stackoverflow.com/a/16459606/376773 - return ('WebkitAppearance' in document.documentElement.style) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (window.console && (console.firebug || (console.exception && console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); -} - -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - -exports.formatters.j = function(v) { - return JSON.stringify(v); -}; - - -/** - * Colorize log arguments if enabled. - * - * @api public - */ - -function formatArgs() { - var args = arguments; - var useColors = this.useColors; - - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); - - if (!useColors) return args; - - var c = 'color: ' + this.color; - args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); - - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - - args.splice(lastC, 0, c); - return args; -} - -/** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public - */ - -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); -} - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - -function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; - } - } catch(e) {} -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - -function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} - return r; -} - -/** - * Enable namespaces listed in `localStorage.debug` initially. - */ - -exports.enable(load()); - -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ - -function localstorage(){ - try { - return window.localStorage; - } catch (e) {} -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/component.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/component.json deleted file mode 100644 index ca106372..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/component.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "debug", - "repo": "visionmedia/debug", - "description": "small debugging utility", - "version": "2.2.0", - "keywords": [ - "debug", - "log", - "debugger" - ], - "main": "browser.js", - "scripts": [ - "browser.js", - "debug.js" - ], - "dependencies": { - "rauchg/ms.js": "0.7.1" - } -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/debug.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/debug.js deleted file mode 100644 index 7571a860..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/debug.js +++ /dev/null @@ -1,197 +0,0 @@ - -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = debug; -exports.coerce = coerce; -exports.disable = disable; -exports.enable = enable; -exports.enabled = enabled; -exports.humanize = require('ms'); - -/** - * The currently active debug mode names, and names to skip. - */ - -exports.names = []; -exports.skips = []; - -/** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lowercased letter, i.e. "n". - */ - -exports.formatters = {}; - -/** - * Previously assigned color. - */ - -var prevColor = 0; - -/** - * Previous log timestamp. - */ - -var prevTime; - -/** - * Select a color. - * - * @return {Number} - * @api private - */ - -function selectColor() { - return exports.colors[prevColor++ % exports.colors.length]; -} - -/** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ - -function debug(namespace) { - - // define the `disabled` version - function disabled() { - } - disabled.enabled = false; - - // define the `enabled` version - function enabled() { - - var self = enabled; - - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - - // add the `color` if not set - if (null == self.useColors) self.useColors = exports.useColors(); - if (null == self.color && self.useColors) self.color = selectColor(); - - var args = Array.prototype.slice.call(arguments); - - args[0] = exports.coerce(args[0]); - - if ('string' !== typeof args[0]) { - // anything else let's inspect with %o - args = ['%o'].concat(args); - } - - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); - - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); - - if ('function' === typeof exports.formatArgs) { - args = exports.formatArgs.apply(self, args); - } - var logFn = enabled.log || exports.log || console.log.bind(console); - logFn.apply(self, args); - } - enabled.enabled = true; - - var fn = exports.enabled(namespace) ? enabled : disabled; - - fn.namespace = namespace; - - return fn; -} - -/** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ - -function enable(namespaces) { - exports.save(namespaces); - - var split = (namespaces || '').split(/[\s,]+/); - var len = split.length; - - for (var i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); - } - } -} - -/** - * Disable debug output. - * - * @api public - */ - -function disable() { - exports.enable(''); -} - -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - -function enabled(name) { - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { - return true; - } - } - return false; -} - -/** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ - -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node.js deleted file mode 100644 index 1d392a81..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node.js +++ /dev/null @@ -1,209 +0,0 @@ - -/** - * Module dependencies. - */ - -var tty = require('tty'); -var util = require('util'); - -/** - * This is the Node.js implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = require('./debug'); -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; - -/** - * Colors. - */ - -exports.colors = [6, 2, 3, 4, 5, 1]; - -/** - * The file descriptor to write the `debug()` calls to. - * Set the `DEBUG_FD` env variable to override with another value. i.e.: - * - * $ DEBUG_FD=3 node script.js 3>debug.log - */ - -var fd = parseInt(process.env.DEBUG_FD, 10) || 2; -var stream = 1 === fd ? process.stdout : - 2 === fd ? process.stderr : - createWritableStdioStream(fd); - -/** - * Is stdout a TTY? Colored output is enabled when `true`. - */ - -function useColors() { - var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase(); - if (0 === debugColors.length) { - return tty.isatty(fd); - } else { - return '0' !== debugColors - && 'no' !== debugColors - && 'false' !== debugColors - && 'disabled' !== debugColors; - } -} - -/** - * Map %o to `util.inspect()`, since Node doesn't do that out of the box. - */ - -var inspect = (4 === util.inspect.length ? - // node <= 0.8.x - function (v, colors) { - return util.inspect(v, void 0, void 0, colors); - } : - // node > 0.8.x - function (v, colors) { - return util.inspect(v, { colors: colors }); - } -); - -exports.formatters.o = function(v) { - return inspect(v, this.useColors) - .replace(/\s*\n\s*/g, ' '); -}; - -/** - * Adds ANSI color escape codes if enabled. - * - * @api public - */ - -function formatArgs() { - var args = arguments; - var useColors = this.useColors; - var name = this.namespace; - - if (useColors) { - var c = this.color; - - args[0] = ' \u001b[3' + c + ';1m' + name + ' ' - + '\u001b[0m' - + args[0] + '\u001b[3' + c + 'm' - + ' +' + exports.humanize(this.diff) + '\u001b[0m'; - } else { - args[0] = new Date().toUTCString() - + ' ' + name + ' ' + args[0]; - } - return args; -} - -/** - * Invokes `console.error()` with the specified arguments. - */ - -function log() { - return stream.write(util.format.apply(this, arguments) + '\n'); -} - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - -function save(namespaces) { - if (null == namespaces) { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG; - } else { - process.env.DEBUG = namespaces; - } -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - -function load() { - return process.env.DEBUG; -} - -/** - * Copied from `node/src/node.js`. - * - * XXX: It's lame that node doesn't expose this API out-of-the-box. It also - * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. - */ - -function createWritableStdioStream (fd) { - var stream; - var tty_wrap = process.binding('tty_wrap'); - - // Note stream._type is used for test-module-load-list.js - - switch (tty_wrap.guessHandleType(fd)) { - case 'TTY': - stream = new tty.WriteStream(fd); - stream._type = 'tty'; - - // Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; - - case 'FILE': - var fs = require('fs'); - stream = new fs.SyncWriteStream(fd, { autoClose: false }); - stream._type = 'fs'; - break; - - case 'PIPE': - case 'TCP': - var net = require('net'); - stream = new net.Socket({ - fd: fd, - readable: false, - writable: true - }); - - // FIXME Should probably have an option in net.Socket to create a - // stream from an existing fd which is writable only. But for now - // we'll just add this hack and set the `readable` member to false. - // Test: ./node test/fixtures/echo.js < /etc/passwd - stream.readable = false; - stream.read = null; - stream._type = 'pipe'; - - // FIXME Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; - - default: - // Probably an error on in uv_guess_handle() - throw new Error('Implement me. Unknown stream file type!'); - } - - // For supporting legacy API we put the FD here. - stream.fd = fd; - - stream._isStdio = true; - - return stream; -} - -/** - * Enable namespaces listed in `process.env.DEBUG` initially. - */ - -exports.enable(load()); diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/.npmignore b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/.npmignore deleted file mode 100644 index d1aa0ce4..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/.npmignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -test -History.md -Makefile -component.json diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/History.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/History.md deleted file mode 100644 index 32fdfc17..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/History.md +++ /dev/null @@ -1,66 +0,0 @@ - -0.7.1 / 2015-04-20 -================== - - * prevent extraordinary long inputs (@evilpacket) - * Fixed broken readme link - -0.7.0 / 2014-11-24 -================== - - * add time abbreviations, updated tests and readme for the new units - * fix example in the readme. - * add LICENSE file - -0.6.2 / 2013-12-05 -================== - - * Adding repository section to package.json to suppress warning from NPM. - -0.6.1 / 2013-05-10 -================== - - * fix singularization [visionmedia] - -0.6.0 / 2013-03-15 -================== - - * fix minutes - -0.5.1 / 2013-02-24 -================== - - * add component namespace - -0.5.0 / 2012-11-09 -================== - - * add short formatting as default and .long option - * add .license property to component.json - * add version to component.json - -0.4.0 / 2012-10-22 -================== - - * add rounding to fix crazy decimals - -0.3.0 / 2012-09-07 -================== - - * fix `ms()` [visionmedia] - -0.2.0 / 2012-09-03 -================== - - * add component.json [visionmedia] - * add days support [visionmedia] - * add hours support [visionmedia] - * add minutes support [visionmedia] - * add seconds support [visionmedia] - * add ms string support [visionmedia] - * refactor tests to facilitate ms(number) [visionmedia] - -0.1.0 / 2012-03-07 -================== - - * Initial release diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/LICENSE b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/LICENSE deleted file mode 100644 index 6c07561b..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -(The MIT License) - -Copyright (c) 2014 Guillermo Rauch - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/README.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/README.md deleted file mode 100644 index 9b4fd035..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# ms.js: miliseconds conversion utility - -```js -ms('2 days') // 172800000 -ms('1d') // 86400000 -ms('10h') // 36000000 -ms('2.5 hrs') // 9000000 -ms('2h') // 7200000 -ms('1m') // 60000 -ms('5s') // 5000 -ms('100') // 100 -``` - -```js -ms(60000) // "1m" -ms(2 * 60000) // "2m" -ms(ms('10 hours')) // "10h" -``` - -```js -ms(60000, { long: true }) // "1 minute" -ms(2 * 60000, { long: true }) // "2 minutes" -ms(ms('10 hours'), { long: true }) // "10 hours" -``` - -- Node/Browser compatible. Published as [`ms`](https://www.npmjs.org/package/ms) in [NPM](http://nodejs.org/download). -- If a number is supplied to `ms`, a string with a unit is returned. -- If a string that contains the number is supplied, it returns it as -a number (e.g: it returns `100` for `'100'`). -- If you pass a string with a number and a valid unit, the number of -equivalent ms is returned. - -## License - -MIT diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/index.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/index.js deleted file mode 100644 index 4f927716..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/index.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Helpers. - */ - -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; - -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} options - * @return {String|Number} - * @api public - */ - -module.exports = function(val, options){ - options = options || {}; - if ('string' == typeof val) return parse(val); - return options.long - ? long(val) - : short(val); -}; - -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - -function parse(str) { - str = '' + str; - if (str.length > 10000) return; - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); - if (!match) return; - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - } -} - -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function short(ms) { - if (ms >= d) return Math.round(ms / d) + 'd'; - if (ms >= h) return Math.round(ms / h) + 'h'; - if (ms >= m) return Math.round(ms / m) + 'm'; - if (ms >= s) return Math.round(ms / s) + 's'; - return ms + 'ms'; -} - -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function long(ms) { - return plural(ms, d, 'day') - || plural(ms, h, 'hour') - || plural(ms, m, 'minute') - || plural(ms, s, 'second') - || ms + ' ms'; -} - -/** - * Pluralization helper. - */ - -function plural(ms, n, name) { - if (ms < n) return; - if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; - return Math.ceil(ms / n) + ' ' + name + 's'; -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/package.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/package.json deleted file mode 100644 index 253335e6..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "ms", - "version": "0.7.1", - "description": "Tiny ms conversion utility", - "repository": { - "type": "git", - "url": "git://github.com/guille/ms.js.git" - }, - "main": "./index", - "devDependencies": { - "mocha": "*", - "expect.js": "*", - "serve": "*" - }, - "component": { - "scripts": { - "ms/index.js": "index.js" - } - }, - "gitHead": "713dcf26d9e6fd9dbc95affe7eff9783b7f1b909", - "bugs": { - "url": "https://github.com/guille/ms.js/issues" - }, - "homepage": "https://github.com/guille/ms.js", - "_id": "ms@0.7.1", - "scripts": {}, - "_shasum": "9cd13c03adbff25b65effde7ce864ee952017098", - "_from": "ms@0.7.1", - "_npmVersion": "2.7.5", - "_nodeVersion": "0.12.2", - "_npmUser": { - "name": "rauchg", - "email": "rauchg@gmail.com" - }, - "maintainers": [ - { - "name": "rauchg", - "email": "rauchg@gmail.com" - } - ], - "dist": { - "shasum": "9cd13c03adbff25b65effde7ce864ee952017098", - "tarball": "http://registry.npmjs.org/ms/-/ms-0.7.1.tgz" - }, - "directories": {}, - "_resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "readme": "ERROR: No README data found!" -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/package.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/package.json deleted file mode 100644 index 7e6d9fc5..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/package.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "name": "debug", - "version": "2.2.0", - "repository": { - "type": "git", - "url": "git://github.com/visionmedia/debug.git" - }, - "description": "small debugging utility", - "keywords": [ - "debug", - "log", - "debugger" - ], - "author": { - "name": "TJ Holowaychuk", - "email": "tj@vision-media.ca" - }, - "contributors": [ - { - "name": "Nathan Rajlich", - "email": "nathan@tootallnate.net", - "url": "http://n8.io" - } - ], - "license": "MIT", - "dependencies": { - "ms": "0.7.1" - }, - "devDependencies": { - "browserify": "9.0.3", - "mocha": "*" - }, - "main": "./node.js", - "browser": "./browser.js", - "component": { - "scripts": { - "debug/index.js": "browser.js", - "debug/debug.js": "debug.js" - } - }, - "gitHead": "b38458422b5aa8aa6d286b10dfe427e8a67e2b35", - "bugs": { - "url": "https://github.com/visionmedia/debug/issues" - }, - "homepage": "https://github.com/visionmedia/debug", - "_id": "debug@2.2.0", - "scripts": {}, - "_shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", - "_from": "debug@*", - "_npmVersion": "2.7.4", - "_nodeVersion": "0.12.2", - "_npmUser": { - "name": "tootallnate", - "email": "nathan@tootallnate.net" - }, - "maintainers": [ - { - "name": "tjholowaychuk", - "email": "tj@vision-media.ca" - }, - { - "name": "tootallnate", - "email": "nathan@tootallnate.net" - } - ], - "dist": { - "shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", - "tarball": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz" - }, - "directories": {}, - "_resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "readme": "ERROR: No README data found!" -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/.npmignore b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/.npmignore deleted file mode 100644 index 17d6b367..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/.npmignore +++ /dev/null @@ -1 +0,0 @@ -/*.tgz diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/CHANGELOG.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/CHANGELOG.md deleted file mode 100644 index 42bcb60a..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -## Unreleased -- Fixes stringify to only take ancestors into account when checking - circularity. - It previously assumed every visited object was circular which led to [false - positives][issue9]. - Uses the tiny serializer I wrote for [Must.js][must] a year and a half ago. -- Fixes calling the `replacer` function in the proper context (`thisArg`). -- Fixes calling the `cycleReplacer` function in the proper context (`thisArg`). -- Speeds serializing by a factor of - Big-O(h-my-god-it-linearly-searched-every-object) it had ever seen. Searching - only the ancestors for a circular references speeds up things considerably. - -[must]: https://github.com/moll/js-must -[issue9]: https://github.com/isaacs/json-stringify-safe/issues/9 diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/LICENSE b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/LICENSE deleted file mode 100644 index 19129e31..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -The ISC License - -Copyright (c) Isaac Z. Schlueter and Contributors - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/Makefile b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/Makefile deleted file mode 100644 index 36088c72..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -NODE_OPTS = -TEST_OPTS = - -love: - @echo "Feel like makin' love." - -test: - @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot $(TEST_OPTS) - -spec: - @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec $(TEST_OPTS) - -autotest: - @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot --watch $(TEST_OPTS) - -autospec: - @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec --watch $(TEST_OPTS) - -pack: - @file=$$(npm pack); echo "$$file"; tar tf "$$file" - -publish: - npm publish - -tag: - git tag "v$$(node -e 'console.log(require("./package").version)')" - -clean: - rm -f *.tgz - npm prune --production - -.PHONY: love -.PHONY: test spec autotest autospec -.PHONY: pack publish tag -.PHONY: clean diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/README.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/README.md deleted file mode 100644 index a11f302a..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# json-stringify-safe - -Like JSON.stringify, but doesn't throw on circular references. - -## Usage - -Takes the same arguments as `JSON.stringify`. - -```javascript -var stringify = require('json-stringify-safe'); -var circularObj = {}; -circularObj.circularRef = circularObj; -circularObj.list = [ circularObj, circularObj ]; -console.log(stringify(circularObj, null, 2)); -``` - -Output: - -```json -{ - "circularRef": "[Circular]", - "list": [ - "[Circular]", - "[Circular]" - ] -} -``` - -## Details - -``` -stringify(obj, serializer, indent, decycler) -``` - -The first three arguments are the same as to JSON.stringify. The last -is an argument that's only used when the object has been seen already. - -The default `decycler` function returns the string `'[Circular]'`. -If, for example, you pass in `function(k,v){}` (return nothing) then it -will prune cycles. If you pass in `function(k,v){ return {foo: 'bar'}}`, -then cyclical objects will always be represented as `{"foo":"bar"}` in -the result. - -``` -stringify.getSerialize(serializer, decycler) -``` - -Returns a serializer that can be used elsewhere. This is the actual -function that's passed to JSON.stringify. - -**Note** that the function returned from `getSerialize` is stateful for now, so -do **not** use it more than once. diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/package.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/package.json deleted file mode 100644 index 7a97649f..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/package.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "name": "json-stringify-safe", - "version": "5.0.1", - "description": "Like JSON.stringify, but doesn't blow up on circular refs.", - "keywords": [ - "json", - "stringify", - "circular", - "safe" - ], - "homepage": "https://github.com/isaacs/json-stringify-safe", - "bugs": { - "url": "https://github.com/isaacs/json-stringify-safe/issues" - }, - "author": { - "name": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me" - }, - "contributors": [ - { - "name": "Andri Möll", - "email": "andri@dot.ee", - "url": "http://themoll.com" - } - ], - "license": "ISC", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/json-stringify-safe.git" - }, - "main": "stringify.js", - "scripts": { - "test": "node test.js" - }, - "devDependencies": { - "mocha": ">= 2.1.0 < 3", - "must": ">= 0.12 < 0.13", - "sinon": ">= 1.12.2 < 2" - }, - "gitHead": "3890dceab3ad14f8701e38ca74f38276abc76de5", - "_id": "json-stringify-safe@5.0.1", - "_shasum": "1296a2d58fd45f19a0f6ce01d65701e2c735b6eb", - "_from": "json-stringify-safe@*", - "_npmVersion": "2.10.0", - "_nodeVersion": "2.0.1", - "_npmUser": { - "name": "isaacs", - "email": "isaacs@npmjs.com" - }, - "dist": { - "shasum": "1296a2d58fd45f19a0f6ce01d65701e2c735b6eb", - "tarball": "http://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "maintainers": [ - { - "name": "isaacs", - "email": "i@izs.me" - }, - { - "name": "moll", - "email": "andri@dot.ee" - } - ], - "directories": {}, - "_resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "readme": "ERROR: No README data found!" -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/stringify.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/stringify.js deleted file mode 100644 index 124a4521..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/stringify.js +++ /dev/null @@ -1,27 +0,0 @@ -exports = module.exports = stringify -exports.getSerialize = serializer - -function stringify(obj, replacer, spaces, cycleReplacer) { - return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces) -} - -function serializer(replacer, cycleReplacer) { - var stack = [], keys = [] - - if (cycleReplacer == null) cycleReplacer = function(key, value) { - if (stack[0] === value) return "[Circular ~]" - return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]" - } - - return function(key, value) { - if (stack.length > 0) { - var thisPos = stack.indexOf(this) - ~thisPos ? stack.splice(thisPos + 1) : stack.push(this) - ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key) - if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value) - } - else stack.push(value) - - return replacer == null ? value : replacer.call(this, key, value) - } -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/mocha.opts b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/mocha.opts deleted file mode 100644 index 2544e586..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/mocha.opts +++ /dev/null @@ -1,2 +0,0 @@ ---recursive ---require must diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/stringify_test.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/stringify_test.js deleted file mode 100644 index 5b325831..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/stringify_test.js +++ /dev/null @@ -1,246 +0,0 @@ -var Sinon = require("sinon") -var stringify = require("..") -function jsonify(obj) { return JSON.stringify(obj, null, 2) } - -describe("Stringify", function() { - it("must stringify circular objects", function() { - var obj = {name: "Alice"} - obj.self = obj - var json = stringify(obj, null, 2) - json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) - }) - - it("must stringify circular objects with intermediaries", function() { - var obj = {name: "Alice"} - obj.identity = {self: obj} - var json = stringify(obj, null, 2) - json.must.eql(jsonify({name: "Alice", identity: {self: "[Circular ~]"}})) - }) - - it("must stringify circular objects deeper", function() { - var obj = {name: "Alice", child: {name: "Bob"}} - obj.child.self = obj.child - - stringify(obj, null, 2).must.eql(jsonify({ - name: "Alice", - child: {name: "Bob", self: "[Circular ~.child]"} - })) - }) - - it("must stringify circular objects deeper with intermediaries", function() { - var obj = {name: "Alice", child: {name: "Bob"}} - obj.child.identity = {self: obj.child} - - stringify(obj, null, 2).must.eql(jsonify({ - name: "Alice", - child: {name: "Bob", identity: {self: "[Circular ~.child]"}} - })) - }) - - it("must stringify circular objects in an array", function() { - var obj = {name: "Alice"} - obj.self = [obj, obj] - - stringify(obj, null, 2).must.eql(jsonify({ - name: "Alice", self: ["[Circular ~]", "[Circular ~]"] - })) - }) - - it("must stringify circular objects deeper in an array", function() { - var obj = {name: "Alice", children: [{name: "Bob"}, {name: "Eve"}]} - obj.children[0].self = obj.children[0] - obj.children[1].self = obj.children[1] - - stringify(obj, null, 2).must.eql(jsonify({ - name: "Alice", - children: [ - {name: "Bob", self: "[Circular ~.children.0]"}, - {name: "Eve", self: "[Circular ~.children.1]"} - ] - })) - }) - - it("must stringify circular arrays", function() { - var obj = [] - obj.push(obj) - obj.push(obj) - var json = stringify(obj, null, 2) - json.must.eql(jsonify(["[Circular ~]", "[Circular ~]"])) - }) - - it("must stringify circular arrays with intermediaries", function() { - var obj = [] - obj.push({name: "Alice", self: obj}) - obj.push({name: "Bob", self: obj}) - - stringify(obj, null, 2).must.eql(jsonify([ - {name: "Alice", self: "[Circular ~]"}, - {name: "Bob", self: "[Circular ~]"} - ])) - }) - - it("must stringify repeated objects in objects", function() { - var obj = {} - var alice = {name: "Alice"} - obj.alice1 = alice - obj.alice2 = alice - - stringify(obj, null, 2).must.eql(jsonify({ - alice1: {name: "Alice"}, - alice2: {name: "Alice"} - })) - }) - - it("must stringify repeated objects in arrays", function() { - var alice = {name: "Alice"} - var obj = [alice, alice] - var json = stringify(obj, null, 2) - json.must.eql(jsonify([{name: "Alice"}, {name: "Alice"}])) - }) - - it("must call given decycler and use its output", function() { - var obj = {} - obj.a = obj - obj.b = obj - - var decycle = Sinon.spy(function() { return decycle.callCount }) - var json = stringify(obj, null, 2, decycle) - json.must.eql(jsonify({a: 1, b: 2}, null, 2)) - - decycle.callCount.must.equal(2) - decycle.thisValues[0].must.equal(obj) - decycle.args[0][0].must.equal("a") - decycle.args[0][1].must.equal(obj) - decycle.thisValues[1].must.equal(obj) - decycle.args[1][0].must.equal("b") - decycle.args[1][1].must.equal(obj) - }) - - it("must call replacer and use its output", function() { - var obj = {name: "Alice", child: {name: "Bob"}} - - var replacer = Sinon.spy(bangString) - var json = stringify(obj, replacer, 2) - json.must.eql(jsonify({name: "Alice!", child: {name: "Bob!"}})) - - replacer.callCount.must.equal(4) - replacer.args[0][0].must.equal("") - replacer.args[0][1].must.equal(obj) - replacer.thisValues[1].must.equal(obj) - replacer.args[1][0].must.equal("name") - replacer.args[1][1].must.equal("Alice") - replacer.thisValues[2].must.equal(obj) - replacer.args[2][0].must.equal("child") - replacer.args[2][1].must.equal(obj.child) - replacer.thisValues[3].must.equal(obj.child) - replacer.args[3][0].must.equal("name") - replacer.args[3][1].must.equal("Bob") - }) - - it("must call replacer after describing circular references", function() { - var obj = {name: "Alice"} - obj.self = obj - - var replacer = Sinon.spy(bangString) - var json = stringify(obj, replacer, 2) - json.must.eql(jsonify({name: "Alice!", self: "[Circular ~]!"})) - - replacer.callCount.must.equal(3) - replacer.args[0][0].must.equal("") - replacer.args[0][1].must.equal(obj) - replacer.thisValues[1].must.equal(obj) - replacer.args[1][0].must.equal("name") - replacer.args[1][1].must.equal("Alice") - replacer.thisValues[2].must.equal(obj) - replacer.args[2][0].must.equal("self") - replacer.args[2][1].must.equal("[Circular ~]") - }) - - it("must call given decycler and use its output for nested objects", - function() { - var obj = {} - obj.a = obj - obj.b = {self: obj} - - var decycle = Sinon.spy(function() { return decycle.callCount }) - var json = stringify(obj, null, 2, decycle) - json.must.eql(jsonify({a: 1, b: {self: 2}})) - - decycle.callCount.must.equal(2) - decycle.args[0][0].must.equal("a") - decycle.args[0][1].must.equal(obj) - decycle.args[1][0].must.equal("self") - decycle.args[1][1].must.equal(obj) - }) - - it("must use decycler's output when it returned null", function() { - var obj = {a: "b"} - obj.self = obj - obj.selves = [obj, obj] - - function decycle() { return null } - stringify(obj, null, 2, decycle).must.eql(jsonify({ - a: "b", - self: null, - selves: [null, null] - })) - }) - - it("must use decycler's output when it returned undefined", function() { - var obj = {a: "b"} - obj.self = obj - obj.selves = [obj, obj] - - function decycle() {} - stringify(obj, null, 2, decycle).must.eql(jsonify({ - a: "b", - selves: [null, null] - })) - }) - - it("must throw given a decycler that returns a cycle", function() { - var obj = {} - obj.self = obj - var err - function identity(key, value) { return value } - try { stringify(obj, null, 2, identity) } catch (ex) { err = ex } - err.must.be.an.instanceof(TypeError) - }) - - describe(".getSerialize", function() { - it("must stringify circular objects", function() { - var obj = {a: "b"} - obj.circularRef = obj - obj.list = [obj, obj] - - var json = JSON.stringify(obj, stringify.getSerialize(), 2) - json.must.eql(jsonify({ - "a": "b", - "circularRef": "[Circular ~]", - "list": ["[Circular ~]", "[Circular ~]"] - })) - }) - - // This is the behavior as of Mar 3, 2015. - // The serializer function keeps state inside the returned function and - // so far I'm not sure how to not do that. JSON.stringify's replacer is not - // called _after_ serialization. - xit("must return a function that could be called twice", function() { - var obj = {name: "Alice"} - obj.self = obj - - var json - var serializer = stringify.getSerialize() - - json = JSON.stringify(obj, serializer, 2) - json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) - - json = JSON.stringify(obj, serializer, 2) - json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) - }) - }) -}) - -function bangString(key, value) { - return typeof value == "string" ? value + "!" : value -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/package.json b/test/fixtures/module-fixture/node_modules/pmx/package.json deleted file mode 100644 index dc51be59..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "pmx", - "version": "0.3.23", - "description": "Keymetrics++ and PM2 adapter", - "main": "index.js", - "dependencies": { - "debug": "*", - "json-stringify-safe": "*" - }, - "devDependencies": { - "express": "*", - "request": "*", - "should": "*", - "mocha": "*", - "shelljs": "*" - }, - "scripts": { - "test": "DEBUG='axm:*' mocha test/*.mocha.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/keymetrics/pmx.git" - }, - "author": { - "name": "Keymetrics I/O" - }, - "license": "MIT", - "readme": "\n# Driver for Keymetrics\n\n![Keymetrics](https://keymetrics.io/assets/images/application-demo.png)\n\nPMX is a module that allows you to create advanced interactions with Keymetrics.\n\nWith it you can:\n- Trigger remote actions / functions\n- Analyze custom metrics / variables (with utilities like Histogram/Counter/Metric/Meters)\n- Report errors (uncaught exceptions and custom errors)\n- Emit events\n- Analyze HTTP latency\n\n# Installation\n\n![Build Status](https://api.travis-ci.org/keymetrics/pmx.png?branch=master)\n\nInstall PMX and add it to your package.json via:\n\n```bash\n$ npm install pmx --save\n```\n\nThen init the module to monitor HTTP, Errors and diverse metrics.\n```javascript\nvar pmx = require('pmx').init(); // By default everything is enabled and ignore_routes is empty\n```\nOr choose what to monitor.\n```javascript\nvar pmx = require('pmx').init({\n http : true, // HTTP routes logging (default: true)\n ignore_routes : [/socket\\.io/, /notFound/], // Ignore http routes with this pattern (Default: [])\n errors : true, // Exceptions loggin (default: true)\n custom_probes : true, // Custom probes (default: true)\n network : true, // Traffic usage monitoring (default: false)\n ports : true // Shows which ports your app is listening on (default: false)\n});\n```\n\n# Custom monitoring\n\n## Emit Events\n\nEmit events and get historical and statistics:\n\n```javascript\nvar pmx = require('pmx');\n\npmx.emit('user:register', {\n user : 'Alex registered',\n email : 'thorustor@gmail.com'\n});\n```\n\n## Custom Action\n\nTrigger function from Keymetrics\n\n### Long running\n\n```javascript\nvar pmx = require('pmx');\n\npmx.action('db:clean', { comment : 'Description for this action' }, function(reply) {\n clean.db(function() {\n /**\n * reply() must be called at the end of the action\n */\n reply({success : true});\n });\n});\n```\n\n## Errors\n\nCatch uncaught exceptions:\n```javascript\nvar pmx = require('pmx').init();\n```\n\nAttach more data from errors that happens in Express:\n```javascript\nvar pmx = require('pmx');\n\napp.get('/' ...);\napp.post(...);\n\napp.use(pmx.expressErrorHandler());\n```\n\nTrigger custom errors:\n```javascript\nvar pmx = require('pmx');\n\npmx.notify({ success : false });\n\npmx.notify('This is an error');\n\npmx.notify(new Error('This is an error'));\n```\n\n## TCP network usage monitoring\n\nIf you enable the flag `network: true` when you init pmx it will show network usage datas (download and upload) in realtime.\n\nIf you enable the flag `ports: true` when you init pmx it will show which ports your app is listenting on.\n\n\n## HTTP latency analysis\n\nMonitor routes, latency and codes. REST compliant.\n\n```javascript\npmx.http(); // You must do this BEFORE any require('http')\n```\nIgnore some routes by passing a list of regular expressions.\n```javascript\npmx.http({\n http : true, // (Default: true)\n ignore_routes : [/socket\\.io/, /notFound/] // Ignore http routes with this pattern (Default: [])\n});\n```\nThis can also be done via pmx.init()\n```javascript\npmx.init({\n http : true, // (Default: true)\n ignore_routes : [/socket\\.io/, /notFound/] // Ignore http routes with this pattern (Default: [])\n});\n```\n\n**This module is enabled by default if you called pmx with the init() function.**\n\n## Measure\n\nMeasure critical segments of you code thanks to 4 kind of probes:\n\n- Simple metrics: Values that can be read instantly\n - Monitor variable value\n- Counter: Things that increment or decrement\n - Downloads being processed, user connected\n- Meter: Things that are measured as events / interval\n - Request per minute for a http server\n- Histogram: Keeps a resevoir of statistically relevant values biased towards the last 5 minutes to explore their distribution\n - Monitor the mean of execution of a query into database\n\n#### Common options\n\n- `name` : The probe name as is will be displayed on the **Keymetrics** dashboard\n- `agg_type` : This param is optional, it can be `sum`, `max`, `min`, `avg` (default) or `none`. It will impact the way the probe data are aggregated within the **Keymetrics** backend. Use `none` if this is irrelevant (eg: constant or string value).\n\n\n### Metric\n\nValues that can be read instantly.\n\n```javascript\nvar probe = pmx.probe();\n\nvar metric = probe.metric({\n name : 'Realtime user',\n agg_type: 'max',\n value : function() {\n return Object.keys(users).length;\n }\n});\n```\n\n### Counter\n\nThings that increment or decrement.\n\n```javascript\nvar probe = pmx.probe();\n\nvar counter = probe.counter({\n name : 'Downloads',\n agg_type: 'sum'\n});\n\nhttp.createServer(function(req, res) {\n counter.inc();\n req.on('end', function() {\n counter.dec();\n });\n});\n```\n\n### Meter\n\nThings that are measured as events / interval.\n\n```javascript\nvar probe = pmx.probe();\n\nvar meter = probe.meter({\n name : 'req/sec',\n samples : 1,\n timeframe : 60\n});\n\nhttp.createServer(function(req, res) {\n meter.mark();\n res.end({success:true});\n});\n```\n#### Options\n\n**samples** option is the rate unit. Defaults to **1** sec.\n\n**timeframe** option is the timeframe over which events will be analyzed. Defaults to **60** sec.\n\n### Histogram\n\nKeeps a resevoir of statistically relevant values biased towards the last 5 minutes to explore their distribution.\n\n```javascript\nvar probe = pmx.probe();\n\nvar histogram = probe.histogram({\n name : 'latency',\n measurement : 'mean'\n});\n\nvar latency = 0;\n\nsetInterval(function() {\n latency = Math.round(Math.random() * 100);\n histogram.update(latency);\n}, 100);\n```\n\n#### Options\n\n**measurement** option can be:\n\n- min: The lowest observed value.\n- max: The highest observed value.\n- sum: The sum of all observed values.\n- variance: The variance of all observed values.\n- mean: The average of all observed values.\n- stddev: The stddev of all observed values.\n- count: The number of observed values.\n- median: 50% of all values in the resevoir are at or below this value.\n- p75: See median, 75% percentile.\n- p95: See median, 95% percentile.\n- p99: See median, 99% percentile.\n- p999: See median, 99.9% percentile.\n\n## Expose data (JSON object)\n\n```javascript\npmx.transpose('variable name', function() { return my_data });\n\n// or\n\npmx.tranpose({\n name : 'variable name',\n value : function() { return my_data; }\n});\n```\n\n## Modules\n\n### Simple app\n\n```\nprocess.env.MODULE_DEBUG = true;\n\nvar pmx = require('pmx');\n\nvar conf = pmx.initModule();\n```\n\n# Beta\n\n### Long running with data emitter (scoped action)\n\nA scoped action is an action that can emit logs related to this action.\n\n```javascript\nvar pmx = require('pmx');\n\npmx.scopedAction('scoped:action', function(options, res) {\n var i = setInterval(function() {\n // Emit progress data\n if (error)\n res.error('oops');\n else\n res.send('this is a chunk of data');\n }, 1000);\n\n setTimeout(function() {\n clearInterval(i);\n return res.end();\n }, 8000);\n});\n```\n\n\n# License\n\nMIT\n", - "readmeFilename": "README.md", - "gitHead": "8f487ccb89d25d5bc1d23a0fc75a50ae7d9aab5a", - "bugs": { - "url": "https://github.com/keymetrics/pmx/issues" - }, - "homepage": "https://github.com/keymetrics/pmx#readme", - "_id": "pmx@0.3.23", - "_shasum": "fbb9c118f63109aedeb4309903898c70f978396b", - "_from": "pmx@*" -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/action.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/action.mocha.js deleted file mode 100644 index 3a195adb..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/action.mocha.js +++ /dev/null @@ -1,164 +0,0 @@ - - -var pmx = require('..'); - -function forkApp(script) { - var app = require('child_process').fork(__dirname + (script || '/proc.mock.js'), []); - return app; -} - -function forkAppWithOptions() { - var app = require('child_process').fork(__dirname + '/proc-option.mock.js', []); - return app; -} - -describe('Action module', function() { - - describe('Action without option', function() { - var app; - var action_name; - - after(function() { - process.kill(app.pid); - }); - - it('should notify PM2 of a new action available', function(done) { - app = forkApp(); - - app.once('message', function(dt) { - dt.type.should.eql('axm:action'); - dt.data.action_name.should.eql('test:nab'); - dt.data.opts.comment.should.eql('This is a test'); - dt.data.opts.display.should.eql(true); - - action_name = dt.data.action_name; - - done(); - }); - }); - - it('should trigger the action', function(done) { - app.once('message', function(dt) { - dt.type.should.eql('axm:reply'); - dt.data.return.res.should.eql('hello moto'); - done(); - }); - - app.send(action_name); - }); - - it('should trigger the action via Object arity 1 (FALLBACK)', function(done) { - app.once('message', function(dt) { - dt.type.should.eql('axm:reply'); - dt.data.return.res.should.eql('hello moto'); - done(); - }); - - app.send({msg : action_name, opts : { sisi : 'true' }}); - }); - - it('should not trigger the action if wrong action name', function(done) { - app.once('message', function(dt) { - throw new Error('Should not be called'); - }); - - app.send({ - action_name : 'im unknown' - }); - - setTimeout(done, 200); - }); - }); - - describe('Action with extra options (parameters)', function() { - var app; - var action_name; - - after(function() { - process.kill(app.pid); - }); - - it('should notify PM2 of a new action available', function(done) { - app = forkAppWithOptions(); - - app.once('message', function(dt) { - dt.type.should.eql('axm:action'); - dt.data.action_name.should.eql('test:with:options'); - action_name = dt.data.action_name; - done(); - }); - }); - - it('should trigger the action without failing (2 args without option)', function(done) { - app.once('message', function(dt) { - dt.type.should.eql('axm:reply'); - dt.data.return.res.should.eql('hello moto'); - done(); - }); - - app.send(action_name); - }); - - it('should trigger the action', function(done) { - app.once('message', function(dt) { - dt.type.should.eql('axm:reply'); - dt.data.return.res.should.eql('hello moto'); - dt.data.return.options.f1.should.eql('ab'); - done(); - }); - - app.send({ msg : action_name, opts : { f1 : 'ab', f2 : 'cd'}}); - }); - - it('should not trigger the action if wrong action name', function(done) { - app.once('message', function(dt) { - throw new Error('Should not be called'); - }); - - app.send('im unknown'); - - setTimeout(done, 200); - }); - - }); - - describe('Scoped Action (option, emitter, callback)', function() { - var app; - var action_name; - - after(function() { - process.kill(app.pid); - }); - - it('should notify PM2 of a new action available', function(done) { - app = forkApp('/fixtures/scoped-action.fixture.js'); - - app.once('message', function(dt) { - dt.type.should.eql('axm:action'); - dt.data.action_name.should.eql('scoped:action'); - dt.data.action_type.should.eql('scoped'); - action_name = dt.data.action_name; - done(); - }); - }); - - it('should stream data', function(done) { - app.once('message', function(dt) { - dt.type.should.eql('axm:scoped_action:stream'); - dt.data.data.should.eql('data random'); - done(); - }); - - app.send({ action_name : action_name, uuid : 'Random nb'}); - }); - - it('should trigger the action', function(done) { - app.on('message', function(dt) { - if (dt.type == 'axm:scoped_action:end') - done(); - }); - }); - - }); - -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/auto.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/auto.mocha.js deleted file mode 100644 index 7e4239ec..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/auto.mocha.js +++ /dev/null @@ -1,101 +0,0 @@ - - -var axm = require('..'); -var request = require('request'); -var should = require('should'); - -var Plan = require('./helpers/plan'); - -function fork() { - return require('child_process').fork(__dirname + '/transaction/app.mock.auto.js', []); -} - -describe('Automatic transaction', function() { - it('should have right properties', function(done) { - axm.should.have.property('http'); - done(); - }); - - var app; - - - after(function() { - process.kill(app.pid); - }); - - it('should receive configuration flag', function(done) { - app = fork(); - - app.once('message', function(data) { - data.type.should.eql('axm:option:configuration'); - done(); - }); - - }); - - it('should not log fast http request', function(done) { - var rcpt = function(data) { - if (data.type == 'axm:option:configuration') - return false; - if (data.type == 'axm:monitor') - return false; - - return data.type.should.not.eql('http:transaction'); - }; - - app.on('message', rcpt); - - setTimeout(function() { - app.removeListener('message', rcpt); - return done(); - }, 500); - - setTimeout(function() { - request('http://127.0.0.1:9007/', function(req, res) {}); - }, 100); - }); - - it('should not log ignored http request', function(done) { - var timer = setTimeout(function() { - app.removeListener('message', rcpt); - return done(); - }, 1000); - - var rcpt = function(data) { - if (data.type == 'axm:option:configuration') - return false; - if (data.type == 'axm:monitor') - return false; - - return data.type.should.not.eql('http:transaction'); - }; - - app.on('message', rcpt); - - setTimeout(function() { - request('http://127.0.0.1:9007/socket.io/slow', function(req, res) {}); - }, 100); - }); - - it('should log slow http request', function(done) { - var plan = new Plan(3, done); - - app.on('message', function(data) { - if (data.type == 'axm:monitor') { - plan.ok(true); - if (Object.keys(data.data) < 3) - plan.ok(false); - } - - if (data.type == 'http:transaction') { - data.data.should.have.properties('ip', 'time', 'url', 'method'); - plan.ok(true); - } - }); - - setTimeout(function() { - request('http://127.0.0.1:9007/slow', function(req, res) {}); - }, 100); - }); - -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/event.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/event.mocha.js deleted file mode 100644 index 901597df..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/event.mocha.js +++ /dev/null @@ -1,38 +0,0 @@ - -var axm = require('..'); - -function fork() { - return require('child_process').fork(__dirname + '/event.mock.js', []); -} - -describe('Event', function() { - it('should have right property', function(done) { - axm.should.have.property('emit'); - done(); - }); - - describe('Event scenario', function() { - var app; - - before(function() { - app = fork(); - }); - - after(function() { - process.kill(app.pid); - }); - - it('should send right event data when called', function(done) { - app.once('message', function(data) { - data.type.should.eql('human:event'); - data.data.user.should.eql('toto'); - data.data.__name.should.eql('test'); - data.data.subobj.subobj.a.should.eql('b'); - done(); - }); - }); - }); - - - -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/event.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/event.mock.js deleted file mode 100644 index faacdcf5..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/event.mock.js +++ /dev/null @@ -1,13 +0,0 @@ - -var axm = require('..'); - -setInterval(function() { - axm.emit('test', { - user : 'toto', - subobj : { - subobj : { - a : 'b' - } - } - }); -}, 100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/express/app.js b/test/fixtures/module-fixture/node_modules/pmx/test/express/app.js deleted file mode 100644 index 39da2a22..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/express/app.js +++ /dev/null @@ -1,23 +0,0 @@ - -var axm = require('../..'); -var express = require('express'); -var app = express(); - -var err = new Error('jajajja'); - -err.url = 'http://thd.com/'; - -axm.notify(err); - -app.get('/', function(req, res){ - res.send('Hello World'); -}); - -app.get('/error', function(req, res, next){ - next(new Error('toto')); -}); - -app.use(axm.expressErrorHandler()); - - -app.listen(3001); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/express/package.json b/test/fixtures/module-fixture/node_modules/pmx/test/express/package.json deleted file mode 100644 index a8c001f1..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/express/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "express", - "version": "0.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies" : { - "express" : "*" - }, - "author": "", - "license": "ISC" -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/histogram.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/histogram.fixture.js deleted file mode 100644 index f8a2776a..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/histogram.fixture.js +++ /dev/null @@ -1,45 +0,0 @@ - - -var axm = require('../..'); - -var probe = axm.probe(); - -var histogram = probe.histogram({ - name : 'test', - measurement : 'p95', - agg_type: 'sum' -}); - -var a = 0; - -setInterval(function() { - a = Math.round(Math.random() * 100); - histogram.update(a); -}, 100); - -var h2 = probe.histogram({ - name : 'mean', - measurement : 'mean', - unit : 'ms' -}); - -var b = 0; - -setInterval(function() { - b = Math.round(Math.random() * 100); - h2.update(b); -}, 100); - - -var h3 = probe.histogram({ - name : 'min', - measurement : 'min', - agg_type: 'min' -}); - -var c = 0; - -setInterval(function() { - c = Math.round(Math.random() * 100); - h3.update(c); -}, 100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/module.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/module.fixture.js deleted file mode 100644 index b19db785..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/module.fixture.js +++ /dev/null @@ -1,4 +0,0 @@ - -var pmx = require('../../..'); - -var conf = pmx.initModule(); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/package.json b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/package.json deleted file mode 100644 index 8d00b455..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "module", - "version": "1.0.0", - "description": "comment", - "main": "module.fixture.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "config" : { - "initial" : "init-val" - }, - "author": "strzel", - "license": "ISC" -} diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor.mock.js deleted file mode 100644 index ba175350..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor.mock.js +++ /dev/null @@ -1,20 +0,0 @@ - -var axm = require('../..'); - -var obj = axm.enableProbes(); - -var i = 2; - -obj.it_works = true; -obj.value = 20; -obj.i = i; - -setTimeout(function() { - i = 4; - obj.it_works = false; - obj.value = 99; - - setTimeout(function() { - axm.stopProbes(); - }, 1100); -}, 1100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor2.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor2.mock.js deleted file mode 100644 index b9216580..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor2.mock.js +++ /dev/null @@ -1,26 +0,0 @@ - - -var axm = require('../..'); - -var obj = axm.enableProbes(); - -var a = { - 'aaa' : { 'ok' : true }, - 'bbb' : { 'ok' : false } -}; - -// Does not refresh because it copies the val -obj.count = Object.keys(a).length; - -obj.countFn = function() { - return Object.keys(a).length; -}; - -setTimeout(function () { - a.ccc = 'test'; - a.ddd = 'test'; - - setTimeout(function () { - axm.stopProbes(); - }, 1100); -}, 1100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify.mock.js deleted file mode 100644 index 87d40422..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify.mock.js +++ /dev/null @@ -1,6 +0,0 @@ - -var axm = require('../..'); - -setTimeout(function() { - axm.notify('hey'); -}, 100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify_catch_all.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify_catch_all.mock.js deleted file mode 100644 index a05c44df..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify_catch_all.mock.js +++ /dev/null @@ -1,9 +0,0 @@ - - -var axm = require('../..'); - -axm.catchAll(); - -setTimeout(function() { - throw new Error('global error'); -}, 200); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/probe.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/probe.fixture.js deleted file mode 100644 index 89e30c27..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/probe.fixture.js +++ /dev/null @@ -1,118 +0,0 @@ - -var axm = require('../..'); - -var probe = axm.probe(); - -var users = { - 'alex' : 'ok', - 'musta' : 'fa' -}; - -/** - * Monitor synchronous return of functions - */ -var rt_users = probe.metric({ - name : 'Realtime user', - agg_type: 'max', - value : function() { - return Object.keys(users).length; - } -}); - -/** - * Monitor value - */ - -var config_example = { - val : 'hey', - test : { - a : 'good', - sign : 'healthy' - } -} - -var cheerio = probe.metric({ - name : 'Cheerio', - value : function() { - return config_example; - } -}); - -/** - * Monitor value - */ - - -// probe.transpose('docker_config', config_example); - -probe.transpose({ - name : 'style_2_docker_config', - data : function doSomething() { - return config_example; - } -}); - -probe.transpose('style_1_docker_config', function doSomething() { - return config_example; -}); - - -/** - * Meter for HTTP - */ -var meter = probe.meter({ - name : 'req/min', - agg_type: 'min', - seconds : 60 -}); - -var http = require('http'); - -http.createServer(function(req, res) { - meter.mark(); - res.end('Thanks'); -}).listen(3400); - -/** - * Meter example - */ - -var meter2 = probe.meter({ - name : 'random', - unit : 'rd', - agg_type: 'sum', - seconds : 1 -}); - -setInterval(function() { - meter2.mark(Math.random() * 100); -}, 10); - - -setTimeout(function() { - counter.inc(); - config_example = { yes : true }; -}, 1100); - -/** - * Counter - */ - -var counter = probe.counter({ - name : 'Downloads', - agg_type: 'max' -}); - -counter.inc(); -counter.dec(); -counter.inc(); -counter.inc(); - -// console.log(cheerio.val()); -// setInterval(function() { -// console.log(counter.val()); -// console.log(meter.val()); -// console.log(meter2.val()); -// console.log(rt_users.val()); -// console.log(cheerio.val()); -// }, 1500); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.fixture.js deleted file mode 100644 index ca3497f1..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.fixture.js +++ /dev/null @@ -1,15 +0,0 @@ - - -var pmx = require('../..'); - -pmx.scopedAction('scoped:action', function(opts, res) { - var i = setInterval(function() { - // Emit progress data - res.send('data random'); - }, 100); - - setTimeout(function() { - clearInterval(i); - res.end('end data'); - }, 800); -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.mocha.js deleted file mode 100644 index 8b137891..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.mocha.js +++ /dev/null @@ -1 +0,0 @@ - diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/transpose.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/transpose.fixture.js deleted file mode 100644 index 8d7f926c..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/transpose.fixture.js +++ /dev/null @@ -1,36 +0,0 @@ - -var axm = require('../..'); - -var probe = axm.probe(); - - -var config_example = { - val : 'hey', - test : { - a : 'good', - sign : 'healthy' - } -} - -/** - * Monitor value - */ - -// This is ompossible to do :( (refresh value by pointer): -// -// probe.transpose('docker_config', config_example); - -probe.transpose({ - name : 'style_2_docker_config', - data : function doSomething() { - return config_example; - } -}); - -probe.transpose('style_1_docker_config', function doSomething() { - return config_example; -}); - -setTimeout(function() { - config_example.val = 'new value'; -}, 1100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/helpers/plan.js b/test/fixtures/module-fixture/node_modules/pmx/test/helpers/plan.js deleted file mode 100644 index 2523adcc..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/helpers/plan.js +++ /dev/null @@ -1,23 +0,0 @@ - -var assert = require('assert'); - -function Plan(count, done) { - this.done = done; - this.count = count; -} - -Plan.prototype.ok = function(expression) { - assert(expression); - - if (this.count === 0) { - assert(false, 'Too many assertions called'); - } else { - this.count--; - } - - if (this.count === 0) { - this.done(); - } -}; - -module.exports = Plan; diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/mocha.opts b/test/fixtures/module-fixture/node_modules/pmx/test/mocha.opts deleted file mode 100644 index d3cb22ae..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/mocha.opts +++ /dev/null @@ -1,4 +0,0 @@ ---require should ---reporter spec ---timeout 30000000 ---slow 300 diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/module.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/module.mocha.js deleted file mode 100644 index 9f2982a4..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/module.mocha.js +++ /dev/null @@ -1,119 +0,0 @@ - -var pmx = require('..'); -var should = require('should'); - -function forkWithoutEnv() { - var app = require('child_process').fork(__dirname + '/fixtures/module/module.fixture.js', [], { - env : { - } - }); - return app; -} - -function forkWithSpecificVar() { - var app = require('child_process').fork(__dirname + '/fixtures/module/module.fixture.js', [], { - env : { - 'module' : '{ "option1" : "value1", "option2" : "value2", "initial" : "over" }' - } - }); - return app; -} - -describe('PMX module', function() { - var app; - var action_name; - - it('should emit a new action', function(done) { - // 1 - It should emit an action - app = forkWithoutEnv(); - - app.once('message', function(dt) { - - /** - * Right event sent - */ - dt.type.should.eql('axm:option:configuration'); - - /** - * Options set - */ - dt.data.show_module_meta.should.exists; - dt.data.description.should.eql('comment'); - dt.data.module_version.should.eql('1.0.0'); - dt.data.module_name.should.eql('module'); - - /** - * Configuration succesfully passed - */ - dt.data.initial.should.eql('init-val'); - - /** - * Should configuration variable be mirrored into module_conf - * attribute (for keymetrics purposes) - */ - dt.data.module_conf.initial.should.eql('init-val'); - done(); - }); - }); - - it('should emit a new action', function(done) { - // 1 - It should emit an action - app = forkWithSpecificVar(); - - app.once('message', function(dt) { - - /** - * Right event sent - */ - dt.type.should.eql('axm:option:configuration'); - - /** - * Options set - */ - dt.data.show_module_meta.should.exists; - dt.data.description.should.eql('comment'); - dt.data.module_version.should.eql('1.0.0'); - dt.data.module_name.should.eql('module'); - - /** - * Configuration succesfully passed - */ - dt.data.option1.should.eql('value1'); - dt.data.option2.should.eql('value2'); - dt.data.initial.should.eql('over'); - - /** - * Should configuration variable be mirrored into module_conf - * attribute (for keymetrics purposes) - */ - dt.data.module_conf.option1.should.eql('value1'); - dt.data.module_conf.option2.should.eql('value2'); - dt.data.module_conf.initial.should.eql('over'); - done(); - }); - }); - - it('should find existing file', function(done) { - var content = pmx.resolvePidPaths([ - 'asdasdsad', - 'asdasd', - 'lolilol', - __dirname + '/fixtures/file.pid' - ]); - - content.should.eql(1456); - done(); - }); - - it('should return null', function(done) { - var content = pmx.resolvePidPaths([ - 'asdasdsad', - 'asdasd', - 'lolilol' - ]); - - should(content).be.null; - done(); - }); - -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/monitor.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/monitor.mocha.js deleted file mode 100644 index 859506d2..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/monitor.mocha.js +++ /dev/null @@ -1,65 +0,0 @@ - -var axm = require('..'); - -function fork() { - return require('child_process').fork(__dirname + '/fixtures/monitor.mock.js', []); -} - -function forkMonitor2() { - return require('child_process').fork(__dirname + '/fixtures/monitor2.mock.js', []); -} - -describe('Monitor', function() { - - it('should have properties', function(done) { - axm.should.have.property('enableProbes'); - done(); - }); - - - it('should send event when called', function(done) { - var app = fork(); - - app.once('message', function(pck) { - pck.type.should.eql('axm:monitor'); - - pck.data.it_works.should.eql(true); - pck.data.value.should.eql(20); - - app.once('message', function(pck) { - pck.type.should.eql('axm:monitor'); - - pck.data.it_works.should.eql(false); - pck.data.value.should.eql(99); - pck.data.i.should.eql(2); - - app.kill(); - - done(); - }); - }); - }); - - it('should send right value with monitor2', function(done) { - var app = forkMonitor2(); - - app.once('message', function(pck) { - pck.type.should.eql('axm:monitor'); - - pck.data.count.should.eql(2); - pck.data.countFn.should.eql(2); - - app.once('message', function(pck) { - pck.type.should.eql('axm:monitor'); - - pck.data.count.should.eql(2); - pck.data.countFn.should.eql(4); - - app.kill(); - - done(); - }); - }); - }); - -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/notify.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/notify.mocha.js deleted file mode 100644 index c38e8cff..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/notify.mocha.js +++ /dev/null @@ -1,90 +0,0 @@ - - -var axm = require('..'); -var should = require('should'); - -function forkCatch() { - var app = require('child_process').fork(__dirname + '/fixtures/notify_catch_all.mock.js', []); - return app; -} - -function forkNotify() { - var app = require('child_process').fork(__dirname + '/fixtures/notify.mock.js', []); - return app; -} - -describe('Notify exceptions', function() { - it('should have the right properties', function(done) { - axm.should.have.property('catchAll'); - axm.should.have.property('notify'); - axm.should.have.property('expressErrorHandler'); - done(); - }); - - it('should process simple string error', function(done) { - var ret = axm._interpretError('this is a message'); - should.exist(ret.stack); - should.exist(ret.message); - ret.message.should.eql('this is a message'); - done(); - }); - - it('should process JSON object', function(done) { - var ret = axm._interpretError({ - line : 'ok', - env : 'sisi' - }); - - should.exist(ret.stack); - should.exist(ret.message); - - ret.data.line.should.eql('ok'); - ret.data.env.should.eql('sisi'); - done(); - }); - - it('should process simple string', function(done) { - var ret = axm._interpretError('Error'); - - should.exist(ret.stack); - should.exist(ret.message); - - done(); - }); - - it('should process error', function(done) { - var ret = axm._interpretError(new Error('error')); - - should.exist(ret.stack); - should.exist(ret.message); - - done(); - }); - - - it('should catchAll exception in fork mode', function(done) { - var app = forkCatch(); - - app.once('message', function(data) { - data.type.should.eql('axm:option:configuration'); - app.once('message', function(data) { - data.type.should.eql('process:exception'); - data.data.message.should.eql('global error'); - process.kill(app.pid); - done(); - }); - }); - }); - - it('should notify process about error', function(done) { - var app = forkNotify(); - - app.once('message', function(data) { - data.type.should.eql('process:exception'); - data.data.message.should.eql('hey'); - process.kill(app.pid); - done(); - }); - }); - -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/pmx.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/pmx.mocha.js deleted file mode 100644 index 3f75ba81..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/pmx.mocha.js +++ /dev/null @@ -1,18 +0,0 @@ - -var pmx = require('..'); - -describe('PMX driver', function() { - it('should have the right properties', function(done) { - pmx.should.have.property('emit'); - pmx.should.have.property('action'); - done(); - }); - - describe('Event module', function() { - it('should not hang if process not forked', function(done) { - pmx.emit('testo', { data : 'ok' }); - done(); - }); - - }); -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/probe.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/probe.mocha.js deleted file mode 100644 index 1b705454..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/probe.mocha.js +++ /dev/null @@ -1,83 +0,0 @@ - -var axm = require('..'); - -function fork(script) { - var app = require('child_process').fork(__dirname + (script || '/fixtures/probe.fixture.js'), []); - return app; -} - -function forkHistogram() { - var app = require('child_process').fork(__dirname + '/fixtures/histogram.fixture.js', []); - return app; - -} -describe('Probe', function() { - it('should have the right properties', function(done) { - axm.should.have.property('probe'); - - var probe = axm.probe(); - - probe.should.have.property('meter'); - probe.should.have.property('metric'); - probe.should.have.property('histogram'); - probe.should.have.property('counter'); - done(); - }); - - it('should fork app and receive data from probes', function(done) { - var app = fork(); - - app.on('message', function(pck) { - // Will iterate two times, metric change the value to false - pck.type.should.eql('axm:monitor'); - - pck.data.should.have.properties('req/min', - 'Realtime user', - 'random', - 'Cheerio'); - - if (pck.data.random.value && pck.data.random.agg_type == 'sum' && - pck.data.Cheerio.value.yes == true && pck.data.Cheerio.agg_type == 'avg' && - pck.data.Downloads.value > 1 && pck.data.Downloads.agg_type == 'max') { - app.kill(); - done(); - } - }); - }); - - it('should receive transposed data', function(done) { - var app = fork('/fixtures/transpose.fixture.js'); - var pass = 0; - - app.on('message', function(pck) { - // Will iterate two times, metric change the value to false - pck.type.should.eql('axm:monitor'); - - pck.data.should.have.properties('style_2_docker_config', - 'style_1_docker_config'); - - if (pck.data.style_1_docker_config.value.val == 'new value' && - pck.data.style_2_docker_config.value.val == 'new value') { - app.kill(); - done(); - } - }); - }); - - it('should fork app and receive data', function(done) { - var app = forkHistogram(); - - app.on('message', function(pck) { - pck.type.should.eql('axm:monitor'); - - if (pck.data.mean && pck.data.mean.agg_type == 'avg' && - pck.data.min && pck.data.min.agg_type == 'min' && - pck.data.test && pck.data.test.agg_type == 'sum') { - app.kill(); - done(); - } - }); - }); - - -}) diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/proc-option.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/proc-option.mock.js deleted file mode 100644 index bcdd7794..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/proc-option.mock.js +++ /dev/null @@ -1,8 +0,0 @@ - - -var axm = require('..'); - -axm.action('test:with:options', function(options, reply) { - console.log('CHILD: Action test called from external process'); - reply({ res : 'hello moto', options : options}); -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/proc.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/proc.mock.js deleted file mode 100644 index 3bb99a4d..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/proc.mock.js +++ /dev/null @@ -1,7 +0,0 @@ - -var axm = require('..'); - -axm.action('test:nab', {comment : 'This is a test', display : true}, function(reply) { - console.log('CHILD: Action test called from external process'); - reply({ res : 'hello moto'}); -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/profiling.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/profiling.mocha.js deleted file mode 100644 index 05da3f5b..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/profiling.mocha.js +++ /dev/null @@ -1,47 +0,0 @@ - -var pmx = require('..'); -var Profiling = require('../lib/probes/profiling.js'); -var should = require('should'); -var shelljs = require('shelljs'); - -describe('Profiling', function() { - it('should have right properties', function(done) { - pmx.should.have.property('v8Profiling'); - Profiling.should.have.property('detectV8Profiler'); - Profiling.should.have.property('exposeProfiling'); - Profiling.should.have.property('v8Profiling'); - done(); - }); - - it('should return error as v8-profiler not present', function(done) { - Profiling.detectV8Profiler(function(err, data) { - err.should.not.be.null; - should(data).be.undefined; - done(); - }); - }); - - describe.skip('V8-profiler', function() { - before(function(done) { - shelljs.exec('npm install v8-profiler', function() { - setTimeout(done, 10000); - }); - }); - - after(function(done) { - shelljs.exec('npm uninstall v8-profiler', function() { - done(); - }); - }); - - it('should detect v8 profiler', function(done) { - Profiling.detectV8Profiler(function(err, data) { - console.log(arguments); - err.should.not.be.null; - should(data).be.undefined; - done(); - }); - }); - }); - -}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/transaction.pmx.js b/test/fixtures/module-fixture/node_modules/pmx/test/transaction.pmx.js deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/transaction/app.mock.auto.js b/test/fixtures/module-fixture/node_modules/pmx/test/transaction/app.mock.auto.js deleted file mode 100644 index 50cd2bd3..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/transaction/app.mock.auto.js +++ /dev/null @@ -1,37 +0,0 @@ - -require('../..').init({ - ignore_routes : [/\/socket\.io.*/] -}); - -var express = require('express'); -var app = express(); - -app.get('/', function(req, res) { - res.send(202, {success:true}); -}); - -app.get('/nothing', function(req, res) { - res.send('yes'); -}); - - -app.get('/slow', function(req, res) { - setTimeout(function() { - res.send('yes'); - }, 700); -}); - -app.get('/socket.io/slow', function(req, res) { - setTimeout(function() { - res.send('yes'); - }, 700); -}); - -app.get('/nothing2', function(req, res) { - setTimeout(function() { - res.send('yes'); - }, 1000); -}); - - -app.listen(9007); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/transaction/package.json b/test/fixtures/module-fixture/node_modules/pmx/test/transaction/package.json deleted file mode 100644 index bdc7199e..00000000 --- a/test/fixtures/module-fixture/node_modules/pmx/test/transaction/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "transaction", - "version": "0.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "devDependencies" : { - "express" : "*" - }, - "author": "", - "license": "ISC" -} diff --git a/test/fixtures/module-fixture/scoped-action.js b/test/fixtures/module-fixture/scoped-action.js index 49d844aa..bf988d6d 100644 --- a/test/fixtures/module-fixture/scoped-action.js +++ b/test/fixtures/module-fixture/scoped-action.js @@ -1,5 +1,5 @@ -var pmx = require('pmx'); +var pmx = require('@pm2/io'); var conf = pmx.initModule({ diff --git a/test/fixtures/path-resolution/ecosystem3.config.js b/test/fixtures/path-resolution/ecosystem3.config.js new file mode 100644 index 00000000..d201bfdd --- /dev/null +++ b/test/fixtures/path-resolution/ecosystem3.config.js @@ -0,0 +1,15 @@ +module.exports = { + /** + * Application configuration section + * http://pm2.keymetrics.io/docs/usage/application-declaration/ + */ + apps : [ + { + name : "test-cluster", + script : "./echo.js", + out_file : 'echo-out.log', + error_file : 'echo-err.log', + instances: 4 + } + ] +} diff --git a/test/fixtures/probes.js b/test/fixtures/probes.js index bf2e07ca..28b34677 100644 --- a/test/fixtures/probes.js +++ b/test/fixtures/probes.js @@ -1,6 +1,6 @@ -var pmx = require('pmx'); +var pmx = require('@pm2/io'); var conf = pmx.init(); var http = require('http'); diff --git a/test/fixtures/start-app/ecosystem.config.js b/test/fixtures/start-app/ecosystem.config.js new file mode 100644 index 00000000..2fc0a706 --- /dev/null +++ b/test/fixtures/start-app/ecosystem.config.js @@ -0,0 +1,10 @@ +module.exports = { + apps : [{ + cmd: "node -e 'setTimeout(function() { }, 100000); console.log(process.env.TEST)'", + log: 'test-conf.log', + merge_logs: true, + env: { + TEST: 'test_val' + } + }] +}; diff --git a/test/fixtures/start-app/http.js b/test/fixtures/start-app/http.js new file mode 100644 index 00000000..0dc42cb1 --- /dev/null +++ b/test/fixtures/start-app/http.js @@ -0,0 +1,21 @@ + +var http = require('http'); + +var app = http.createServer(function(req, res) { + res.writeHead(200); + res.end('hey'); +}) + +var listener = app.listen(0, function() { + console.log('Listening on port ' + listener.address().port); +}); + +process.on('message', function(msg) { + if (msg == 'shutdown') { + console.log('Closing all connections...'); + setTimeout(function() { + console.log('Finished closing connections'); + process.exit(0); + }, 100); + } +}); diff --git a/test/fixtures/start-consistency/child.js b/test/fixtures/start-consistency/child.js index b22f8e48..c457878b 100644 --- a/test/fixtures/start-consistency/child.js +++ b/test/fixtures/start-consistency/child.js @@ -1,5 +1,5 @@ -require('pmx').init({ +require('@pm2/io').init({ http : true }); diff --git a/test/interface/aggregator.mocha.js b/test/interface/aggregator.mocha.js deleted file mode 100644 index b90d0d79..00000000 --- a/test/interface/aggregator.mocha.js +++ /dev/null @@ -1,239 +0,0 @@ - -process.env.DEBUG='pm2:aggregator'; -var should = require('should'); -var Aggregator = require('../../lib/Interactor/TransactionAggregator.js'); -var Utility = require('../../lib/Interactor/Utility.js'); -var Plan = require('../helpers/plan.js'); -var TraceFactory = require('./misc/trace_factory.js'); -var TraceMock = require('./misc/trace.json'); -var path = require('path'); -var fs = require('fs'); - -describe('Transactions Aggregator', function() { - var aggregator; - var stackParser; - - it('should instanciate context cache', function() { - var cache = new Utility.Cache({ - miss: function (key) { - try { - var content = fs.readFileSync(path.resolve(key)); - return content.toString().split(/\r?\n/); - } catch (err) { - return undefined; - } - } - }) - - stackParser = new Utility.StackTraceParser({ cache: cache, context: 2}); - }); - - it('should instanciate aggregator', function() { - aggregator = new Aggregator({ stackParser: stackParser}); - }); - - describe('.censorSpans', function() { - var trace = TraceFactory.generateTrace('/yoloswag/swag', 2); - - it('should not fail', function() { - aggregator.censorSpans(null); - }); - - it('should censor span', function() { - should.exist(trace.spans[1].labels.results); - aggregator.censorSpans(trace.spans); - should.not.exist(trace.spans[1].labels.results); - trace.spans[1].labels.cmd.should.containEql('?'); - }); - }); - - describe('.isIdentifier', function() { - it('should be an identifier (api version)', function() { - aggregator.isIdentifier('v1').should.equal(true); - }); - - it('should be an identifier (number)', function() { - aggregator.isIdentifier('123').should.equal(true); - }); - - it('should be an identifier (random str)', function() { - aggregator.isIdentifier('65f4ez656').should.equal(true); - }); - - it('should be an identifier (uuid)', function() { - aggregator.isIdentifier('123e4567-e89b-12d3-a456-426655440000').should.equal(true); - aggregator.isIdentifier('123e4567e89b12d3a456426655440000').should.equal(true); - }); - - it('should be an identifier', function() { - aggregator.isIdentifier('toto-toto-tooa').should.equal(true); - aggregator.isIdentifier('toto@toto.fr').should.equal(true); - aggregator.isIdentifier('toto@toto.fr').should.equal(true); - aggregator.isIdentifier('fontawesome-webfont.eot').should.equal(true); - aggregator.isIdentifier('life_is_just_fantasy').should.equal(true); - aggregator.isIdentifier('OR-IS_THIS-REAL_LIFE').should.equal(true); - }); - - it('should be NOT an identifier', function() { - aggregator.isIdentifier('bucket').should.equal(false); - aggregator.isIdentifier('admin').should.equal(false); - aggregator.isIdentifier('auth').should.equal(false); - aggregator.isIdentifier('users').should.equal(false); - aggregator.isIdentifier('version').should.equal(false); - }); - }); - - describe('.matchPath - aggregate', function() { - var routes = { - 'bucket/6465577': { spans: true } - }; - - it('should not fail', function() { - aggregator.matchPath(); - aggregator.matchPath('/'); - aggregator.matchPath('/', {}); - aggregator.matchPath('/', { - '/' : {} - }); - }); - - it('should match first route', function() { - var match = aggregator.matchPath('bucket/67754', routes); - should.exist(match); - match.should.be.a.String(); - match.should.equal('bucket/*'); - should.exist(routes['bucket/*']) - }); - - it('should NOT match any route', function() { - should.not.exist(aggregator.matchPath('toto/67754', routes)); - }); - - it('should match aggregated route with *', function() { - var match = aggregator.matchPath('bucket/87998', routes); - should.exist(match); - match.should.be.a.String(); - match.should.equal('bucket/*'); - should.exist(routes['bucket/*']) - }); - }); - - describe('merging trace together', function() { - var trace = TraceFactory.generateTrace('yoloswag/swag', 2); - var ROUTES = { - 'yoloswag/swag': {} - }; - - it('should not fail', function() { - aggregator.mergeTrace() - aggregator.mergeTrace(null, trace) - aggregator.mergeTrace({}, trace) - aggregator.mergeTrace({}) - }); - - it('should add a trace', function() { - aggregator.mergeTrace(ROUTES['yoloswag/swag'], trace) - ROUTES['yoloswag/swag'].meta.histogram.getCount().should.be.equal(1); - ROUTES['yoloswag/swag'].variances.length.should.be.equal(1); - ROUTES['yoloswag/swag'].variances[0].spans.length.should.be.equal(3); - }); - - it('should merge with the first variance', function() { - aggregator.mergeTrace(ROUTES['yoloswag/swag'], trace); - ROUTES['yoloswag/swag'].variances.length.should.be.equal(1); - ROUTES['yoloswag/swag'].variances[0].histogram.getCount().should.be.equal(2); - }); - - it('should merge as a new variance with the same route', function () { - var trace2 = TraceFactory.generateTrace('yoloswag/swag', 3) - trace2.spans.forEach(function (span) { - span.min = span.max = span.mean = Math.round(new Date(span.endTime) - new Date(span.startTime)); - }) - aggregator.mergeTrace(ROUTES['yoloswag/swag'], trace2); - ROUTES['yoloswag/swag'].meta.histogram.getCount().should.be.equal(3); - ROUTES['yoloswag/swag'].variances.length.should.be.equal(2); - ROUTES['yoloswag/swag'].variances[0].histogram.getCount().should.be.equal(2); - ROUTES['yoloswag/swag'].variances[1].histogram.getCount().should.be.equal(1); - ROUTES['yoloswag/swag'].variances[1].spans.length.should.be.equal(4); - }); - }); - - describe('.aggregate', function() { - it('should not fail', function() { - var dt = aggregator.aggregate(null); - should(dt).be.false(); - }); - - it('should aggregate', function() { - // Simulate some data - var packet = TraceFactory.generatePacket('yoloswag/swag', 'appname'); - aggregator.aggregate(packet); - packet = TraceFactory.generatePacket('yoloswag/swag', 'appname'); - aggregator.aggregate(packet); - packet = TraceFactory.generatePacket('yoloswag/swag', 'appname'); - aggregator.aggregate(packet); - packet = TraceFactory.generatePacket('sisi/aight', 'appname'); - aggregator.aggregate(packet); - packet = TraceFactory.generatePacket('sisi/aight', 'APP2'); - aggregator.aggregate(packet); - - var agg = aggregator.getAggregation(); - - // should get 2 apps in agg - should.exist(agg['appname']); - should.exist(agg['APP2']); - - // should contain 2 routes for appname - Object.keys(agg['appname'].routes).length.should.eql(2); - should.exist(agg['appname'].process); - agg['appname'].meta.trace_count.should.eql(4); - should.exist(agg['appname'].meta.histogram.percentiles([0.5])[0.5]); - - // should pm_id not taken into account - should.not.exist(agg['appname'].process.pm_id); - }); - }); - - describe('.normalizeAggregation', function() { - it('should get normalized aggregattion', function(done) { - var ret = aggregator.prepareAggregationforShipping(); - should.exist(ret['appname'].process.server); - should.exist(ret['APP2'].process.server); - done(); - }); - }); - - describe('.resetAggregation and .clearData', function() { - it('should get transactions', function() { - var cache = aggregator.getAggregation(); - Object.keys(cache).length.should.eql(2); - }); - - it('should .resetAggregation for "appname" app', function() { - var cache = aggregator.getAggregation(); - - cache['appname'].meta.trace_count.should.eql(4); - Object.keys(cache['appname'].routes).length.should.eql(2); - - aggregator.resetAggregation('appname', {}) - cache = aggregator.getAggregation(); - Object.keys(cache).length.should.eql(2); - - cache['appname'].meta.trace_count.should.eql(0); - Object.keys(cache['appname'].routes).length.should.eql(0); - }); - - it('should .clearData', function() { - var cache = aggregator.getAggregation(); - cache['APP2'].meta.trace_count.should.eql(1); - Object.keys(cache['APP2'].routes).length.should.eql(1); - aggregator.clearData(); - - cache = aggregator.getAggregation(); - cache['APP2'].meta.trace_count.should.eql(0); - Object.keys(cache['APP2'].routes).length.should.eql(0); - }); - - }); - -}); diff --git a/test/interface/bus.fork.spec.mocha.js b/test/interface/bus.fork.spec.mocha.js index 3ed46921..72e35829 100644 --- a/test/interface/bus.fork.spec.mocha.js +++ b/test/interface/bus.fork.spec.mocha.js @@ -1,6 +1,5 @@ var should = require('should'); -var Ipm2 = require('../../lib/Interactor/pm2-interface'); var PM2 = require('../..'); var Plan = require('../helpers/plan.js'); @@ -58,13 +57,12 @@ process.on('uncaughtException', function(e) { describe('PM2 BUS / RPC', function() { var pm2 = new PM2.custom({ - independent : true, cwd : __dirname + '/../fixtures/interface' }); var pm2_bus; after(function(done) { - pm2.destroy(done); + pm2.delete('all', () => done()); }); before(function(done) { @@ -89,7 +87,6 @@ describe('PM2 BUS / RPC', function() { var plan = new Plan(2, done); pm2_bus.on('*', function(event, data) { - console.log(event); if (event == 'process:event') { event.should.eql('process:event'); data.should.have.properties(PROCESS_EVENT); @@ -173,22 +170,6 @@ describe('PM2 BUS / RPC', function() { should(err).be.null(); }); }); - - it('should (transaction:http)', function(done) { - - pm2_bus.on('*', function(event, data) { - if (event == 'http:transaction') { - data.should.have.properties(TRANSACTION_HTTP_EVENT); - data.process.should.have.properties(PROCESS_ARCH); - done(); - } - }); - - pm2.start('./http_transaction.js', {}, function(err, data) { - should(err).be.null(); - }); - }); - }); }); diff --git a/test/interface/bus.spec.mocha.js b/test/interface/bus.spec.mocha.js index 706d5a1e..4db6b9c3 100644 --- a/test/interface/bus.spec.mocha.js +++ b/test/interface/bus.spec.mocha.js @@ -1,6 +1,5 @@ var should = require('should'); -var Ipm2 = require('../../lib/Interactor/pm2-interface'); var PM2 = require('../..'); var Plan = require('../helpers/plan.js'); @@ -60,13 +59,12 @@ process.on('uncaughtException', function(e) { describe('PM2 BUS / RPC', function() { var pm2 = new PM2.custom({ - independent : true, cwd : __dirname + '/../fixtures/interface' }); var pm2_bus; after(function(done) { - pm2.destroy(done); + pm2.delete('all', () => done()); }); before(function(done) { @@ -173,20 +171,6 @@ describe('PM2 BUS / RPC', function() { }); }); - it('should (transaction:http)', function(done) { - pm2_bus.on('*', function(event, data) { - if (event == 'http:transaction') { - data.should.have.properties(TRANSACTION_HTTP_EVENT); - data.process.should.have.properties(PROCESS_ARCH); - done(); - } - }); - - pm2.start('./http_transaction.js', {instances : 1}, function(err, data) { - should(err).be.null(); - }); - }); - }); }); diff --git a/test/interface/cache.mocha.js b/test/interface/cache.mocha.js deleted file mode 100644 index ecd30771..00000000 --- a/test/interface/cache.mocha.js +++ /dev/null @@ -1,86 +0,0 @@ - -var should = require('should'); -var Utility = require('../../lib/Interactor/Utility.js'); -var path = require('path'); -var fs = require('fs'); - -describe('Cache Utility', function() { - var aggregator; - var stackParser; - var cache; - - it('should instanciate context cache', function() { - cache = new Utility.Cache({ - miss: function (key) { - try { - var content = fs.readFileSync(path.resolve(key)); - return content.toString().split(/\r?\n/); - } catch (err) { - return null; - } - } - }) - }); - - it('should get null without key', function() { - should(cache.get()).be.null(); - }); - - it('should get null with unknow value', function() { - should(cache.get('toto')).be.null(); - }); - - it('should get null', function() { - should(cache.get()).be.null(); - }); - - it('should set null', function() { - should(cache.set()).be.false(); - }); - - it('should not set key without value', function() { - should(cache.set('toto')).be.false(); - }); - - it('should set value', function() { - should(cache.set('toto', 'val')).be.true(); - }); - - it('should get value', function() { - should(cache.get('toto')).eql('val'); - }); - - it('should reset', function() { - cache.reset(); - }); - - it('should get null with unknow value', function() { - should(cache.get('toto')).be.null(); - }); - - it('should instanciate context cache with ttl', function() { - cache = new Utility.Cache({ - miss: function (key) { - try { - var content = fs.readFileSync(path.resolve(key)); - return content.toString().split(/\r?\n/); - } catch (err) { - return null; - } - }, - ttl: 1 - }); - }); - - it('should add a key', function () { - should(cache.set('toto', 'yeslife')).be.true(); - }); - - it('should wait one second to see the key disapear', function (done) { - setTimeout(function () { - should(cache.get('toto')).be.null(); - done(); - }, 3000); - }); - -}); diff --git a/test/interface/custom-actions.mocha.js b/test/interface/custom-actions.mocha.js deleted file mode 100644 index 5d6117ec..00000000 --- a/test/interface/custom-actions.mocha.js +++ /dev/null @@ -1,169 +0,0 @@ - - -var PM2 = require('../..'); -var should = require('should'); -var nssocket = require('nssocket'); -var events = require('events'); -var util = require('util'); - -var Cipher = require('../../lib/Interactor/Cipher.js'); -var cst = require('../../constants.js'); -var Plan = require('../helpers/plan.js'); -var Interactor = require('../../lib/Interactor/InteractorDaemonizer.js'); - -var server = new events.EventEmitter(); -var pm2_bus; - -process.env.NODE_ENV = 'local_test'; - -var meta_connect = { - secret_key : 'osef', - public_key : 'osef', - machine_name : 'osef' -}; - -/** - * Mock server receiving data - * @method forkInteractor - * @return CallExpression - */ -function createMockServer(cb) { - var server = nssocket.createServer(function(_socket) { - - console.log('Got new connection in Mock server'); - - server.on('cmd', function(data) { - console.log('Sending command %j', data); - _socket.send(data._type, data); - }); - - _socket.data('*', function(data) { - this.event.forEach(function(ev) { - server.emit(ev, data); - }); - }); - - }); - - server.on('error', function(e) { - throw new Error(e); - }); - - server.on('listening', function() { - cb(null, server); - }); - - server.listen(4322, '0.0.0.0'); -} - -function startSomeApps(pm2, cb) { - pm2.start({ - script : './events/custom_action.js', - name : 'custom-action' - }, cb); -} - -describe('Custom actions', function() { - var server; - var interactor; - var pm2 = new PM2.custom({ - independent : true, - cwd : __dirname + '/../fixtures', - secret_key : 'osef', - public_key : 'osef', - machine_name : 'osef', - daemon_mode: true - });; - - before(function(done) { - createMockServer(function(err, _server) { - server = _server; - pm2.connect(function(err) { - startSomeApps(pm2, function() { - pm2.launchBus(function(err, bus) { - pm2_bus = bus; - setTimeout(done, 500); - }); - }); - }); - }); - }); - - after(function(done) { - server.close(); - pm2.destroy(done); - }); - - it('should send ask, receive ask:rep and identify agent', function(done) { - server.once('ask:rep', function(pck) { - var data = Cipher.decipherMessage(pck.data, meta_connect.secret_key); - data.machine_name.should.eql(meta_connect.machine_name); - done(); - }); - - server.emit('cmd', { _type : 'ask' }); - }); - - /** - * PM2 agent is now identified - */ - - it('should trigger remote action successfully', function(done) { - var plan = new Plan(2, done); - - var success = function(pck) { - plan.ok(true); - server.removeListener('trigger:action:failure', failure); - }; - - var failure = function(pck) { - console.log(pck); - plan.ok(false); - }; - - server.once('trigger:action:success', success); - - server.once('trigger:action:failure', failure); - - pm2_bus.on('axm:reply', function(pck) { - pck.data.return.success.should.be.true; - pck.data.return.subobj.a.should.eql('b'); - plan.ok(true); - }); - - server.emit('cmd', { - _type : 'trigger:action', - process_id : 0, - action_name : 'refresh:db' - }); - }); - - it('should trigger failure action', function(done) { - var plan = new Plan(1, done); - - var success = function(pck) { - plan.ok(false); - }; - - var failure = function(pck) { - server.removeListener('trigger:action:success', success); - plan.ok(true); - }; - - server.once('trigger:action:success', success); - - server.once('trigger:action:failure', failure); - - pm2_bus.on('axm:reply', function(pck) { - plan.ok(false); - }); - - server.emit('cmd', { - _type : 'trigger:action', - process_id : 0, - action_name : 'unknown:action' - }); - }); - - -}); diff --git a/test/interface/exception.e2e.mocha.js b/test/interface/exception.e2e.mocha.js deleted file mode 100644 index 65ef7f78..00000000 --- a/test/interface/exception.e2e.mocha.js +++ /dev/null @@ -1,88 +0,0 @@ - -process.env.NODE_ENV = 'local_test'; -process.env.KM_URL_REFRESH_RATE = 1000; - -var axon = require('pm2-axon'); -var PM2 = require('../..'); -var should = require('should'); -var sub; - -function listen(cb) { - sub = axon.socket('sub'); - sub.bind(8080, cb); -} - -function listenRev(cb) { - var listener_server = require('nssocket').createServer(function(_socket) { - }); - - listener_server.listen(4322, '0.0.0.0', function() { - console.log('Reverse interact online'); - cb(); - }); -} - -describe('Programmatically test interactor', function() { - var pm2; - - before(function(done) { - listen(function() { - listenRev(function() { - - pm2 = new PM2.custom({ - public_key : 'xxx', - secret_key : 'yyy', - cwd : __dirname + '/../fixtures/interface' - }); - - pm2.connect(function() { - pm2.kill(function() { - done(); - }); - }); - }); - }); - }); - - after(function(done) { - pm2.kill(done); - }); - - describe('application testing', function() { - it('should start test application', function(done) { - sub.once('message', function(data) { - var packet = JSON.parse(data); - packet.data['process:event'].length.should.eql(2) - done(); - }); - - pm2.start({ - script : 'process_exception_with_logs.js', - name : 'API' - }, function(err, data) { - if (err) done(err); - //console.log(arguments); - }); - }); - - it('should get transaction trace via interactor output', function(done) { - (function callAgain() { - sub.once('message', function(data) { - var packet = JSON.parse(data); - - if (packet.data['process:exception']) { - packet.data['process:exception'][0].data.callsite.should.containEql('process_exception_with_logs.js:7'); - packet.data['process:exception'][0].data.context.should.containEql('console.log'); - should.exist(packet.data['process:exception'][0].data.last_logs); - //console.log - done(); - } - else callAgain(); - }); - })() - - pm2.trigger('API', 'exception'); - }); - - }); -}); diff --git a/test/interface/filter.mocha.js b/test/interface/filter.mocha.js deleted file mode 100644 index 9e48d7cf..00000000 --- a/test/interface/filter.mocha.js +++ /dev/null @@ -1,23 +0,0 @@ - -var Filter = require('../../lib/Interactor/Filter.js'); -var should = require('should'); -var os = require('os'); - -describe('Filter Utility', function() { - it('should .machineSnapshot works as expected', function() { - var filtered = Filter.machineSnapshot([], { - REVERSE_INTERACT : true, - PM2_VERSION : '2.2.0' - }); - filtered.server.should.have.properties(['loadavg', 'total_mem', 'free_mem']); - should(filtered.server.total_mem).eql(os.totalmem()); - should(filtered.server.arch).eql(os.arch()); - }); - - it('should .monitoring works as expected', function() { - var filtered = Filter.monitoring([], {}); - filtered.should.have.properties(['loadavg', 'total_mem', 'free_mem', 'processes']); - filtered.total_mem.should.eql(os.totalmem()); - }); - -}); diff --git a/test/interface/interactor.connect.mocha.js b/test/interface/interactor.connect.mocha.js deleted file mode 100644 index f9974038..00000000 --- a/test/interface/interactor.connect.mocha.js +++ /dev/null @@ -1,227 +0,0 @@ - -process.env.NODE_ENV = 'local_test'; -process.env.TRAVIS = true; -process.env.DEBUG='interface:*'; - -var PM2 = require('../..'); -var should = require('should'); -var nssocket = require('nssocket'); -var events = require('events'); -var util = require('util'); -var axon = require('pm2-axon'); -var sock = axon.socket('sub'); - -var pub_sock = sock.bind(8080); -var Cipher = require('../../lib/Interactor/Cipher.js'); -var cst = require('../../constants.js'); -var Plan = require('../helpers/plan.js'); -var Configuration = require('../../lib/Configuration.js'); -var Helpers = require('../helpers/apps.js'); - -var server = null; -var listener_server; - -var _socket_list = []; - -var meta_connect = { - secret_key : 'osef', - public_key : 'osef', - machine_name : 'osef' -}; - -/** - * Mock server receiving data - * @method forkInteractor - * @return CallExpression - */ -function createMockServer(cb) { - - pub_sock.server.on('connection', function(socket) { - _socket_list.push(socket); - console.log('Got new connection on mock server'); - }); - - server = new events.EventEmitter(); - - listener_server = nssocket.createServer(function(_socket) { - server.on('cmd', function(data) { - _socket.send(data._type, data); - }); - - _socket.data('*', function(data) { - this.event.forEach(function(ev) { - server.emit(ev, data); - }); - }); - - }); - - listener_server.on('error', function(e) { - throw new Error(e); - }); - - listener_server.on('listening', function() { - cb(null, server); - }); - - listener_server.listen(4322, '0.0.0.0'); -} - -describe('Interactor testing', function() { - var server; - var interactor; - var pm2_bus; - - var pm2 = new PM2.custom({ - independent : true, - cwd : __dirname + '/../fixtures', - secret_key : 'osef', - public_key : 'osef', - machine_name : 'osef', - daemon_mode: true - }); - - before(function(done) { - Configuration.unset('pm2:passwd', function(err, data) { - createMockServer(function(err, _server) { - server = _server; - - pm2.connect(function(err, data) { - Helpers.startSomeApps(pm2, function(err, dt) { - done(); - }); - }); - }); - }); - }); - - after(function(done) { - listener_server.close(); - pm2.destroy(done); - }); - - describe('Interactor methods', function() { - it('should display info', function(done) { - pm2.interactInfos(function(err, meta) { - meta.should.have.properties([ - 'machine_name', - 'public_key', - 'secret_key', - 'socket_path', - 'pm2_home_monitored' - ]) - - meta.pm2_home_monitored.should.eql(pm2.pm2_home); - done(); - }); - }); - }); - - describe('Input command / Output data checks', function() { - it('should send ask, receive ask:rep and identify agent', function(done) { - server.once('ask:rep', function(pck) { - var data = Cipher.decipherMessage(pck.data, meta_connect.secret_key); - data.machine_name.should.eql(meta_connect.machine_name); - done(); - }); - - server.emit('cmd', { _type : 'ask' }); - }); - - it('should get status via PushInteractor and PM2 should be statused as not protected', function(done) { - sock.once('message', function(data) { - var dt = JSON.parse(data); - - dt.public_key.should.eql('osef'); - - var status = dt.data.status.data; - var procs = status.process; - var server = status.server; - - procs.length.should.eql(1); - - var meta = dt.data.status; - should.exists(dt.sent_at); - meta.protected.should.be.false(); - meta.rev_con.should.be.true(); - meta.server_name.should.eql('osef'); - done(); - }); - - it('should get status via PushInteractor and PM2 should be statused as not protected', function(done) { - sock.once('message', function(data) { - var dt = JSON.parse(data); - - dt.public_key.should.eql('osef'); - - var status = dt.data.status.data; - var procs = status.process; - var server = status.server; - - procs.length.should.eql(1); - - var meta = dt.data.status; - - should.exists(dt.sent_at); - meta.protected.should.be.false(); - meta.rev_con.should.be.true(); - meta.server_name.should.eql('osef'); - - done(); - }); - }); - }); - - describe('General behaviors', function() { - it('should receive event application restart', function(done) { - - sock.once('message', function(data) { - var dt = JSON.parse(data); - var monitoring = dt.data.monitoring; - var process_event = dt.data['process:event']; - - //console.log(JSON.stringify(process_event, '', 2)); - done(); - }); - - pm2.restart('all', function() {}); - }); - }); - - describe('PM2 password checking', function() { - it('should set a password', function(done) { - pm2.set('pm2:passwd', 'testpass', function(err, data) { - should.not.exists(err); - setTimeout(done, 1000); - }); - }); - - it('should interactor be notified of password set', function(done) { - sock.once('message', function(data) { - var dt = JSON.parse(data); - // Has switched to true - dt.data.status.protected.should.be.true(); - done(); - }); - }); - }); - - }); - - describe('Offline', function() { - it('should handle offline gracefully', function(done) { - _socket_list.forEach(function(socket, i) { - _socket_list[i].destroy(); - }); - - sock.closeSockets(); - - pub_sock.server.close(function() { - console.log('Server closed'); - }); - setTimeout(done, 500); - }); - }); - - -}); diff --git a/test/interface/interactor.daemonizer.mocha.js b/test/interface/interactor.daemonizer.mocha.js deleted file mode 100644 index 35dba90c..00000000 --- a/test/interface/interactor.daemonizer.mocha.js +++ /dev/null @@ -1,164 +0,0 @@ - -var should = require('should'); -var fs = require('fs'); -var os = require('os'); -var default_conf = require('../../constants'); -var interactorDaemonizer = require('../../lib/Interactor/InteractorDaemonizer'); -var json5 = require('../../lib/tools/json5.js'); - -describe('Daemonizer interactor', function() { - before(function(done) { - delete process.env.PM2_SECRET_KEY; - delete process.env.PM2_PUBLIC_KEY; - delete process.env.KEYMETRICS_NODE; - - try { - fs.unlinkSync(default_conf.INTERACTION_CONF); - } catch(e) {} - done(); - }); - - describe('General tests', function() { - it('should try get set keys but get error because nothing exposed', function(done) { - interactorDaemonizer.getOrSetConf(default_conf, null, function(err, data) { - err.should.not.be.null(); - done(); - }); - }); - }); - - describe('Default behavior', function() { - after(function() { - fs.unlinkSync(default_conf.INTERACTION_CONF); - }); - - it('should set right node by default', function(done) { - interactorDaemonizer.getOrSetConf(default_conf, { - secret_key : 'xxx', - public_key : 'yyy', - machine_name : null, - info_node : null - }, function(err, data) { - should(err).be.null(); - data.info_node.should.eql(default_conf.KEYMETRICS_ROOT_URL); - - var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF)); - interaction_conf.info_node.should.eql(default_conf.KEYMETRICS_ROOT_URL); - - return done(); - }); - }); - - it('should retrieve data from file without env variable', function(done) { - interactorDaemonizer.getOrSetConf(default_conf, null, function(err, data) { - should(err).be.null(); - data.secret_key.should.eql('xxx'); - data.public_key.should.eql('yyy'); - data.info_node.should.eql(default_conf.KEYMETRICS_ROOT_URL); - - var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF)); - interaction_conf.info_node.should.eql(default_conf.KEYMETRICS_ROOT_URL); - - return done(); - }); - }); - - it('should set new keys and write in configuration file', function(done) { - interactorDaemonizer.getOrSetConf(default_conf, { - secret_key : 'XXXS2', - public_key : 'XXXP2', - info_node : 'test2.url' - }, function(err, data) { - should(err).be.null(); - data.secret_key.should.eql('XXXS2'); - data.public_key.should.eql('XXXP2'); - data.info_node.should.eql('test2.url'); - - var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF)); - interaction_conf.secret_key.should.eql('XXXS2'); - interaction_conf.public_key.should.eql('XXXP2'); - interaction_conf.info_node.should.eql('test2.url'); - - should.exist(interaction_conf.version_management.active); - should(interaction_conf.version_management.password).be.null(); - - interaction_conf.machine_name.should.startWith(os.hostname()); - return done(); - }); - }); - - it('should retrieve data from file without env variable', function(done) { - interactorDaemonizer.getOrSetConf(default_conf, null, function(err, data) { - should(err).be.null(); - data.secret_key.should.eql('XXXS2'); - data.public_key.should.eql('XXXP2'); - data.info_node.should.eql('test2.url'); - return done(); - }); - }); - - it('should retrieve the same data with null fields', function(done) { - interactorDaemonizer.getOrSetConf(default_conf, { - secret_key : null, - public_key : null, - machine_name : null, - info_node : null - }, function(err, data) { - should(err).be.null(); - data.secret_key.should.eql('XXXS2'); - data.public_key.should.eql('XXXP2'); - data.info_node.should.eql('test2.url'); - return done(); - }); - }); - - }); - - describe('Environment variable override', function() { - before(function() { - process.env.PM2_SECRET_KEY = 'XXXS'; - process.env.PM2_PUBLIC_KEY = 'XXXP'; - process.env.KEYMETRICS_NODE = 'test.url'; - }); - - after(function() { - delete process.env.PM2_SECRET_KEY; - delete process.env.PM2_PUBLIC_KEY; - delete process.env.KEYMETRICS_NODE; - }); - - it('should work with env variables and create file', function(done) { - - interactorDaemonizer.getOrSetConf(default_conf, { - secret_key : null, - public_key : null, - machine_name : null, - info_node : null - }, function(err, data) { - should(err).be.null(); - data.secret_key.should.eql('XXXS'); - data.public_key.should.eql('XXXP'); - data.info_node.should.eql('test.url'); - - should.exist(data.version_management.active); - should(data.version_management.password).be.null(); - try { - fs.statSync(default_conf.INTERACTION_CONF); - } catch(e) { - return done(e); - } - return done(); - }); - }); - - it('should retrieve data from file without env variable', function(done) { - interactorDaemonizer.getOrSetConf(default_conf, null, function(err, data) { - should(err).be.null(); - data.secret_key.should.eql('XXXS'); - data.public_key.should.eql('XXXP'); - data.info_node.should.eql('test.url'); - return done(); - }); - }); - }); -}); diff --git a/test/interface/misc/trace.json b/test/interface/misc/trace.json deleted file mode 100644 index 962497e6..00000000 --- a/test/interface/misc/trace.json +++ /dev/null @@ -1,167 +0,0 @@ -[{ - "projectId": 0, - "traceId": "43fd648369374111b4ee56565c7cecb2", - "spans": [ - { - "name": "/api/bucket", - "parentSpanId": "0", - "spanId": 36, - "kind": "RPC_SERVER", - "labels": { - "http/method": "OPTIONS", - "http/url": "http://cl1.km.io/api/bucket", - "http/source/ip": "::ffff:127.0.0.1", - "http/status_code": "204" - }, - "startTime": "2016-11-13T16:55:51.677Z", - "endTime": "2016-11-13T16:55:51.680Z" - }, - { - "name": "redis-set", - "parentSpanId": 36, - "spanId": 37, - "kind": "RPC_CLIENT", - "labels": { - "command": "set", - "arguments": "[\"sess:rYrMvzCAwhlXrIOp8swjKxvIYh1UN5EF\",\"{\\\"cookie\\\":{\\\"originalMaxAge\\\":null,\\\"expires\\\":null,\\\"secure\\\":false,\\\"httpOnly\\\":true,\\\"domain\\\":\\\".km.io\\\",\\\"path\\\":\\\"/\\\"},\\\"passport\\\":{}}\",\"EX\",120]", - "result": "OK" - }, - "startTime": "2016-11-13T16:55:51.678Z", - "endTime": "2016-11-13T16:55:51.678Z" - } - ] -},{ - "projectId": 0, - "traceId": "43887a4ff20c44b990c8ec6540440690", - "spans": [ - { - "name": "/api/bucket", - "parentSpanId": "0", - "spanId": 38, - "kind": "RPC_SERVER", - "labels": { - "http/method": "GET", - "http/url": "http://cl1.km.io/api/bucket", - "http/source/ip": "::ffff:127.0.0.1", - "express/request.route.path": "/", - "http/status_code": "304" - }, - "startTime": "2016-11-13T16:55:51.779Z", - "endTime": "2016-11-13T16:55:51.829Z" - }, - { - "name": "redis-get", - "parentSpanId": 38, - "spanId": 39, - "kind": "RPC_CLIENT", - "labels": { - "command": "get", - "arguments": "[\"sess:As-xHUEPTRSvi9lrK8j3gWO_mxnl7Llk\"]", - "result": "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"secure\":false,\"httpOnly\":true,\"domain\":\".km.io\",\"path\":\"/\"},\"passport\":{}}" - }, - "startTime": "2016-11-13T16:55:51.781Z", - "endTime": "2016-11-13T16:55:51.782Z" - }, - { - "name": "mongo-cursor", - "parentSpanId": 38, - "spanId": 40, - "kind": "RPC_CLIENT", - "labels": { - "db": "devdb6.tokens", - "cmd": "{\"find\":\"devdb6.tokens\",\"limit\":-1,\"skip\":0,\"query\":{\"type\":\"access_token\",\"token\":\"u009vf00u9cdyf8yyzzhpkxrrhq07euhfd3p106mfgbbqq3icfd9katq7oz2oe6bwqxhuzhl9uzkquq8mgnz0k80oryr7i4ym2litx317uakb8dcm9y5irqi7l2f5e81\"},\"slaveOk\":false,\"batchSize\":1}", - "results": "{_id:{_bsontype:ObjectID,id:X(ÂÓð†\f#\u0002í\\},token:u009vf00u9cdyf8yyzzhpkxrrhq07euhfd3p106mfgbbqq3icfd9katq7oz2oe6bwqxhuz..." - }, - "startTime": "2016-11-13T16:55:51.783Z", - "endTime": "2016-11-13T16:55:51.786Z" - }, - { - "name": "mongo-cursor", - "parentSpanId": 38, - "spanId": 41, - "kind": "RPC_CLIENT", - "labels": { - "db": "devdb6.users", - "cmd": "{\"find\":\"devdb6.users\",\"limit\":0,\"skip\":0,\"query\":{\"_id\":{\"$in\":[\"57f26987af49d57472b3d104\"]}},\"slaveOk\":false}", - "results": "{_id:{_bsontype:ObjectID,id:Wòi‡¯IÕtr³Ñ\u0004},short_id:k3l0x,auth_token:iuub9912b8ruh6h7dgoi,username:alexandre,email:alex..." - }, - "startTime": "2016-11-13T16:55:51.787Z", - "endTime": "2016-11-13T16:55:51.789Z" - }, - { - "name": "mongo-cursor", - "parentSpanId": 38, - "spanId": 42, - "kind": "RPC_CLIENT", - "labels": { - "db": "devdb6.clients", - "cmd": "{\"find\":\"devdb6.clients\",\"limit\":0,\"skip\":0,\"query\":{\"_id\":{\"$in\":[\"57fe6588ffc3dd335c78b7e0\"]}},\"slaveOk\":false}", - "results": "{_id:{_bsontype:ObjectID,id:WþeÿÃÝ3\\x·à},name:Keymetrics Dashboard,clientID:5413907556,clientSecret:2393878333,autho..." - }, - "startTime": "2016-11-13T16:55:51.788Z", - "endTime": "2016-11-13T16:55:51.794Z" - }, - { - "name": "mongo-cursor", - "parentSpanId": 38, - "spanId": 43, - "kind": "RPC_CLIENT", - "labels": { - "db": "devdb6.users", - "cmd": "{\"find\":\"devdb6.users\",\"limit\":0,\"skip\":0,\"query\":{\"_id\":{\"$in\":[\"57f26987af49d57472b3d104\"]}},\"slaveOk\":false}" - }, - "startTime": "2016-11-13T16:55:51.789Z", - "endTime": "2016-11-13T16:55:51.790Z" - }, - { - "name": "mongo-cursor", - "parentSpanId": 38, - "spanId": 44, - "kind": "RPC_CLIENT", - "labels": { - "db": "devdb6.buckets", - "cmd": "{\"find\":\"devdb6.buckets\",\"limit\":0,\"skip\":0,\"query\":{\"_id\":{\"$in\":[\"57f269abaf49d57472b3d109\",\"581e81e03f91d96e46eb0f65\",\"581e831316d4875e482dd174\",\"581e836716d4875e482dd175\",\"581e83f316d4875e482dd176\",\"581e840c4a4ade7c49389b1e\",\"581e843e8c08e8b9494604a3\",\"581e84a20f148e364b5331f3\",\"581e859e0b5bce564d4b246e\",\"581e86a7eb307d47504fe25f\",\"581e86f9eb307d47504fe260\"]}},\"slaveOk\":false}", - "results": "{_id:{_bsontype:ObjectID,id:Wòi«¯IÕtr³Ñ\t},secret_id:mcp6snmnon1asmt,public_id:mxfiwlvzracelhl,node_cache:{_id:{_bsonty..." - }, - "startTime": "2016-11-13T16:55:51.792Z", - "endTime": "2016-11-13T16:55:51.794Z" - }, - { - "name": "mongo-cursor", - "parentSpanId": 38, - "spanId": 45, - "kind": "RPC_CLIENT", - "labels": { - "db": "devdb6.buckets", - "cmd": "{\"find\":\"devdb6.buckets\",\"limit\":0,\"skip\":0,\"query\":{\"_id\":{\"$in\":[\"57f269abaf49d57472b3d109\",\"581e81e03f91d96e46eb0f65\",\"581e831316d4875e482dd174\",\"581e836716d4875e482dd175\",\"581e83f316d4875e482dd176\",\"581e840c4a4ade7c49389b1e\",\"581e843e8c08e8b9494604a3\",\"581e84a20f148e364b5331f3\",\"581e859e0b5bce564d4b246e\",\"581e86a7eb307d47504fe25f\",\"581e86f9eb307d47504fe260\"]}},\"slaveOk\":false}" - }, - "startTime": "2016-11-13T16:55:51.794Z", - "endTime": "2016-11-13T16:55:51.794Z" - }, - { - "name": "mongo-cursor", - "parentSpanId": 38, - "spanId": 46, - "kind": "RPC_CLIENT", - "labels": { - "db": "devdb6.clients", - "cmd": "{\"find\":\"devdb6.clients\",\"limit\":0,\"skip\":0,\"query\":{\"_id\":{\"$in\":[\"57fe6588ffc3dd335c78b7e0\"]}},\"slaveOk\":false}" - }, - "startTime": "2016-11-13T16:55:51.794Z", - "endTime": "2016-11-13T16:55:51.795Z" - }, - { - "name": "redis-expire", - "parentSpanId": 38, - "spanId": 47, - "kind": "RPC_CLIENT", - "labels": { - "command": "expire", - "arguments": "[\"sess:As-xHUEPTRSvi9lrK8j3gWO_mxnl7Llk\",120]", - "result": "1" - }, - "startTime": "2016-11-13T16:55:51.827Z", - "endTime": "2016-11-13T16:55:51.829Z" - } - ] -}] diff --git a/test/interface/misc/trace_factory.js b/test/interface/misc/trace_factory.js deleted file mode 100644 index b2ee85b5..00000000 --- a/test/interface/misc/trace_factory.js +++ /dev/null @@ -1,146 +0,0 @@ - -var crypto = require('crypto'); -var moment = require('moment'); -var path = require('path'); -var WEBSITE_ROOT = 'http://toto.com'; -var spanId = 0; - -var random_routes = [ - '/api/bucket', - '/api/bucket/users', - '/api/bucket/chameau', - '/backo/testo' -]; - -function getRandomInt(min, max) { - min = Math.ceil(min); - return Math.floor(Math.random() * (Math.floor(max) - min)) + min; -} - -/** - * Generate Trace - * @param {String} route_path route name, default to random route name - * @param {Integer} db_query_nb number of spans, default to random number (0-10) - */ -function generateTrace(route_path, db_query_nb) { - if (!db_query_nb) - db_query_nb = getRandomInt(2, 5); - if (!route_path) - route_path = random_routes[getRandomInt(0, random_routes.length - 1)]; - var parentSpanId = ++spanId; - - var timeframe = []; - - var trace = { - projectId : 0, - traceId : crypto.randomBytes(32).toString('hex'), - spans : [{ - "name": route_path, - "parentSpanId": "0", - "spanId": parentSpanId, - "kind": "RPC_SERVER", - "labels": { - "http/method": "GET", - "http/path": route_path, - "http/url": WEBSITE_ROOT + route_path, - "http/source/ip": "::ffff:127.0.0.1", - "http/status_code": "204" - }, - "startTime": moment().subtract(db_query_nb + 1, 'seconds').toISOString(), - "endTime": moment().toISOString() - }] - }; - - for (var i = 0; i < db_query_nb; i++) { - trace.spans[i + 1] = { - "name": "mongo-cursor", - "parentSpanId": parentSpanId, - "spanId": ++spanId, - "kind": "RPC_CLIENT", - "labels": { - "db": "devdb6.tokens", - "cmd": "{\"find\":\"devdb6.tokens\",\"limit\":-1,\"skip\":0,\"query\":{\"type\":\"access_token\",\"token\":\"u00i7l2f5e81\"},\"slaveOk\":false,\"batchSize\":1}", - "results": "{_id:{_bsontype:ObjectID,id:X(Â\\},token:u009vf00..." - }, - "startTime": moment().subtract(db_query_nb - i + 1, 'seconds').toISOString(), - "endTime": moment().subtract(db_query_nb - i, 'seconds').toISOString() - }; - } - - return trace; -} - -exports.generateTrace = generateTrace; - -// Generate the same kind of data sent by pm2 -exports.generatePacket = function(route, app_name) { - return { - data : generateTrace(route), - process : { - name : app_name, - pm_id : 4, - server : 'test', - rev : 'xxx' - } - }; -}; - -exports.staticTrace = { - "spans": [ - { - "name":"/auth/signin", - "parentSpanId":"0", - "spanId":9, - "kind":"RPC_SERVER", - "labels":{ - "http/method":"POST", - "http/path":"/auth/signin", - "express/request.route.path":"/signin", - "http/status_code":"200" - }, - "startTime":"2016-11-11T14:03:18.449Z", - "endTime":"2016-11-11T14:03:18.792Z" - }, - { - "name":"mysql-query", - "parentSpanId": 9, - "spanId": 10, - "kind":"RPC_CLIENT", - "labels": { - "sql":"SELECT * FROM users WHERE mail = ?", - "values":"XXXXX", - "result":"XXXX" - }, - "startTime":"2016-11-11T14:03:18.558Z", - "endTime":"2016-11-11T14:03:18.568Z" - } - ] -}; - -exports.stacktrace = { - stack_frame: [ - { - file_name: 'events.js', - line_number: 10, - column_number: 10, - method_name: '' - }, - { - file_name: 'node_modules/express.js', - line_number: 10, - column_number: 10, - method_name: '' - }, - { - file_name: path.resolve(__dirname, 'trace_factory.js'), - line_number: 10, - column_number: 10, - method_name: '' - } - ] -} - - -if (require.main === module) { - console.log(generateTrace()); -} diff --git a/test/interface/monitor.mocha.js b/test/interface/monitor.mocha.js deleted file mode 100644 index 9baedf9c..00000000 --- a/test/interface/monitor.mocha.js +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-env mocha */ - -process.env.NODE_ENV='test'; - -'use strict'; - -var pm2 = require('../../index.js'); -var async = require('async'); -var assert = require('assert'); -var path = require('path'); -var PushInteractor = require('../../lib/Interactor/PushInteractor.js'); - -describe('unmonitor process', function () { - before(function (done) { - pm2.connect(function (err) { - if (err) return done(err); - pm2.delete('all', function () { - return done(); - }); - }); - }); - - after(function (done) { - pm2.delete('all', function (_) { - return done(); - }); - }); - - it('should start some processes', function (done) { - async.times(3, function (n, next) { - pm2.start({ - script: path.resolve(__dirname, '../fixtures/empty.js'), - name: 'test-' + n - }, next); - }, done); - }); - - it('should have 3 processes started', function (done) { - pm2.list(function (err, processes) { - assert(err === null); - assert(processes.length === 3); - return done(err); - }); - }); - - it('should start the push interactor', function (done) { - PushInteractor.start({ - url: 'toto', - conf: { - ipm2: require('../../lib/Interactor/pm2-interface.js')() - } - }); - return setTimeout(done, 100); - }); - - it('should return three processes with interactor', function (done) { - PushInteractor.preparePacket(function (err, data) { - if (err) return done(err); - - assert(data.process.length === 3); - return done(); - }); - }); - - it('should run the unmonitor command', function (done) { - pm2.monitorState('unmonitor', '0', done); - }); - - it('should return two processes with interactor', function (done) { - PushInteractor.preparePacket(function (err, data) { - if (err) return done(err); - - assert(data.process.length === 2); - return done(); - }); - }); - - it('should run the unmonitor command', function (done) { - pm2.monitorState('monitor', '0', done); - }); - - it('should return three processes with interactor', function (done) { - PushInteractor.preparePacket(function (err, data) { - if (err) return done(err); - - assert(data.process.length === 3); - return done(); - }); - }); -}); diff --git a/test/interface/password.mocha.js b/test/interface/password.mocha.js deleted file mode 100644 index 68772fd5..00000000 --- a/test/interface/password.mocha.js +++ /dev/null @@ -1,20 +0,0 @@ - -var Password = require('../../lib/Interactor/Password.js'); -var should = require('should'); - -describe('Password test', function() { - var crypted = ''; - - it('should crypt a password', function() { - crypted = Password.generate('testpass'); - }); - - it('should fail with wrong password', function() { - Password.verify('testpasds', crypted).should.be.false; - }); - - it('should success with right password', function() { - Password.verify('testpass', crypted).should.be.true; - }); - -}); diff --git a/test/interface/pm2.link.check.mocha.js b/test/interface/pm2.link.check.mocha.js deleted file mode 100644 index 6bc24e6b..00000000 --- a/test/interface/pm2.link.check.mocha.js +++ /dev/null @@ -1,86 +0,0 @@ -process.env.NODE_ENV = 'local_test'; -process.env.TRAVIS = true; - -var PM2 = require('../..'); -var should = require('should'); - -describe('PM2 link variable checks', function() { - var server; - this.timeout(5000); - - describe('km_link false', function() { - var pm2 = new PM2.custom({ - cwd : __dirname + '/../fixtures' - }); - - before(function(done) { - pm2.connect(function(err, data) { - done(); - }); - }); - - after(function(done) { - pm2.kill(done); - }); - - it('should start an app and app km_link to false', function(done) { - pm2.start({ - trace : true, - script : 'http.js' - }, function(err) { - done(); - }) - }); - - it('should have km_link to false', function(done) { - // Wait for process initialization - setTimeout(function() { - pm2.list(function(err, dt) { - dt[0].pm2_env.km_link.should.be.false(); - dt[0].pm2_env.axm_options.transactions.should.be.false(); - done(); - }); - }, 500); - }); - }); - - describe('km_link true', function() { - var pm2; - - before(function(done) { - pm2 = new PM2.custom({ - cwd : __dirname + '/../fixtures', - secret_key : 'osef', - public_key : 'osef', - machine_name : 'osef', - daemon_mode: true - }); - - pm2.connect(done); - }); - - after(function(done) { - pm2.kill(done); - }); - - it('should start an app and app km_link to false', function(done) { - pm2.start({ - script : 'http.js', - trace : true - }, done) - }); - - it('should have km_link to false', function(done) { - // Wait for process initialization - setTimeout(function() { - pm2.list(function(err, dt) { - dt[0].pm2_env.km_link.should.be.true(); - //dt[0].pm2_env.axm_options.transactions.should.be.true(); - dt[0].pm2_env.axm_options.tracing_enabled.should.be.true(); - done(); - }); - }, 1000); - }); - }); - -}); diff --git a/test/interface/push_interactor.mocha.js b/test/interface/push_interactor.mocha.js deleted file mode 100644 index 032b3a97..00000000 --- a/test/interface/push_interactor.mocha.js +++ /dev/null @@ -1,78 +0,0 @@ - -process.env.DEBUG='interface:push-interactor'; -process.env.NODE_ENV = 'local_test'; -process.env.PM2_PUBLIC_KEY = 'xxxx'; -process.env.PM2_SECRET_KEY = 'yyyy'; -process.env.PM2_REVERSE_INTERACT = true; -process.env.PM2_MACHINE_NAME = 'xmachine'; -process.env.KM_URL_REFRESH_RATE = 1000; - -var InterfaceD = require('../../lib/Interactor/Daemon.js'); -var Helpers = require('../helpers/apps.js'); -var axon = require('pm2-axon'); - -var pm2; - -var sock; - -function listen(cb) { - sock = axon.socket('sub'); - sock.bind(8080, cb); -} - -function listenRev(cb) { - var listener_server = require('nssocket').createServer(function(_socket) { - }); - - listener_server.listen(4322, '0.0.0.0', cb); -} - -describe('Programmatically test interactor', function() { - before(function(done) { - Helpers.forkPM2(function(err, _pm2) { - listen(function() { - listenRev(function() { - pm2 = _pm2; - done(); - }); - }); - }); - }); - - after(function(done) { - pm2.on('exit', done); - pm2.kill(); - }); - - it('should start Daemon', function(done) { - InterfaceD.start(); - setTimeout(done, 2000); - }); - - it('should receive a message', function(done) { - sock.once('message', function(data) { - data = JSON.parse(data); - done(); - }); - }); - - it('should still receive messages', function(done) { - sock.once('message', function(data) { - done(); - }); - }); - - it('should simulate server restart', function(done) { - sock.close(done); - }); - - it('should recreate connection', function(done) { - listen(done); - }); - - it('should still receive messages', function(done) { - sock.once('message', function(data) { - done(); - }); - }); -}); diff --git a/test/interface/remote.mocha.js b/test/interface/remote.mocha.js deleted file mode 100644 index d1ded5a1..00000000 --- a/test/interface/remote.mocha.js +++ /dev/null @@ -1,249 +0,0 @@ - -process.env.NODE_ENV = 'local_test'; - -var PM2 = require('../..'); -var should = require('should'); -var nssocket = require('nssocket'); -var events = require('events'); -var util = require('util'); -var Cipher = require('../../lib/Interactor/Cipher.js'); -var cst = require('../../constants.js'); - -var send_cmd = new events.EventEmitter(); -var meta_connect = { - secret_key : 'test-secret-key', - public_key : 'test-public-key', - machine_name : 'test-machine-name' -}; - -function createMockServer(cb) { - var server = nssocket.createServer(function(_socket) { - - console.log('Got new connection in Mock server'); - - send_cmd.on('cmd', function(data) { - if (process.env.DEBUG) - console.log('Sending command %j', data); - _socket.send(data._type, data); - }); - - _socket.data('*', function(data) { - this.event.forEach(function(ev) { - send_cmd.emit(ev, data); - }); - }); - - }); - - server.on('error', function(e) { - throw new Error(e); - }); - - server.on('listening', function() { - cb(null, server); - }); - - server.listen(4322, '0.0.0.0'); -} - -function startSomeApps(pm2, cb) { - pm2.start('./child.js', {instances : 4, name : 'child'}, cb); -} - -describe('REMOTE PM2 ACTIONS', function() { - var server; - var interactor; - var pm2 = new PM2.custom({ - independent : true, - cwd : __dirname + '/../fixtures', - secret_key : 'test-secret-key', - public_key : 'test-public-key', - machine_name : 'test-machine-name', - daemon_mode: true - });; - - after(function(done) { - server.close(); - pm2.destroy(done); - }); - - before(function(done) { - createMockServer(function(err, _server) { - console.log('Mock server created'); - server = _server; - pm2.connect(function(err, _pm2) { - startSomeApps(pm2, function() { - done(); - }); - }); - }); - }); - - it('should send ask, receive ask:rep and identify agent', function(done) { - send_cmd.once('ask:rep', function(pck) { - var data = Cipher.decipherMessage(pck.data, meta_connect.secret_key); - data.machine_name.should.eql(meta_connect.machine_name); - done(); - }); - - send_cmd.emit('cmd', { _type : 'ask' }); - }); - - /** - * PM2 agent is now identified - */ - it('should act on PM2', function(done) { - send_cmd.once('trigger:pm2:result', function(pck) { - if (pck.ret.data.length > 0) - done(); - else - done(new Error('wrong data rcvied')); - }); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:action', - method_name : 'restart', - parameters : {name : 'child' } - }); - }); - - it('should act on PM2 but handle failure', function(done) { - send_cmd.once('trigger:pm2:result', function(pck) { - // Error is present telling process does not exists - pck.ret.err.should.not.be.null(); - done(); - }); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:action', - method_name : 'restart', - parameters : {name : 'UNKNOWN APP' } - }); - }); - - it('should RELOAD', function(done) { - send_cmd.once('trigger:pm2:result', function(pck) { - /** - * Once remote command is finished... - */ - - should(pck.ret.err).be.null(); - - pm2.list(function(err, ret) { - ret.forEach(function(proc) { - proc.pm2_env.restart_time.should.eql(2); - }); - }); - - done(); - }); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:action', - method_name : 'reload', - parameters : {name : 'child' } - }); - }); - - it('should gracefulRELOAD', function(done) { - send_cmd.once('trigger:pm2:result', function(pck) { - /** - * Once remote command is finished... - */ - - should(pck.ret.err).be.null(); - - pm2.list(function(err, ret) { - ret.forEach(function(proc) { - proc.pm2_env.restart_time.should.eql(3); - }); - }); - - done(); - }); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:action', - method_name : 'gracefulReload', - parameters : {name : 'child' } - }); - }); - - it('should RESET metadata', function(done) { - send_cmd.once('trigger:pm2:result', function(pck) { - /** - * Once remote command is finished... - */ - should(pck.ret.err).be.null(); - - pm2.list(function(err, ret) { - ret.forEach(function(proc) { - proc.pm2_env.restart_time.should.eql(0); - }); - }); - - done(); - }); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:action', - method_name : 'reset', - parameters : {name : 'child' } - }); - }); - - it('should delete all processes', function(done) { - pm2.delete('all', {}, function() { - startSomeApps(pm2, function() { - pm2.list(function(err, ret) { - ret.forEach(function(proc) { - proc.pm2_env.restart_time.should.eql(0); - }); - done(); - }); - }); - }); - }); - - it('should test .remote', function(done) { - pm2.remote('restart', { - name : 'child' - }, function(err, procs) { - - pm2.list(function(err, ret) { - ret.forEach(function(proc) { - proc.pm2_env.restart_time.should.eql(1); - }); - done(); - }); - }); - }); - - it('should test .remote and handle failure', function(done) { - pm2.remote('restart', { - name : 'UNKNOWN_NAME' - }, function(err, procs) { - pm2.list(function(err, ret) { - ret.forEach(function(proc) { - proc.pm2_env.restart_time.should.eql(1); - }); - done(); - }); - }); - }); - - it('should test .remote #2', function(done) { - pm2.remote('reload', { - name : 'child' - }, function(err, procs) { - - pm2.list(function(err, ret) { - ret.forEach(function(proc) { - proc.pm2_env.restart_time.should.eql(2); - }); - done(); - }); - }); - }); - -}); diff --git a/test/interface/request.mocha.js b/test/interface/request.mocha.js deleted file mode 100644 index eaeb9d0e..00000000 --- a/test/interface/request.mocha.js +++ /dev/null @@ -1,91 +0,0 @@ - -process.env.DEBUG="interface:*"; - -var should = require('should'); -var assert = require('assert'); -var HttpRequest = require('../../lib/Interactor/HttpRequest.js'); - -var PORT = 8080; - -function mockIrritableServer(cb) { - var http = require('http'); - var url = require('url'); - - function handleRequest(req, res) { - var uri = url.parse(req.url).pathname; - - if (uri == '/api/node/verifyPM2') { - // res.writeHead(505, {"Content-Type": "text/json"}); - // return res.end(new Buffer(50).fill('h')); - // } - // console.log(uri); - return false; - } - if (uri == '/api/misc/pm2_version') { - res.writeHead(505); - return res.end(); - } - } - - //Create a server - var server = http.createServer(handleRequest); - - //Lets start our server - server.listen(PORT, function(err){ - if (err) console.error(err); - cb(null, server); - }); -} - -describe('Http requests', function() { - var _server = null; - - before(function(done) { - mockIrritableServer(function(err, server) { - _server = server; - done(); - }); - }); - - after(function(done) { - _server.close(done); - }); - - describe('POST', function() { - it('should post to 404 URL', function(done) { - HttpRequest.post({ - port : 9999, - url : 'http://keymetrics.io/NOTHING', - data : { no : 'thing' } - }, function(err, data) { - assert(err); - assert(err.code == 'ENOTFOUND'); - assert(data == null); - done(); - }) - }); - - it('should timeout after 7secs', function(done) { - HttpRequest.post({ - port : PORT, - url : '127.0.0.1', - data : { no : 'thing' } - }, function(err, data) { - assert(err); - assert(err.code == 'ECONNRESET'); - assert(data == null); - done(); - }); - }); - - }); - - // @todo: more behavioral tests (reverse interactor failcheck) - - // @todo: do more tests when doing changeUrls - it.skip('should change urls (forcing reconnection)', function(done) { - InterfaceD.changeUrls('app.km.io', 'app.km.io:4322'); - setTimeout(done, 2000); - }); - -}); diff --git a/test/interface/scoped_pm2_actions.mocha.js b/test/interface/scoped_pm2_actions.mocha.js deleted file mode 100644 index ecf837cc..00000000 --- a/test/interface/scoped_pm2_actions.mocha.js +++ /dev/null @@ -1,258 +0,0 @@ - -var PM2 = require('../..'); -var should = require('should'); -var nssocket = require('nssocket'); -var events = require('events'); -var util = require('util'); -var Cipher = require('../../lib/Interactor/Cipher.js'); -var cst = require('../../constants.js'); -var Plan = require('../helpers/plan.js'); -var Configuration = require('../../lib/Configuration.js'); -var Helpers = require('../helpers/apps.js'); -var Interactor = require('../../lib/Interactor/InteractorDaemonizer.js'); -var gl_interactor_process; - -var send_cmd = new events.EventEmitter(); - -process.env.NODE_ENV = 'local_test'; - -var meta_connect = { - secret_key : 'test-secret-key', - public_key : 'test-public-key', - machine_name : 'test-machine-name' -}; - -/** - * Description - * @method forkInteractor - * @return CallExpression - */ -function forkInteractor(cb) { - Interactor.launchAndInteract(meta_connect, function(err, data, interactor_process) { - gl_interactor_process = interactor_process; - cb(); - }); -} - -/** - * Mock server receiving data - * @method forkInteractor - * @return CallExpression - */ -function createMockServer(cb) { - var server = nssocket.createServer(function(_socket) { - - send_cmd.on('cmd', function(data) { - if (process.env.DEBUG) - console.log('Sending command %j', data); - _socket.send(data._type, data); - }); - - _socket.data('*', function(data) { - this.event.forEach(function(ev) { - send_cmd.emit(ev, data); - }); - }); - - }); - - server.on('error', function(e) { - throw new Error(e); - }); - - server.on('listening', function() { - cb(null, server); - }); - - server.listen(4322, '0.0.0.0'); -} - -function startSomeApps(cb) { - pm2.start('./child.js', {instances : 1, name : 'child'}, cb); -} - -var pm2 = new PM2.custom({ - independent : true, - cwd : __dirname + '/../fixtures', - secret_key : 'test-secret-key', - public_key : 'test-public-key', - machine_name : 'test-machine-name', - daemon_mode: true -}); - -describe('SCOPED PM2 ACTIONS', function() { - var server; - var interactor; - - after(function(done) { - server.close(); - pm2.destroy(done); - }); - - before(function(done) { - createMockServer(function(err, _server) { - server = _server; - pm2.connect(function() { - startSomeApps(function(err) { - gl_interactor_process = pm2.Client.interactor_process; - // @todo: would be nice to know when an app is ready - // @priotity: minor - setTimeout(done, 1500); - }); - }); - }); - }); - - it('should send ask, receive ask:rep and identify agent', function(done) { - send_cmd.once('ask:rep', function(pck) { - var data = Cipher.decipherMessage(pck.data, meta_connect.secret_key); - data.machine_name.should.eql(meta_connect.machine_name); - done(); - }); - - send_cmd.emit('cmd', { _type : 'ask' }); - }); - - /** - * PM2 agent is now identified - */ - describe('Test non auth remote commands', function() { - before(function(done) { - Configuration.unset('pm2:passwd', function(err, data) { - should.not.exists(err); - done(); - }); - }); - - it('should restart command via scoped pm2 action (no pass needed)', function(done) { - var good = false; - var plan = new Plan(2, function() { - gl_interactor_process.removeListener('message', actionCheck); - good = true; - done(); - }); - - function actionCheck(pck) { - if (good) return false; - if (pck.event == 'pm2:scoped:stream' && pck.data.out === 'Action restart received') - return plan.ok(true); - if (pck.event == 'pm2:scoped:end') - return plan.ok(true); - if (pck.event == 'pm2:scoped:error') - return plan.ok(false, pck); - return false; - } - - gl_interactor_process.on('message', actionCheck); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:scoped:action', - action_name : 'restart', - uuid : '1234', - options : { args : ['child'] } - }); - - }); - - }); - - describe('Password verification', function() { - - before(function(done) { - Configuration.unset('pm2:passwd', function(err, data) { - should.not.exists(err); - done(); - }); - }); - - it('should error when call an action that is password protected', function(done) { - function actionCheck(pck) { - if (pck.event == 'pm2:scoped:error' && pck.data.out.indexOf('Missing password') > -1) { - gl_interactor_process.removeListener('message', actionCheck); - done(); - } - }; - - gl_interactor_process.on('message', actionCheck); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:scoped:action', - action_name : 'install', - uuid : '5678', - options : { args : ['child'] } - }); - }); - - it('should fail when password passed but no pm2 password configured', function(done) { - function actionCheck(pck) { - if (pck.event == 'pm2:scoped:error' && pck.data.out.indexOf('Password at PM2') > -1) { - gl_interactor_process.removeListener('message', actionCheck); - done(); - } - }; - - gl_interactor_process.on('message', actionCheck); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:scoped:action', - action_name : 'install', - uuid : '5678', - password : 'random-pass', - options : { args : ['pm2-module'] } - }); - }); - - it('should set a password', function(done) { - pm2.set('pm2:passwd', 'testpass', function(err, data) { - should.not.exists(err); - done(); - }); - }); - - it('should fail when wrong password', function(done) { - function actionCheck(pck) { - if (pck.event == 'pm2:scoped:error' && pck.data.out.indexOf('Password does not match') > -1) { - gl_interactor_process.removeListener('message', actionCheck); - setTimeout(done, 100); - } - }; - - gl_interactor_process.on('message', actionCheck); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:scoped:action', - action_name : 'install', - uuid : '5678', - password : 'random-pass', - options : { args : ['pm2-module'] } - }); - }); - - it('should work when good password passed', function(done) { - function actionCheck(pck) { - if (pck.event === 'pm2:scoped:end') { - gl_interactor_process.removeListener('message', actionCheck); - done(); - } - if (pck.event === 'pm2:scoped:error') { - gl_interactor_process.removeListener('message', actionCheck); - done('{ERROR} Wrong password!' + JSON.stringify(pck)); - } - }; - - gl_interactor_process.on('message', actionCheck); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:scoped:action', - action_name : 'ping', - uuid : '5678', - password : 'testpass', - options : {} - }); - }); - - - }); - - -}); diff --git a/test/interface/stacktrace.mocha.js b/test/interface/stacktrace.mocha.js deleted file mode 100644 index 630f844b..00000000 --- a/test/interface/stacktrace.mocha.js +++ /dev/null @@ -1,157 +0,0 @@ - -var should = require('should'); -var Aggregator = require('../../lib/Interactor/TransactionAggregator.js'); -var Utility = require('../../lib/Interactor/Utility.js'); -var TraceFactory = require('./misc/trace_factory.js'); -var path = require('path'); -var fs = require('fs'); -var assert = require('assert'); - -describe('StackTrace Utility', function() { - var aggregator; - var stackParser; - - it('should instanciate context cache', function() { - var cache = new Utility.Cache({ - miss: function (key) { - try { - var content = fs.readFileSync(path.resolve(key)); - return content.toString().split(/\r?\n/); - } catch (err) { - return undefined; - } - } - }) - - stackParser = new Utility.StackTraceParser({ cache: cache, context: 2}); - }); - - it('should instanciate aggregator', function() { - aggregator = new Aggregator({ stackParser: stackParser}); - }); - - describe('.parse', function() { - it('should parse stacktrace and get context', function(done) { - var obj = [{ - labels: { - stacktrace: JSON.stringify(TraceFactory.stacktrace) - } - }]; - - aggregator.parseStacktrace(obj); - obj[0].labels['source/file'].indexOf('test/interface/misc/trace_factory.js:10').should.be.above(0); - should(obj[0].labels['source/context']).eql("var random_routes = [\n '/api/bucket',\n>>'/api/bucket/users',\n '/api/bucket/chameau',\n '/backo/testo'"); - done(); - }); - - it('should handle malformated stacktraces', function() { - aggregator.parseStacktrace([{ - labels: { - stacktrace: JSON.stringify({ - stack_frame: [{ - line_number: 10, - column_number: 10, - method_name: '' - }, { - file_name: 'node_modules/express.js', - column_number: 10, - method_name: '' - }, { - file_name: path.resolve(__dirname, 'trace_factory.js'), - line_number: 10, - column_number: 10, - method_name: '' - }] - }) - } - }]); - }); - - it('should handle malformated stacktrace v1', function() { - aggregator.parseStacktrace([{ - labels: { - stacktrace: JSON.stringify({ - stack_frame: [{ - file_name: 'events.js' - },{ - file_name: 'node_modules/express.js' - },{ - file_name: path.resolve(__dirname, 'trace_factory.js') - }] - }) - } - }]); - }); - - it('should handle malformated stacktrace v2', function() { - aggregator.parseStacktrace([{ - labels: { - stacktrace: JSON.stringify({ - stack_frame: [{ - file_name: 'events.js', - column_number: 10, - method_name: '' - },{ - file_name: 'node_modules/express.js', - column_number: 10, - method_name: '' - },{ - file_name: path.resolve(__dirname, 'trace_factory.js'), - line_number: 10, - column_number: 10, - method_name: '' - }] - }) - } - }]); - }); - - it('should handle malformated stacktrace v3', function() { - aggregator.parseStacktrace([{ - labels: {} - }]); - }); - - it('should handle malformated stacktrace v4', function() { - aggregator.parseStacktrace([{ - }]); - }); - - it('should handle malformated stacktrace v5', function() { - aggregator.parseStacktrace([]); - }); - - it('should handle malformated stacktrace v5', function() { - aggregator.parseStacktrace(); - }); - - }); - - describe('.attachContext', function () { - it('should extract context from stackframes', function () { - var error = stackParser.attachContext({ - stackframes: [ - { - file_name: '/toto/tmp/lol', - line_number: 10 - } - ] - }); - assert(error !== undefined); - assert(error.stackframes === undefined); - assert(error.callsite !== undefined); - assert(error.callsite.indexOf('/toto/tmp/lol') >= 0); - }); - - it('should extract context from the stack string', function () { - var error = new Error(); - // stack is lazy so we need to load it - error.stack = error.stack; - error = stackParser.attachContext(error); - assert(error !== undefined); - assert(error.stackframes === undefined); - assert(error.callsite.indexOf(__filename) >= 0); - assert(error.context.indexOf('var error = new Error()') >= 0); - }); - }); -}); diff --git a/test/parallel.js b/test/parallel.js new file mode 100644 index 00000000..838520e2 --- /dev/null +++ b/test/parallel.js @@ -0,0 +1,111 @@ + +const async = require('async') +const fs = require('fs') +const exec = require('child_process').exec +const path = require('path') +const chalk = require('chalk') +const Table = require('cli-table-redemption'); + +const testFolder = './test/e2e/' + +const CONCURRENT_TEST = 3 +const DOCKER_IMAGE_NAME = 'pm2-test' + +var timings = {}; + +function run(cmd, cb) { + exec(cmd, function(err, stdout, stderr) { + if (err) { + console.log(`Retrying ${cmd}`) + return exec(cmd, function(err, stdout, stderr) { + if (err) return cb(stdout.split('\n')); + return cb(null); + }) + } + return cb(null) + }) +} + +function buildContainer(cb) { + exec(`docker build -t ${DOCKER_IMAGE_NAME} -f test/Dockerfile .`, cb) +} + +function listAllTest(cb) { + var test_suite = [] + + fs.readdir(testFolder, (err, folders) => { + async.forEachLimit(folders, 4, (folder, next) => { + var fold = path.join(testFolder, folder) + fs.readdir(fold, (err, files) => { + if (err) return next() + files.forEach((file) => { + test_suite.push(path.join(fold, file)) + }) + next() + }) + }, function() { + launchTestSuite(test_suite, cb) + }) + }) +} + +function launchTestSuite(files, cb) { + async.forEachLimit(files, CONCURRENT_TEST, function(file, next) { + var cmd = `docker run -v ${path.resolve(__dirname, '..')}:/var/pm2 ${DOCKER_IMAGE_NAME} bash ${file}` + + console.log(chalk.bold(`Running test ${file}`)) + timings[file] = new Date().getTime() + + run(cmd, function(err) { + if (err) { + // Display Error + console.error(chalk.bold.red(`${'='.repeat(25)} Test File ${file} has failed ${'='.repeat(25)}`)) + console.error(chalk.bold('Output (stderr):')) + err.forEach(function(line) { + console.error(line) + }) + console.error(chalk.bold.red(`${'='.repeat(80)}`)) + return next(err) + } + + timings[file] = new Date().getTime() - timings[file] + + console.log(chalk.bold.green(`✓ Test ${file} success`)) + return next(); + }) + }, (err) => { + if (err) { + console.log('Test Suite has failed') + cb(err) + } + console.log('Test Suite passed succesfully') + cb() + }) +} + +buildContainer(function(err) { + if (err) { + console.error(err) + process.exit(1) + } + console.log(`Container ${DOCKER_IMAGE_NAME} has been built`) + + return listAllTest(function(err) { + + var table = new Table({ + head: ['Test', 'Duration'], + style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true} + }) + + Object.keys(timings).forEach(function(test) { + table.push([test, timings[test]]) + }) + + console.log(table.toString()); + + if (err) { + return console.error(chalk.bold.red('Test suite failed')) + } + console.log(chalk.bold.blue('Test suite succeeded')) + }) +}) diff --git a/test/parallel_programmatic_tests.sh b/test/parallel_programmatic_tests.sh deleted file mode 100755 index d4e40e9a..00000000 --- a/test/parallel_programmatic_tests.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -export NODE_ENV='test' - -function fail { - echo -e "######## \033[31m ✘ $1\033[0m" -} - -function success { - echo -e "\033[32m------------> ✔ $1\033[0m" -} - -function spec { - [ $? -eq 0 ] || fail "$1" - success "$1" -} - -pkill -f PM2 - -cd test/ - -parallel --gnu --keep-order --joblog joblog --halt now,fail=1 -j+0 < programmatic_commands.txt -spec "Should text have passed" -cat joblog - -# possible to pass --tmux diff --git a/test/pm2_behavior_tests.sh b/test/pm2_behavior_tests.sh deleted file mode 100644 index 6b1fa630..00000000 --- a/test/pm2_behavior_tests.sh +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/bash/include.sh" - -# Abort script at first error -set -e -# Display all commands executed -set -o verbose - -# if [ $TRAVIS ] -# then -# export DEBUG="*" -# fi - -bash ./test/bash/cli-actions-1.sh -spec "CLI basic test" -bash ./test/bash/cli-actions-2.sh -spec "Second hard cli tests" - -# Power feature -bash ./test/bash/pm2-dev.sh -spec "pm2-dev" -bash ./test/bash/pm2-runtime.sh -spec "pm2-runtime" -bash ./test/bash/options-via-env.sh -spec "set option via environment" -bash ./test/bash/startup.sh -spec "upstart startup test" -bash ./test/bash/dump.sh -spec "dump test" -bash ./test/bash/resurrect.sh -spec "resurrect test" -# bash ./test/bash/docker.sh -# spec "Docker tests" -bash ./test/bash/nvm-node-version.sh -spec "NVM node version setting" -bash ./test/bash/mjs.sh -spec "Test import syntax" - -bash ./test/bash/cron-system.sh -spec "Cron system tests" -bash ./test/bash/promise.sh -spec "Promise warning message tests" - -# bash ./test/bash/log-timestamp.sh -# spec "timestamp prefix of pm2.log" -bash ./test/bash/watch.sh -spec "watch system tests" -bash ./test/bash/versioning-cmd.sh -spec "versioning system tests" -bash ./test/bash/args.sh -spec "check arguments passing" -bash ./test/bash/smart-start.sh -spec "smart start test" -bash ./test/bash/multiparam.sh -spec "Multiparam process management" -bash ./test/bash/json-file.sh -spec "JSON file test" -bash ./test/bash/yaml-configuration.sh -spec "YAML configuration support" -bash ./test/bash/piped-config.sh -spec "Piped JSON file test" -bash ./test/bash/extra-lang.sh -spec "Various programming languages checks (Python, PHP)" -bash ./test/bash/json-reload.sh -spec "JSON reload test" -bash ./test/bash/homogen-json-action.sh -spec "Homogen json actions" -bash ./test/bash/app-config-update.sh -spec "CLI/JSON argument reload" -bash ./test/bash/start-consistency.sh -spec "Consistency between a JSON an CLI start" -bash ./test/bash/harmony.sh -spec "Harmony test" -bash ./test/bash/log-custom.sh -spec "Custom log timestamp" -bash ./test/bash/reload.sh -spec "Reload" -bash ./test/bash/right-exit-code.sh -spec "Verification exit code" -bash ./test/bash/log-reload.sh -spec "Log reload" -bash ./test/bash/gracefulReload.sh -spec "gracefulReload system 1" -bash ./test/bash/gracefulReload2.sh -spec "gracefulReload system 2" -bash ./test/bash/gracefulReload3.sh -spec "gracefulReload system 3" -bash ./test/bash/misc.sh -spec "MISC features" -bash ./test/bash/fork.sh -spec "Fork system working" -bash ./test/bash/get-set.sh -spec "Configuration system working" -bash ./test/bash/infinite-loop.sh -spec "Infinite loop stop" -bash ./test/bash/env-refresh.sh -spec "Environment refresh on restart" -bash ./test/bash/reset.sh -spec "Reset meta" -bash ./test/bash/startOrX.sh -spec "startOrX commands" -bash ./test/bash/binary.sh -spec "binary test" -bash ./test/bash/log-entire.sh -spec "merge stdout && stderr" -bash ./test/bash/module.sh -spec "module system" -bash ./test/bash/module-safeguard.sh -spec "module safeguard system (--safe)" -bash ./test/bash/vizion.sh -spec "vizion features (versioning control)" -bash ./test/bash/wrapped-fork.sh -spec "wrapped fork" -bash ./test/bash/app-configuration.sh -spec "App configuration" -bash ./test/bash/interpreter.sh -spec "Javascript transpilers tests" -bash ./test/bash/source_map.sh -spec "Source map resolution on exception" -bash ./test/bash/inside-pm2.sh -spec "Starting a process inside a PM2 process" -bash ./test/bash/js-configuration.sh -spec "js configuration support" -bash ./test/bash/wait-ready-event.sh -spec "Wait for application ready event" -bash ./test/bash/serve.sh -spec "pm2 serve CLI method" -bash ./test/bash/monit.sh -spec "km selective monitoring " -bash ./test/bash/log-null.sh -spec "Logging path set to null" -bash ./test/bash/log-json.sh -spec "Logging directly to file in json" -bash ./test/bash/operate-regex.sh -spec "Operate process that match regex" -bash ./test/bash/daemon-paths-override.sh -spec "Override daemon configuration paths" -bash ./test/bash/increment-var.sh -spec "Increment env variables" -bash ./test/bash/instance-number.sh -spec "Negative instance number spawn one worker" -bash ./test/bash/attach.sh -spec "pm2 attach method" - -# Issues related -bash ./test/bash/issues/2337.sh - -$pm2 kill diff --git a/test/pm2_programmatic_tests.sh b/test/pm2_programmatic_tests.sh deleted file mode 100644 index 695c622f..00000000 --- a/test/pm2_programmatic_tests.sh +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env bash - -alias mocha='../node_modules/mocha/bin/mocha' -pm2="`type -P node` `pwd`/bin/pm2" - -# Abort script at first error -set -e -# Display all commands executed -set -o verbose - -function fail { - echo -e "######## \033[31m ✘ $1\033[0m" - exit 1 -} - -function success { - echo -e "\033[32m------------> ✔ $1\033[0m" -} - -function spec { - [ $? -eq 0 ] || fail "$1" - $pm2 uninstall all - $pm2 link delete - $pm2 kill - success "$1" -} - -$pm2 uninstall all - -# if [ $TRAVIS ] -# then -# export DEBUG="pm2:*" -# fi -cd test/programmatic - -mocha --opts ./mocha.opts ./god.mocha.js -spec "God test" - -mocha --opts ./mocha.opts ./programmatic.js -spec "Programmatic test" - -mocha --opts ./mocha.opts ./containerizer.mocha.js -spec "Dockerfile parser test" - -mocha --opts ./mocha.opts ./api.mocha.js -spec "API tests" -mocha --opts ./mocha.opts ./path_resolution.mocha.js -spec "API tests" -mocha --opts ./mocha.opts ./lazy_api.mocha.js -spec "API tests" -mocha --opts ./mocha.opts ./reload-locker.mocha.js -spec "Reload locker tests" - -mocha --opts ./mocha.opts ./api.backward.compatibility.mocha.js -spec "API Backward compatibility tests" -mocha --opts ./mocha.opts ./custom_action.mocha.js -spec "Custom Actions tests" - -mocha --opts ./mocha.opts ./logs.js -spec "Logs test" -mocha --opts ./mocha.opts ./watcher.js -spec "Watcher" -mocha --opts ./mocha.opts ./max_memory_limit.js -spec "Max memory tests" -# mocha --opts ./mocha.opts ./module_configuration.mocha.js -# spec "Max memory tests" -mocha --opts ./mocha.opts ./cluster.mocha.js -spec "Cluster tests" -mocha --opts ./mocha.opts ./graceful.mocha.js -spec "Graceful tests" -mocha --opts ./mocha.opts ./inside.mocha.js -spec "Inside pm2 call tests" -mocha --opts ./mocha.opts ./misc_commands.js -spec "MISC tests" -mocha --opts ./mocha.opts ./signals.js -spec "SIGINT signal interception + delay customization" -mocha --opts ./mocha.opts ./send_data_process.mocha.js -spec "Send data to a process" -mocha --opts ./mocha.opts ./modules.mocha.js -spec "Module API testing" -mocha --opts ./mocha.opts ./module_retrocompat.mocha.js -spec "Module retrocompatibility system" - -mocha --opts ./mocha.opts ./json_validation.mocha.js -spec "JSON validation test" -mocha --opts ./mocha.opts ./env_switching.js -spec "JSON environment switching on JSON restart with --env" -mocha --opts ./mocha.opts ./configuration.mocha.js -spec "Configuration system working" - -# -# Interface testing -# -cd ../interface - -echo $PM2_HOME - -mocha --opts ./mocha.opts ./exception.e2e.mocha.js -spec "E2E exception system checking" -mocha --opts ./mocha.opts ./interactor.connect.mocha.js -spec "Interactor test #1 with password setting" -mocha --opts ./mocha.opts ./interactor.daemonizer.mocha.js -spec "Remote interactor keys save verification" -mocha --opts ./mocha.opts ./scoped_pm2_actions.mocha.js -spec "Scoped PM2 Remote interactions test" -mocha --opts ./mocha.opts ./remote.mocha.js -spec "Remote interactions test" -mocha --opts ./mocha.opts ./password.mocha.js -spec "Password library checking" -mocha --opts ./mocha.opts ./custom-actions.mocha.js -spec "Custom actions test" -mocha --opts ./mocha.opts ./bus.spec.mocha.js -spec "Protocol communication test" -mocha --opts ./mocha.opts ./bus.fork.spec.mocha.js -spec "Protocol communication test" -mocha --opts ./mocha.opts ./request.mocha.js -spec "Protocol communication test" -mocha --opts ./mocha.opts ./aggregator.mocha.js -spec "Transaction trace aggregator test" -mocha --opts ./mocha.opts ./stacktrace.mocha.js -spec "Stacktrace Utility" -mocha --opts ./mocha.opts ./cache.mocha.js -spec "Cache Utility" -mocha --opts ./mocha.opts ./filter.mocha.js -spec "Filter Utility" -mocha --opts ./mocha.opts ./utility.mocha.js -spec "PM2 Utility" -mocha --opts ./mocha.opts ./pm2.link.check.mocha.js -spec "Transaction option enablement" -mocha --opts ./mocha.opts ./monitor.mocha.js -spec "Monitor / Unmonitor commands" diff --git a/test/programmatic/configuration.mocha.js b/test/programmatic/configuration.mocha.js index 0a2bc86a..7ff040d7 100644 --- a/test/programmatic/configuration.mocha.js +++ b/test/programmatic/configuration.mocha.js @@ -1,9 +1,14 @@ -var should = require('should'); +var should = require('should'); +var PM2 = require('../..'); var Configuration = require('../../lib/Configuration.js'); describe('Configuration via SET / GET tests', function() { + before(function(done) { + PM2.list(done); + }); + it('should set a value', function(done) { Configuration.set('key1', 'val1', function(err, data) { should.not.exists(err); diff --git a/test/programmatic/custom_action.mocha.js b/test/programmatic/custom_action.mocha.js index 9de735f8..6df8aaf3 100644 --- a/test/programmatic/custom_action.mocha.js +++ b/test/programmatic/custom_action.mocha.js @@ -5,16 +5,12 @@ var pm2 = require('../..'); var should = require('should'); describe('Custom actions via CLI/API', function() { - after(function(done) { - pm2.kill(done); + before(function(done) { + pm2.delete('all', function() { done() }); }); - before(function(done) { - pm2.connect(function() { - pm2.kill(function() { - pm2.connect(done); - }) - }); + after(function(done) { + pm2.delete('all', function() { done() }); }); it('should start custom action script', function(done) { diff --git a/test/programmatic/god.mocha.js b/test/programmatic/god.mocha.js index 887a15f3..5c22cd53 100644 --- a/test/programmatic/god.mocha.js +++ b/test/programmatic/god.mocha.js @@ -256,4 +256,52 @@ describe('God', function() { }); }); + it('should get monitor data', function(done) { + var f = require('child_process').fork('../fixtures/echo.js') + + var processes = [ + // stopped status + { + pm2_env: {status: cst.STOPPED_STATUS} + }, + // axm pid + { + pm2_env: { + status: cst.ONLINE_STATUS, axm_options: {pid: process.pid} + } + }, + // axm pid is NaN + { + pm2_env: { + status: cst.ONLINE_STATUS, axm_options: {pid: 'notanumber'} + } + }, + { + pm2_env: { + status: cst.ONLINE_STATUS + }, + pid: f.pid + } + ] + + // mock + var g = { + getFormatedProcesses: function() { + return processes + } + } + + require('../../lib/God/ActionMethods.js')(g) + + g.getMonitorData({}, function(err, procs) { + should(err).be.null(); + procs.length.should.be.equal(processes.length); + procs[0].monit.should.be.deepEqual({memory: 0, cpu: 0}); + procs[1].monit.memory.should.be.greaterThan(0); + procs[2].monit.should.be.deepEqual({memory: 0, cpu: 0}); + procs[3].monit.memory.should.be.greaterThan(0); + f.kill() + done() + }) + }); }); diff --git a/test/programmatic/id.mocha.js b/test/programmatic/id.mocha.js new file mode 100644 index 00000000..1d879859 --- /dev/null +++ b/test/programmatic/id.mocha.js @@ -0,0 +1,82 @@ + +process.chdir(__dirname); + +var PM2 = require('../..'); +var should = require('should'); +var assert = require('assert') + +describe('Unique ID verification', function() { + describe('when starting', function() { + var _id = null + + before(function(done) { + PM2.delete('all', function() { done() }); + }); + + after(function(done) { + PM2.delete('all', function() { done() }); + }); + + it('should start a script', function(done) { + PM2.start('../fixtures/child.js', function(err) { + should(err).be.null(); + PM2.list(function(err, list) { + should(err).be.null(); + assert(list.length > 0) + assert(typeof list[0].pm2_env.unique_id === 'string') + _id = list[0].pm2_env.unique_id + done(); + }); + }); + }); + + it('should stop app by id', function(done) { + PM2.stop(0, done); + }); + + it('should restart and not changed unique id', function(done) { + PM2.restart(0, (err) => { + should(err).be.null(); + PM2.list(function(err, list) { + should(err).be.null(); + assert(list.length > 0) + assert(typeof list[0].pm2_env.unique_id === 'string') + assert( list[0].pm2_env.unique_id === _id) + done(); + }); + }); + }); + + + it('should generate another unique id for new process', function(done) { + PM2.start('./../fixtures/child.js', { name: 'toto' }, function(err) { + assert(!err); + PM2.list(function(err, list) { + should(err).be.null(); + assert(list.length === 2) + assert(typeof list[0].pm2_env.unique_id === 'string') + assert(typeof list[1].pm2_env.unique_id === 'string') + assert(list[0].pm2_env.unique_id !== typeof list[1].pm2_env.unique_id) + done(); + }); + }); + }); + + it('should duplicate a process and have a new id', function(done) { + PM2.scale('child', 2, function(err) { + assert(!err); + PM2.list(function(err, list) { + should(err).be.null(); + should(list.length).eql(3); + assert(typeof list[0].pm2_env.unique_id === 'string') + assert(typeof list[1].pm2_env.unique_id === 'string') + assert(typeof list[2].pm2_env.unique_id === 'string') + assert(list[0].pm2_env.unique_id !== typeof list[1].pm2_env.unique_id) + assert(list[1].pm2_env.unique_id !== typeof list[2].pm2_env.unique_id) + assert(list[0].pm2_env.unique_id !== typeof list[2].pm2_env.unique_id) + done(); + }); + }); + }); + }); +}); diff --git a/test/programmatic/logs.js b/test/programmatic/logs.js index 6cf72e6b..8f73f908 100644 --- a/test/programmatic/logs.js +++ b/test/programmatic/logs.js @@ -33,7 +33,8 @@ describe('Programmatic log feature test', function() { pm2.start({ script: './echo.js', error_file : 'error-echo.log', - out_file : 'out-echo.log' + out_file : 'out-echo.log', + merge_logs: false }, function(err, procs) { should(err).be.null(); diff --git a/test/programmatic/path_resolution.mocha.js b/test/programmatic/path_resolution.mocha.js index 2c6f40b1..a29ed0d1 100644 --- a/test/programmatic/path_resolution.mocha.js +++ b/test/programmatic/path_resolution.mocha.js @@ -6,6 +6,7 @@ var PM2 = require('../..'); var should = require('should'); describe('Path resolution in configuration file', function() { + this.timeout(4000) before(function(done) { PM2.delete('all', function() { done() } ); }); @@ -16,8 +17,8 @@ describe('Path resolution in configuration file', function() { it('should resolve paths (home)', function(done) { PM2.start('./path-resolution/ecosystem.config.js', function(err, proc) { - should(proc[0].pm2_env.pm_err_log_path).eql(path.join(process.env.HOME, 'echo-err-0.log')); - should(proc[0].pm2_env.pm_out_log_path).eql(path.join(process.env.HOME, 'echo-out-0.log')); + should(proc[0].pm2_env.pm_err_log_path).eql(path.join(process.env.HOME, 'echo-err.log')); + should(proc[0].pm2_env.pm_out_log_path).eql(path.join(process.env.HOME, 'echo-out.log')); should(proc[0].pm2_env.pm_pid_path).eql(path.join(process.env.HOME, 'echo-pid.log')); done(); }); @@ -25,10 +26,22 @@ describe('Path resolution in configuration file', function() { it('should resolve paths (local)', function(done) { PM2.start('./path-resolution/ecosystem2.config.js', function(err, proc) { - should(proc[0].pm2_env.pm_err_log_path).eql(path.join(process.cwd(), 'echo-err-0.log')); - should(proc[0].pm2_env.pm_out_log_path).eql(path.join(process.cwd(), 'echo-out-0.log')); + should(proc[0].pm2_env.pm_err_log_path).eql(path.join(process.cwd(), 'echo-err.log')); + should(proc[0].pm2_env.pm_out_log_path).eql(path.join(process.cwd(), 'echo-out.log')); should(proc[0].pm2_env.pm_pid_path).eql(path.join(process.cwd(), 'echo-pid.log')); done(); }); }); + + it('should auto prefix log path on cluster mode', function(done) { + PM2.start('./path-resolution/ecosystem3.config.js', function(err, proc) { + should(proc[0].pm2_env.pm_err_log_path).eql(path.join(process.cwd(), 'echo-err-0.log')); + should(proc[0].pm2_env.pm_out_log_path).eql(path.join(process.cwd(), 'echo-out-0.log')); + + should(proc[1].pm2_env.pm_err_log_path).eql(path.join(process.cwd(), 'echo-err-1.log')); + should(proc[1].pm2_env.pm_out_log_path).eql(path.join(process.cwd(), 'echo-out-1.log')); + done(); + }); + }); + }); diff --git a/test/programmatic/programmatic.js b/test/programmatic/programmatic.js index e71f0aec..478e1361 100644 --- a/test/programmatic/programmatic.js +++ b/test/programmatic/programmatic.js @@ -18,7 +18,12 @@ describe('PM2 programmatic calls', function() { }); after(function(done) { - pm2.kill(done); + pm2.delete('all', function(err, ret) { + // clean dump file + pm2.clearDump(function(err) { + pm2.kill(done); + }); + }); }); before(function(done) { diff --git a/test/programmatic/reload-locker.mocha.js b/test/programmatic/reload-locker.mocha.js index c07fc0c3..0e07938c 100644 --- a/test/programmatic/reload-locker.mocha.js +++ b/test/programmatic/reload-locker.mocha.js @@ -17,6 +17,10 @@ describe('Reload locker system', function() { cwd : '../fixtures' }); + before(function(done) { + pm2.list(done); + }); + after(function(done) { pm2.kill(done) }); diff --git a/test/programmatic/signals.js b/test/programmatic/signals.js index 6c9c93b3..fad65206 100644 --- a/test/programmatic/signals.js +++ b/test/programmatic/signals.js @@ -162,21 +162,6 @@ describe('Signal kill (+delayed)', function() { }); }); - - it('should graceful reload script', function(done) { - setTimeout(function() { - pm2.list(function(err, list) { - list[0].pm2_env.status.should.eql('online'); - list[0].pm2_env.restart_time.should.eql(2); - done(); - }); - }, 1500); - - pm2.gracefulReload('delayed-sigint', function(err, app) { - //done(err); - }); - - }); }); describe('with 4000ms via kill_timeout (json/cli option)', function() { diff --git a/test/programmatic_commands.txt b/test/programmatic_commands.txt deleted file mode 100644 index f8e9053e..00000000 --- a/test/programmatic_commands.txt +++ /dev/null @@ -1,22 +0,0 @@ -mocha ./programmatic/god.mocha.js -mocha ./programmatic/programmatic.js -mocha ./programmatic/logs.js -mocha ./programmatic/watcher.js -mocha ./programmatic/max_memory_limit.js -mocha ./programmatic/cluster.mocha.js -mocha ./programmatic/misc_commands.js -mocha ./programmatic/signals.js -mocha ./programmatic/send_data_process.mocha.js -mocha ./programmatic/return.mocha.js -mocha ./programmatic/json_validation.mocha.js -mocha ./programmatic/env_switching.js -mocha ./programmatic/configuration.mocha.js -mocha ./interface/interactor.connect.mocha.js -mocha ./interface/interactor.daemonizer.mocha.js -mocha ./interface/remote.mocha.js -mocha ./interface/scoped_pm2_actions.mocha.js -mocha ./interface/password.mocha.js -mocha ./interface/custom-actions.mocha.js -mocha ./interface/bus.spec.mocha.js -mocha ./interface/bus.fork.spec.mocha.js -mocha ./interface/request.mocha.js diff --git a/test/unit.sh b/test/unit.sh new file mode 100644 index 00000000..1fb7143d --- /dev/null +++ b/test/unit.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +alias mocha='../node_modules/mocha/bin/mocha' +pm2="`type -P node` `pwd`/bin/pm2" + +# Abort script at first error +set -e +# Display all commands executed +set -o verbose + +function fail { + echo -e "######## \033[31m ✘ $1\033[0m" + exit 1 +} + +function success { + echo -e "\033[32m------------> ✔ $1\033[0m" +} + +function spec { + [ $? -eq 0 ] || fail "$1" + $pm2 uninstall all + $pm2 link delete + $pm2 kill + success "$1" +} + +$pm2 uninstall all + +# if [ $TRAVIS ] +# then +# export DEBUG="pm2:*" +# fi +cd test/programmatic + +mocha --exit --opts ./mocha.opts ./god.mocha.js +spec "God test" + +mocha --exit --opts ./mocha.opts ./programmatic.js +spec "Programmatic test" + +mocha --exit --opts ./mocha.opts ./containerizer.mocha.js +spec "Dockerfile parser test" + +mocha --exit --opts ./mocha.opts ./api.mocha.js +spec "API tests" +mocha --exit --opts ./mocha.opts ./path_resolution.mocha.js +spec "API tests" +mocha --exit --opts ./mocha.opts ./lazy_api.mocha.js +spec "API tests" +mocha --exit --opts ./mocha.opts ./reload-locker.mocha.js +spec "Reload locker tests" + +mocha --exit --opts ./mocha.opts ./api.backward.compatibility.mocha.js +spec "API Backward compatibility tests" +mocha --exit --opts ./mocha.opts ./custom_action.mocha.js +spec "Custom Actions tests" + +mocha --exit --opts ./mocha.opts ./logs.js +spec "Logs test" +mocha --exit --opts ./mocha.opts ./watcher.js +spec "Watcher" +mocha --exit --opts ./mocha.opts ./max_memory_limit.js +spec "Max memory tests" +# mocha --exit --opts ./mocha.opts ./module_configuration.mocha.js +# spec "Max memory tests" +mocha --exit --opts ./mocha.opts ./cluster.mocha.js +spec "Cluster tests" +mocha --exit --opts ./mocha.opts ./graceful.mocha.js +spec "Graceful tests" +mocha --exit --opts ./mocha.opts ./inside.mocha.js +spec "Inside pm2 call tests" +mocha --exit --opts ./mocha.opts ./misc_commands.js +spec "MISC tests" +mocha --exit --opts ./mocha.opts ./signals.js +spec "SIGINT signal interception + delay customization" +mocha --exit --opts ./mocha.opts ./send_data_process.mocha.js +spec "Send data to a process" +mocha --exit --opts ./mocha.opts ./modules.mocha.js +spec "Module API testing" +# mocha --exit --opts ./mocha.opts ./module_retrocompat.mocha.js +# spec "Module retrocompatibility system" + +mocha --exit --opts ./mocha.opts ./json_validation.mocha.js +spec "JSON validation test" +mocha --exit --opts ./mocha.opts ./env_switching.js +spec "JSON environment switching on JSON restart with --env" +mocha --exit --opts ./mocha.opts ./configuration.mocha.js +spec "Configuration system working" +mocha --exit --opts ./mocha.opts ./id.mocha.js +spec "Uniqueness id for each process" + +# +# Interface testing +# +cd ../interface + +# echo $PM2_HOME + +mocha --exit --opts ./mocha.opts ./bus.spec.mocha.js +spec "Protocol communication test" +mocha --exit --opts ./mocha.opts ./bus.fork.spec.mocha.js +spec "Protocol communication test" +mocha --exit --opts ./mocha.opts ./utility.mocha.js +spec "PM2 Utility" diff --git a/types/index.d.ts b/types/index.d.ts index 42ff4293..49c058d2 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -311,7 +311,7 @@ export interface StartOptions { * Eg “–harmony” or [”–harmony”,”–debug”]. Only applies if interpreter is something other * than “none” (its “node” by default). */ - interpreterArgs?: string | string[]; + interpreter_args?: string | string[]; /** * The working directory to start the process with. */ @@ -328,7 +328,7 @@ export interface StartOptions { /** * The display format for log timestamps (eg “YYYY-MM-DD HH:mm Z”). The format is a moment display format. */ - logDateFormat?: string; + log_date_format?: string; /** * Default: “~/.pm2/logs/~/.pm2/pids/app_name-id.pid”) * The path to a file to write the pid of the started process. The file will be overwritten. @@ -339,26 +339,26 @@ export interface StartOptions { /** * The minimum uptime of the script before it’s considered successfully started. */ - minUptime?: number; + min_uptime?: number; /** * The maximum number of times in a row a script will be restarted if it exits in less than min_uptime. */ - maxRestarts?: number; + max_restarts?: number; /** * If sets and script’s memory usage goes about the configured number, pm2 restarts the script. * Uses human-friendly suffixes: ‘K’ for kilobytes, ‘M’ for megabytes, ‘G’ for gigabytes’, etc. Eg “150M”. */ - maxMemoryRestart?: number; + max_memory_restart?: number; /** * (Default: 1600) * The number of milliseconds to wait after a stop or restart command issues a SIGINT signal to kill the * script forceably with a SIGKILL signal. */ - killTimeout?: number; + kill_timeout?: number; /** * (Default: 0) Number of millseconds to wait before restarting a script that has exited. */ - restartDelay?: number; + restart_delay?: number; /** * (Default: “node”) The interpreter for your script (eg “python”, “ruby”, “bash”, etc). * The value “none” will execute the ‘script’ as a binary executable. @@ -368,7 +368,7 @@ export interface StartOptions { * (Default: ‘fork’) If sets to ‘cluster’, will enable clustering * (running multiple instances of the script). */ - execMode?: string; + exec_mode?: string; /** * (Default: 1) How many instances of script to create. Only relevant in exec_mode ‘cluster’. */ @@ -379,7 +379,7 @@ export interface StartOptions { * ‘test.js’ started via pm2, normally you would have 4 stdout log files and 4 stderr log files, * but with this option set to true you would only have one stdout file and one stderr file. */ - mergeLogs?: boolean; + merge_logs?: boolean; /** * If set to true, the application will be restarted on change of the script file. */ @@ -391,10 +391,10 @@ export interface StartOptions { */ force?: boolean; cron?: any; - executeCommand?: any; + execute_command?: any; write?: any; - sourceMapSupport?: any; - disableSourceMapSupport?: any; + source_map_support?: any; + disable_source_map_support?: any; /** * The environment variables to pass on to the process. */ diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..0d6ed766 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1894 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@keymetrics/pmx@^2.0.0-alpha11": + version "2.0.0-alpha11" + resolved "https://registry.yarnpkg.com/@keymetrics/pmx/-/pmx-2.0.0-alpha11.tgz#6429672ee942dc30c200acaac1e305ec30a9cb32" + dependencies: + async "^2.6.0" + debug "^3.1.0" + deep-metrics "0.0.2" + deepmerge "^2.1.0" + json-stringify-safe "^5.0.1" + semver "^5.5.0" + signal-exit "^3.0.2" + tslib "^1.6.0" + vxx "^1.2.2" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +amp-message@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/amp-message/-/amp-message-0.1.2.tgz#a78f1c98995087ad36192a41298e4db49e3dfc45" + dependencies: + amp "0.3.1" + +amp@0.3.1, amp@~0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/amp/-/amp-0.3.1.tgz#6adf8d58a74f361e82c1fa8d389c079e139fc47d" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + +async-listener@^0.6.0: + version "0.6.9" + resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.9.tgz#51bc95e41095417f33922fb4dee4f232b3226488" + dependencies: + semver "^5.3.0" + shimmer "^1.1.0" + +async@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.0.tgz#ac3613b1da9bed1b47510bb4651b8931e47146c7" + +async@^1.5: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^2.5, async@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" + dependencies: + lodash "^4.14.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +atob@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +blessed@^0.1.81: + version "0.1.81" + resolved "https://registry.yarnpkg.com/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.0, braces@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + define-property "^1.0.0" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + kind-of "^6.0.2" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +browser-stdout@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +charm@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/charm/-/charm-0.1.2.tgz#06c21eed1a1b06aeb67553cdc53e23274bac2296" + +chokidar@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.0" + optionalDependencies: + fsevents "^1.0.0" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-table-redemption@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cli-table-redemption/-/cli-table-redemption-1.0.1.tgz#0359d8c34df74980029d76dff071a05a127c4fdd" + dependencies: + chalk "^1.1.3" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" + dependencies: + color-name "^1.1.1" + +color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" + dependencies: + delayed-stream "~1.0.0" + +commander@2.14.1: + version "2.14.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" + +commander@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +continuation-local-storage@^3.1.4: + version "3.2.1" + resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" + dependencies: + async-listener "^0.6.0" + emitter-listener "^1.1.1" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cron@^1.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/cron/-/cron-1.3.0.tgz#7e459968eaf94e1a445be796ce402166c234659d" + dependencies: + moment-timezone "^0.5.x" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@2.6.8: + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + dependencies: + ms "2.0.0" + +debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.0, debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + +deep-metrics@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/deep-metrics/-/deep-metrics-0.0.2.tgz#180900dea82a2c4b976be2b7684914748f5a0931" + dependencies: + semver "^5.3.0" + +deepmerge@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.0.tgz#511a54fff405fc346f0240bb270a3e9533a31102" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +diff@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +emitter-listener@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.1.tgz#e8bbbe8244bc8e0d0b4ef71cd14294c7f241c7ec" + dependencies: + shimmer "^1.2.0" + +escape-regexp@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/escape-regexp/-/escape-regexp-0.0.1.tgz#f44bda12d45bbdf9cb7f862ee7e4827b3dd32254" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +eventemitter2@5.0.1, eventemitter2@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452" + +eventemitter2@~0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0, extend@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fclone@1.0.11, fclone@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fclone/-/fclone-1.0.11.tgz#10e85da38bfea7fc599341c296ee1d77266ee640" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fs-extra@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.39" + +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +"gkt@https://tgz.pm2.io/gkt-1.0.0.tgz": + version "1.0.0" + resolved "https://tgz.pm2.io/gkt-1.0.0.tgz#405502b007f319c3f47175c4474527300f2ab5ad" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.5: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + +handy-http@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/handy-http/-/handy-http-1.0.2.tgz#24860dca376a69fbb7cd1a604a221e2a56639126" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +hawk@3.1.3, hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@^0.4.4: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +interpret@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-odd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" + dependencies: + is-number "^4.0.0" + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +is@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json3@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keymetrics-agent@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/keymetrics-agent/-/keymetrics-agent-0.4.0.tgz#9c23bb33427faaea6ed4dfdbd8316de4f3a229c9" + dependencies: + async "^2.6.0" + eventemitter2 "^5.0.1" + fclone "^1.0.11" + handy-http "^1.0.2" + moment "^2.21.0" + nssocket "^0.6.0" + pm2-axon "3.0.2" + pm2-axon-rpc "^0.5.0" + ws "^5.1.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +lazy@~1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690" + +lodash._baseassign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + dependencies: + lodash._basecopy "^3.0.0" + lodash.keys "^3.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + +lodash._basecreate@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + +lodash.create@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" + dependencies: + lodash._baseassign "^3.0.0" + lodash._basecreate "^3.0.0" + lodash._isiterateecall "^3.0.0" + +lodash.findindex@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.findindex/-/lodash.findindex-4.6.0.tgz#a3245dee61fb9b6e0624b535125624bb69c11106" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + +lodash.isequal@^4.0.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.merge@^4.6.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54" + +lodash@^4.14.0: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +methods@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^3.1.4: + version "3.1.9" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.9.tgz#15dc93175ae39e52e93087847096effc73efcf89" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + +mime-types@^2.1.12, mime-types@~2.1.7: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" + +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mocha@^3.5: + version "3.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" + dependencies: + browser-stdout "1.3.0" + commander "2.9.0" + debug "2.6.8" + diff "3.2.0" + escape-string-regexp "1.0.5" + glob "7.1.1" + growl "1.9.2" + he "1.1.1" + json3 "3.3.2" + lodash.create "3.1.1" + mkdirp "0.5.1" + supports-color "3.1.2" + +moment-timezone@^0.5.x: + version "0.5.14" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.14.tgz#4eb38ff9538b80108ba467a458f3ed4268ccfcb1" + dependencies: + moment ">= 2.9.0" + +"moment@>= 2.9.0", moment@^2.19, moment@^2.21.0: + version "2.21.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +mute-stream@~0.0.4: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +nan@^2.3.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + +nanomatch@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-odd "^2.0.0" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +needle@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.0.tgz#f14efc69cee1024b72c8b21c7bdf94a731dc12fa" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + +node-pre-gyp@^0.6.39: + version "0.6.39" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" + dependencies: + detect-libc "^1.0.2" + hawk "3.1.3" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nssocket@0.6.0, nssocket@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/nssocket/-/nssocket-0.6.0.tgz#59f96f6ff321566f33c70f7dbeeecdfdc07154fa" + dependencies: + eventemitter2 "~0.4.14" + lazy "~1.0.11" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +once@^1.3.0, once@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +pidusage@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/pidusage/-/pidusage-2.0.6.tgz#32bc37e57ca828f46b1c1e679a9cfc56ef945850" + +pm2-axon-rpc@^0.5.0, pm2-axon-rpc@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/pm2-axon-rpc/-/pm2-axon-rpc-0.5.1.tgz#ad3c43c43811c71f13e5eee2821194d03ceb03fe" + dependencies: + debug "^3.0" + +pm2-axon@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pm2-axon/-/pm2-axon-3.0.2.tgz#53de1d34edbf266d58f6b1dea2d8244c71ad24b9" + dependencies: + amp "~0.3.1" + amp-message "~0.1.1" + debug "~2.2.0" + escape-regexp "0.0.1" + +pm2-axon@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pm2-axon/-/pm2-axon-3.1.0.tgz#1b4527f3385e203adc1a5b0488bb52f0322731da" + dependencies: + amp "~0.3.1" + amp-message "~0.1.1" + debug "^3.0" + escape-regexp "0.0.1" + +pm2-deploy@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/pm2-deploy/-/pm2-deploy-0.3.9.tgz#adeee775c56d52b8f251ba9b0abe0db50a01dfc7" + dependencies: + async "^1.5" + tv4 "^1.3" + +pm2-multimeter@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz#1a1e55153d41a05534cea23cfe860abaa0eb4ace" + dependencies: + charm "~0.1.1" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +promptly@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/promptly/-/promptly-2.2.0.tgz#2a13fa063688a2a5983b161fff0108a07d26fc74" + dependencies: + read "^1.0.4" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +rc@^1.1.7: + version "1.2.6" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.6.tgz#eb18989c6d4f4f162c399f79ddd29f3835568092" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + dependencies: + mute-stream "~0.0.4" + +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +request@2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@^1.1.6: + version "1.6.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.6.0.tgz#0fbd21278b27b4004481c395349e7aba60a9ff5c" + dependencies: + path-parse "^1.0.5" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +semver@^5.0.1, semver@^5.3, semver@^5.3.0, semver@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shelljs@0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.1.tgz#729e038c413a2254c4078b95ed46e0397154a9f1" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +shimmer@^1.0.0, shimmer@^1.1.0, shimmer@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.0.tgz#f966f7555789763e74d8841193685a5e78736665" + +should-equal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3" + dependencies: + should-type "^1.4.0" + +should-format@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/should-format/-/should-format-3.0.3.tgz#9bfc8f74fa39205c53d38c34d717303e277124f1" + dependencies: + should-type "^1.3.0" + should-type-adaptors "^1.0.1" + +should-type-adaptors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz#401e7f33b5533033944d5cd8bf2b65027792e27a" + dependencies: + should-type "^1.3.0" + should-util "^1.0.0" + +should-type@^1.3.0, should-type@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/should-type/-/should-type-1.4.0.tgz#0756d8ce846dfd09843a6947719dfa0d4cff5cf3" + +should-util@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.0.tgz#c98cda374aa6b190df8ba87c9889c2b4db620063" + +should@^13: + version "13.2.1" + resolved "https://registry.yarnpkg.com/should/-/should-13.2.1.tgz#84e6ebfbb145c79e0ae42307b25b3f62dcaf574e" + dependencies: + should-equal "^2.0.0" + should-format "^3.0.3" + should-type "^1.4.0" + should-type-adaptors "^1.0.1" + should-util "^1.0.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +source-map-resolve@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" + dependencies: + atob "^2.0.0" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5: + version "0.5.4" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.4.tgz#54456efa89caa9270af7cd624cc2f123e51fbae8" + dependencies: + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" + dependencies: + has-flag "^1.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" + dependencies: + has-flag "^3.0.0" + +tar-pack@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@~2.3.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + dependencies: + punycode "^1.4.1" + +tslib@^1.6.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tv4@^1.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +universalify@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +use@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" + dependencies: + kind-of "^6.0.2" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@^3.0.0, uuid@^3.0.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + +v8-compile-cache@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz#8d32e4f16974654657e676e0e467a348e89b0dc4" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vizion@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/vizion/-/vizion-0.2.0.tgz#9cfb9e710e06e7b3d0e67474d38ab6b9f6a0c55b" + dependencies: + async "0.9.0" + +vxx@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/vxx/-/vxx-1.2.2.tgz#741fb51c6f11d3383da6f9b92018a5d7ba807611" + dependencies: + continuation-local-storage "^3.1.4" + debug "^2.6.3" + extend "^3.0.0" + is "^3.2.0" + lodash.findindex "^4.4.0" + lodash.isequal "^4.0.0" + lodash.merge "^4.6.0" + methods "^1.1.1" + semver "^5.0.1" + shimmer "^1.0.0" + uuid "^3.0.1" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + dependencies: + string-width "^1.0.2" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +ws@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.1.1.tgz#1d43704689711ac1942fd2f283e38f825c4b8b95" + dependencies: + async-limiter "~1.0.0" + +yamljs@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b" + dependencies: + argparse "^1.0.7" + glob "^7.0.5"