diff --git a/package-lock.json b/package-lock.json index bf66d300f5..a932343e63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,386 +44,6 @@ "resolved": "packages/core", "link": true }, - "node_modules/@artilleryio/platform-fargate": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@artilleryio/platform-fargate/-/platform-fargate-2.2.0.tgz", - "integrity": "sha512-k8+DKz/wow0piPxQCL0tNtMPsO1BROq6/oPe76mDiACct4BE4u72CS+KvRL62FRyFEL0lIctZtmYCZncaMJ1kQ==", - "dependencies": { - "@aws-sdk/credential-providers": "^3.41.0", - "@oclif/core": "^2.8.12", - "@supercharge/promise-pool": "^2.2.0", - "artillery-plugin-ensure": "^1.1.5", - "async": "^2.6.3", - "aws-sdk": "^2.1338.0", - "chalk": "^2.3.0", - "cli-table3": "^0.6.0", - "debug": "4.3.1", - "dependency-tree": "^6.1.0", - "detective": "^5.1.0", - "dotenv": "16.0.1", - "driftless": "2.0.3", - "ejs": "^3.1.8", - "got": "11.8.5", - "hdr-histogram-js": "^1.0.0", - "is-builtin-module": "^2.0.0", - "jsonwebtoken": "^9.0.1", - "lodash": "^4.17.20", - "log-update": "^4.0.0", - "moment": "^2.22.2", - "nanoid": "^3.3.4", - "ora": "^1.4.0", - "rc": "^1.2.8", - "semver-compare": "^1.0.0", - "sqs-consumer": "5.8.0", - "stats-lite": "^2.1.0", - "tmp": "0.0.33", - "traverse": "^0.6.6", - "typeorm": "^0.3.6", - "typeorm-aurora-data-api-driver": "^2.4.2", - "uuid": "^3.1.0", - "walk-sync": "^0.3.2", - "yaml-js": "^0.2.3" - }, - "engines": { - "node": ">= 18.16.1" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/@oclif/core": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.9.0.tgz", - "integrity": "sha512-Dd+uby0yf1CymNE4mkvnA+754AxPcTPh9j3AVtgCxAXZtbaphGFMd0OoM6xld8SVAKrDPSfE0WL185oJj3sCTw==", - "dependencies": { - "@types/cli-progress": "^3.11.0", - "ansi-escapes": "^4.3.2", - "ansi-styles": "^4.3.0", - "cardinal": "^2.1.1", - "chalk": "^4.1.2", - "clean-stack": "^3.0.1", - "cli-progress": "^3.12.0", - "debug": "^4.3.4", - "ejs": "^3.1.8", - "fs-extra": "^9.1.0", - "get-package-type": "^0.1.0", - "globby": "^11.1.0", - "hyperlinker": "^1.0.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "js-yaml": "^3.14.1", - "natural-orderby": "^2.0.3", - "object-treeify": "^1.1.33", - "password-prompt": "^1.1.2", - "semver": "^7.5.3", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "supports-color": "^8.1.1", - "supports-hyperlinks": "^2.2.0", - "ts-node": "^10.9.1", - "tslib": "^2.5.0", - "widest-line": "^3.1.0", - "wordwrap": "^1.0.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/@oclif/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/@oclif/core/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/@oclif/core/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/builtin-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-2.0.0.tgz", - "integrity": "sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/cli-spinners": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", - "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@artilleryio/platform-fargate/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/is-builtin-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-2.0.0.tgz", - "integrity": "sha512-G2jLHphOywpgrL/AaJKWDXpdpGR9X4V1PCkB+EwG5Z28z8EukgdWnAUFAS2wdBtIpwHhHBIiq0NBOWEbSXN0Rg==", - "dependencies": { - "builtin-modules": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dependencies": { - "chalk": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/ora": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-1.4.0.tgz", - "integrity": "sha512-iMK1DOQxzzh2MBlVsU42G80mnrvUhqsMh74phHtDlrcTZPK0pH6o7l7DRshK+0YsxDyEuaOkziVdvM3T0QTzpw==", - "dependencies": { - "chalk": "^2.1.0", - "cli-cursor": "^2.1.0", - "cli-spinners": "^1.0.1", - "log-symbols": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@artilleryio/platform-fargate/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/@artilleryio/types": { "resolved": "packages/types", "link": true @@ -2119,14 +1739,6 @@ "node": ">=6.9.0" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/@commitlint/cli": { "version": "9.1.2", "dev": true, @@ -5679,19 +5291,6 @@ "version": "3.1.0", "license": "MIT" }, - "node_modules/@sqltools/formatter": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", - "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" - }, - "node_modules/@supercharge/promise-pool": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@supercharge/promise-pool/-/promise-pool-2.4.0.tgz", - "integrity": "sha512-O9CMipBlq5OObdt1uKJGIzm9cdjpPWfj+a+Zw9EgWKxaMNHKC7EU7X9taj3H0EGQNLOSq2jAcOa3EzxlfHsD6w==", - "engines": { - "node": ">=8" - } - }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "license": "MIT", @@ -6273,14 +5872,6 @@ "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==" }, - "node_modules/app-root-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", - "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/append-transform": { "version": "2.0.0", "dev": true, @@ -6570,12 +6161,9 @@ } }, "node_modules/ast-module-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-3.0.0.tgz", - "integrity": "sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ==", - "engines": { - "node": ">=6.0" - } + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-2.7.1.tgz", + "integrity": "sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw==" }, "node_modules/ast-types": { "version": "0.13.4", @@ -6729,6 +6317,14 @@ "dev": true, "license": "MIT" }, + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "bin": { + "babylon": "bin/babylon.js" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "license": "MIT" @@ -7681,19 +7277,6 @@ "node": ">= 0.2.0" } }, - "node_modules/cli-table3": { - "version": "0.6.2", - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, "node_modules/cli-truncate": { "version": "2.1.0", "dev": true, @@ -8427,14 +8010,6 @@ "node": ">=0.10" } }, - "node_modules/data-api-client": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/data-api-client/-/data-api-client-1.3.0.tgz", - "integrity": "sha512-+Q+lChhl5PBogsB7nO/VZFF3X0WJe8y93dyft50HIg2Bg+c765wM/sXkfBz5pjmGoRESkB/GLesQJLTMBbK4dQ==", - "dependencies": { - "sqlstring": "^2.3.2" - } - }, "node_modules/data-uri-to-buffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz", @@ -8464,6 +8039,7 @@ }, "node_modules/date-fns": { "version": "2.29.3", + "dev": true, "license": "MIT", "engines": { "node": ">=0.11" @@ -8533,18 +8109,6 @@ "node": ">=0.10.0" } }, - "node_modules/decomment": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/decomment/-/decomment-0.9.5.tgz", - "integrity": "sha512-h0TZ8t6Dp49duwyDHo3iw67mnh9/UpFiSSiOb5gDK1sqoXzrfX/SQxIUQd2R2QEiSnqib0KF2fnKnGfAhAs6lg==", - "dependencies": { - "esprima": "4.0.1" - }, - "engines": { - "node": ">=6.4", - "npm": ">=2.15" - } - }, "node_modules/decompress-response": { "version": "6.0.0", "license": "MIT", @@ -8585,7 +8149,6 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "dev": true, "license": "MIT" }, "node_modules/default-require-extensions": { @@ -8615,7 +8178,8 @@ }, "node_modules/defined": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8653,14 +8217,14 @@ } }, "node_modules/dependency-tree": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-6.5.0.tgz", - "integrity": "sha512-r0KO5BkQy8sMbyTD8NxSDOexsySbGpSH5fIsxCLZjarFTXMryiZoR7+Ao6OWkwLz6OeQPYCOIjUDh+knWitMEw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-6.1.0.tgz", + "integrity": "sha512-UgkDPtveJ5ivm+h3TysJmzhhnWLfgY9KVFE2i+w6M10+1BmE6W+lzoGnG94w1Dy2PCfy0E8h0T9A0XlNxifPhQ==", "dependencies": { - "commander": "^2.19.0", - "debug": "^4.1.1", - "filing-cabinet": "^2.3.0", - "precinct": "^5.3.1" + "commander": "^2.6.0", + "debug": "^3.1.0", + "filing-cabinet": "^1.13.0", + "precinct": "^4.1.0" }, "bin": { "dependency-tree": "bin/cli.js" @@ -8674,6 +8238,14 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/dependency-tree/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/deprecation": { "version": "2.3.1", "dev": true, @@ -8688,69 +8260,150 @@ "minimalistic-assert": "^1.0.0" } }, - "node_modules/destroy": { - "version": "1.2.0", - "dev": true, - "license": "MIT", + "node_modules/destroy": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detective": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz", + "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==", + "dependencies": { + "acorn-node": "^1.3.0", + "defined": "^1.0.0", + "minimist": "^1.1.1" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/detective-amd": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-2.4.0.tgz", + "integrity": "sha512-ogtE4oKkh1bJKOa4CQMK1FH7pgq5C7FT8J9+jNldAhbP0QkL+wbs1XdkhOG0rXyWsKvEIs7Om00Yy1s+IHU+1Q==", + "dependencies": { + "ast-module-types": "^2.3.1", + "escodegen": "^1.8.0", + "get-amd-module-type": "^2.0.4", + "node-source-walk": "^3.0.0" + }, + "bin": { + "detective-amd": "bin/detective-amd.js" + } + }, + "node_modules/detective-amd/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/detective-amd/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/detective-amd/node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/detective-amd/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">= 0.8.0" } }, - "node_modules/detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "node_modules/detective-amd/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dependencies": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" - }, - "bin": { - "detective": "bin/detective.js" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" }, "engines": { - "node": ">=0.8.0" + "node": ">= 0.8.0" } }, - "node_modules/detective-amd": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-3.1.2.tgz", - "integrity": "sha512-jffU26dyqJ37JHR/o44La6CxtrDf3Rt9tvd2IbImJYxWKTMdBjctp37qoZ6ZcY80RHg+kzWz4bXn39e4P7cctQ==", + "node_modules/detective-amd/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/detective-amd/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detective-amd/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dependencies": { - "ast-module-types": "^3.0.0", - "escodegen": "^2.0.0", - "get-amd-module-type": "^3.0.0", - "node-source-walk": "^4.2.0" - }, - "bin": { - "detective-amd": "bin/cli.js" + "prelude-ls": "~1.1.2" }, "engines": { - "node": ">=6.0" + "node": ">= 0.8.0" } }, "node_modules/detective-cjs": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-3.1.3.tgz", - "integrity": "sha512-ljs7P0Yj9MK64B7G0eNl0ThWSYjhAaSYy+fQcpzaKalYl/UoQBOzOeLCSFEY1qEBhziZ3w7l46KG/nH+s+L7BQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-2.0.1.tgz", + "integrity": "sha512-N7zolR3HcXuqyIxtx2ER0iJ422AHRoHdxJE4fhCidPQHvXIdYSvq9nt8j3wrT4T4nZlzgpQc9iAubfhqQfGe6w==", "dependencies": { - "ast-module-types": "^3.0.0", - "node-source-walk": "^4.0.0" - }, - "engines": { - "node": ">=6.0" + "ast-module-types": "^2.3.2", + "node-source-walk": "^3.0.0" } }, "node_modules/detective-es6": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.2.tgz", - "integrity": "sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-1.2.0.tgz", + "integrity": "sha512-mJUovHcmWRVvqC99YOPvZcegGxrAVvO2Mu2cZ++ULm7LD80vth/cm1S3gOvA08vpVfCVfKykKYyrG5g4fl9ebg==", "dependencies": { - "node-source-walk": "^4.0.0" - }, - "engines": { - "node": ">=6.0" + "node-source-walk": "^3.3.0" } }, "node_modules/detective-less": { @@ -8766,66 +8419,129 @@ "node": ">= 6.0" } }, + "node_modules/detective-less/node_modules/node-source-walk": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-4.3.0.tgz", + "integrity": "sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA==", + "dependencies": { + "@babel/parser": "^7.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, "node_modules/detective-postcss": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-3.0.1.tgz", - "integrity": "sha512-tfTS2GdpUal5NY0aCqI4dpEy8Xfr88AehYKB0iBIZvo8y2g3UsrcDnrp9PR2FbzoW7xD5Rip3NJW7eCSvtqdUw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-2.1.2.tgz", + "integrity": "sha512-Ryf9mdjP4Lgrlkfy1bHkeUrCyS94kUzzrAXkXL/2JmTaG1COU4lkmWm6YG4u1P0SKSP179RVLhbUoJ6J788rRQ==", "dependencies": { - "debug": "^4.1.1", + "debug": "^3.1.0", "is-url": "^1.2.4", - "postcss": "^7.0.2", + "postcss": "^6.0.21", "postcss-values-parser": "^1.5.0" - }, - "engines": { - "node": ">=6.0.0" + } + }, + "node_modules/detective-postcss/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" } }, "node_modules/detective-sass": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-3.0.2.tgz", - "integrity": "sha512-DNVYbaSlmti/eztFGSfBw4nZvwsTaVXEQ4NsT/uFckxhJrNRFUh24d76KzoCC3aarvpZP9m8sC2L1XbLej4F7g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-2.0.1.tgz", + "integrity": "sha512-+gcbXjkW+cPS2HMuRSy9y23kp2vixZ2DdGXEsu6TjMnWvEiEs4KFLTLINbChTW8faPZXfB5elvsL+j6TGcKW4A==", "dependencies": { - "gonzales-pe": "^4.3.0", - "node-source-walk": "^4.0.0" + "debug": "^3.1.0", + "gonzales-pe": "^3.4.4", + "node-source-walk": "^3.2.0" + } + }, + "node_modules/detective-sass/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/detective-sass/node_modules/gonzales-pe": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-3.4.7.tgz", + "integrity": "sha512-Ne+fRUiNEehz6wYltvzWSBfQNXfu6KAkevQnzk4SOoQIT1JTQRNjx/CthCT83u3ntZROEE5ObN6LSaOcDcYlig==", + "dependencies": { + "minimist": "1.1.x" + }, + "bin": { + "gonzales": "bin/gonzales.js" }, "engines": { - "node": ">=6.0" + "node": ">=0.6.0" } }, + "node_modules/detective-sass/node_modules/minimist": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", + "integrity": "sha512-2RbeLaM/Hbo9vJ1+iRrxzfDnX9108qb2m923U+s+Ot2eMey0IYGdSjzHmvtg2XsxoCuMnzOMw7qc573RvnLgwg==" + }, "node_modules/detective-scss": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-2.0.2.tgz", - "integrity": "sha512-hDWnWh/l0tht/7JQltumpVea/inmkBaanJUcXRB9kEEXVwVUMuZd6z7eusQ6GcBFrfifu3pX/XPyD7StjbAiBg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-1.0.1.tgz", + "integrity": "sha512-2z2zUpVghABzkf22YVhpHXezXqz+lxJQcrSJMshpmX2SnIHx9wyx9GZ26zTWjVig5pM4eA7KvtCe63O/cea3cw==", "dependencies": { - "gonzales-pe": "^4.3.0", - "node-source-walk": "^4.0.0" + "debug": "^3.1.0", + "gonzales-pe": "^3.4.4", + "node-source-walk": "^3.2.0" + } + }, + "node_modules/detective-scss/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/detective-scss/node_modules/gonzales-pe": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-3.4.7.tgz", + "integrity": "sha512-Ne+fRUiNEehz6wYltvzWSBfQNXfu6KAkevQnzk4SOoQIT1JTQRNjx/CthCT83u3ntZROEE5ObN6LSaOcDcYlig==", + "dependencies": { + "minimist": "1.1.x" + }, + "bin": { + "gonzales": "bin/gonzales.js" }, "engines": { - "node": ">=6.0" + "node": ">=0.6.0" } }, + "node_modules/detective-scss/node_modules/minimist": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", + "integrity": "sha512-2RbeLaM/Hbo9vJ1+iRrxzfDnX9108qb2m923U+s+Ot2eMey0IYGdSjzHmvtg2XsxoCuMnzOMw7qc573RvnLgwg==" + }, "node_modules/detective-stylus": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-1.0.3.tgz", "integrity": "sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q==" }, "node_modules/detective-typescript": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-4.1.2.tgz", - "integrity": "sha512-jeQMIN/0hjMdMpFGoo9y+ibo+dTb1Vbg6z/peHoRMR69jqH691kgz1gT5XM5UfkDD/Ru0save1bSJBmUr2yjvQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-3.0.0.tgz", + "integrity": "sha512-i7AZLrrJO2HWhvSMp6Kqwsw+uYhMOitvzNgnX2NfK+AUv+w6Eq0HjJ62g2ZZOf4hINMeNsXw0X2n1hb2mZbmOw==", "dependencies": { - "node-source-walk": "^4.0.0", - "typescript": "^3.0.3", - "typescript-eslint-parser": "^18.0.0" - }, - "engines": { - "node": ">=6.0" + "node-source-walk": "^3.3.0", + "typescript": "^2.8.3", + "typescript-eslint-parser": "^15.0.0" } }, "node_modules/detective-typescript/node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9153,11 +8869,6 @@ "node": ">=8.6" } }, - "node_modules/ensure-posix-path": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz", - "integrity": "sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw==" - }, "node_modules/entities": { "version": "2.2.0", "license": "BSD-2-Clause", @@ -10527,13 +10238,10 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-exists-dazinatorfork": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/file-exists-dazinatorfork/-/file-exists-dazinatorfork-1.0.2.tgz", - "integrity": "sha512-r70c72ln2YHzQINNfxDp02hAhbGkt1HffZ+Du8oetWDLjDtFja/Lm10lUaSh9e+wD+7VDvPee0b0C9SAy8pWZg==", - "engines": { - "node": ">=6.0.0" - } + "node_modules/file-exists": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-exists/-/file-exists-2.0.0.tgz", + "integrity": "sha512-xiDzGS+oe7RleGLItoPTKVj3DjKHiVANV6dEyWTt3yB08H/AHuyvjh0aZ8jISQ53yhyTWVLsDzhJ5IbvPciBjg==" }, "node_modules/filelist": { "version": "1.0.4", @@ -10560,23 +10268,22 @@ } }, "node_modules/filing-cabinet": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-2.6.0.tgz", - "integrity": "sha512-7kSlTScEkxoYKXCix7tAQ52ZeIHcx7ZWWArEZgXY+eTMe6yDYFdDhHdkXm9rSmvrrpzdZeR1wiufS1rUt4OzMA==", + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-1.14.4.tgz", + "integrity": "sha512-QKKxmJrWJpQkg6Mb4ENfYJI47cwY5g5VnVKQeR1jtscSjmVE//YmOeD6MGsNJlJO3W9HBhMJwAPccBeM5dK/bw==", "dependencies": { "app-module-path": "^2.2.0", "commander": "^2.13.0", - "debug": "^4.1.1", - "decomment": "^0.9.2", + "debug": "^3.1.0", "enhanced-resolve": "^4.1.0", "is-relative-path": "^1.0.2", - "module-definition": "^3.0.0", - "module-lookup-amd": "^6.1.0", - "resolve": "^1.11.1", - "resolve-dependency-path": "^2.0.0", - "sass-lookup": "^3.0.0", - "stylus-lookup": "^3.0.1", - "typescript": "^3.0.3" + "module-definition": "^2.2.4", + "module-lookup-amd": "^5.0.1", + "resolve": "^1.5.0", + "resolve-dependency-path": "^1.0.2", + "sass-lookup": "^2.0.0", + "stylus-lookup": "^2.0.0", + "typescript": "^2.4.2" }, "bin": { "filing-cabinet": "bin/cli.js" @@ -10590,10 +10297,18 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/filing-cabinet/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/filing-cabinet/node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10647,9 +10362,9 @@ "license": "MIT" }, "node_modules/find": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz", - "integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/find/-/find-0.2.9.tgz", + "integrity": "sha512-7a4/LCiInB9xYMnAUEjLilL9FKclwbwK7VlXw+h5jMvT2TDFeYFCHM24O1XdnC/on/hx8mxVO3FTQkyHZnOghQ==", "dependencies": { "traverse-chain": "~0.1.0" } @@ -11008,15 +10723,12 @@ } }, "node_modules/get-amd-module-type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz", - "integrity": "sha512-PcuKwB8ouJnKuAPn6Hk3UtdfKoUV3zXRqVEvj8XGIXqjWfgd1j7QGdXy5Z9OdQfzVt1Sk29HVe/P+X74ccOuqw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-2.0.5.tgz", + "integrity": "sha512-ScWV7OFmLmoab7zw065zMD0iR8/zg4TqjH3rpElGbhJEikS4R/dErxox9M8wJMWhUsZqX37PbtDI9fNz9LMkWQ==", "dependencies": { - "ast-module-types": "^3.0.0", - "node-source-walk": "^4.2.2" - }, - "engines": { - "node": ">=6.0" + "ast-module-types": "^2.3.2", + "node-source-walk": "^3.2.0" } }, "node_modules/get-bin-path": { @@ -11533,15 +11245,6 @@ "node": ">=8" } }, - "node_modules/hdr-histogram-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-1.2.0.tgz", - "integrity": "sha512-h0YToJ3ewqsaZ3nFTTa6dLOD7sqx+EgdC4+OcJ9Ou7zZDlT0sXSPHHr3cyenQsPqqbVHGn/oFY6zjfEKXGvzmQ==", - "dependencies": { - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, "node_modules/he": { "version": "1.2.0", "dev": true, @@ -12252,11 +11955,6 @@ "version": "2.0.0", "license": "ISC" }, - "node_modules/isnumber": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isnumber/-/isnumber-1.0.0.tgz", - "integrity": "sha512-JLiSz/zsZcGFXPrB4I/AGBvtStkt+8QmksyZBZnVXnnK9XdTEyz0tX8CRYljtwYDuIuZzih6DpHQdi+3Q6zHPw==" - }, "node_modules/isomorphic-ws": { "version": "4.0.1", "license": "MIT", @@ -13344,6 +13042,7 @@ }, "node_modules/log-update": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-escapes": "^4.3.0", @@ -13360,6 +13059,7 @@ }, "node_modules/log-update/node_modules/ansi-styles": { "version": "4.3.0", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -13373,6 +13073,7 @@ }, "node_modules/log-update/node_modules/color-convert": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -13383,10 +13084,12 @@ }, "node_modules/log-update/node_modules/color-name": { "version": "1.1.4", + "dev": true, "license": "MIT" }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -13402,6 +13105,7 @@ }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "6.2.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -14182,37 +13886,34 @@ "license": "MIT" }, "node_modules/module-definition": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-3.4.0.tgz", - "integrity": "sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-2.2.4.tgz", + "integrity": "sha512-1mXB+Sbx/r4cSIaMPFKypknxgS/IL27ouABibSSgR61woGGpw8b/LniUC5NXpJwyk2ZufhctomVIXNwtak6s4g==", "dependencies": { - "ast-module-types": "^3.0.0", - "node-source-walk": "^4.0.0" + "ast-module-types": "^2.3.2", + "node-source-walk": "^3.0.0" }, "bin": { - "module-definition": "bin/cli.js" - }, - "engines": { - "node": ">=6.0" + "module-definition": "bin/module-definition.js" } }, "node_modules/module-lookup-amd": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-6.2.0.tgz", - "integrity": "sha512-uxHCj5Pw9psZiC1znjU2qPsubt6haCSsN9m7xmIdoTciEgfxUkE1vhtDvjHPuOXEZrVJhjKgkmkP+w73rRuelQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-5.0.2.tgz", + "integrity": "sha512-lOyf83SGq+gE0ElfuyCVFqpl9NqKFxfz7Yj+1crSmf7ozAHEkWIqeDTT4z1+2VOXYhxLcXCJF1XFpWU7y1KtaA==", "dependencies": { "commander": "^2.8.1", - "debug": "^4.1.0", - "file-exists-dazinatorfork": "^1.0.2", - "find": "^0.3.0", + "debug": "^3.1.0", + "file-exists": "^2.0.0", + "find": "^0.2.8", "requirejs": "^2.3.5", - "requirejs-config-file": "^3.1.1" + "requirejs-config-file": "3.0.0" }, "bin": { "lookup-amd": "bin/cli.js" }, "engines": { - "node": ">=6.0.0" + "node": ">=4.0.0" } }, "node_modules/module-lookup-amd/node_modules/commander": { @@ -14220,6 +13921,14 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/module-lookup-amd/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/moment": { "version": "2.29.4", "license": "MIT", @@ -14592,14 +14301,11 @@ "license": "MIT" }, "node_modules/node-source-walk": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-4.3.0.tgz", - "integrity": "sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-3.3.0.tgz", + "integrity": "sha512-Mh6WG6jY2SaE9cqDYOrCYsY2ZO1eDqSg17anwRTxlCwyMdOH+wSOAq8Udc68brWP5oQ5pUW7ecQcGBMFof74jA==", "dependencies": { - "@babel/parser": "^7.0.0" - }, - "engines": { - "node": ">=6.0" + "babylon": "^6.17.0" } }, "node_modules/nopt": { @@ -15188,6 +14894,10 @@ "node": ">=12.0.0" } }, + "node_modules/oclif-hello-world": { + "resolved": "packages/artillery-cli", + "link": true + }, "node_modules/oclif/node_modules/fs-extra": { "version": "8.1.0", "dev": true, @@ -15369,6 +15079,7 @@ }, "node_modules/os-tmpdir": { "version": "1.0.2", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -15621,6 +15332,7 @@ }, "node_modules/pako": { "version": "1.0.11", + "dev": true, "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { @@ -15962,19 +15674,16 @@ } }, "node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=4.0.0" } }, "node_modules/postcss-values-parser": { @@ -15990,11 +15699,6 @@ "node": ">=4" } }, - "node_modules/postcss/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, "node_modules/postcss/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -16015,23 +15719,23 @@ } }, "node_modules/precinct": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/precinct/-/precinct-5.3.1.tgz", - "integrity": "sha512-HOIXDarP6S5JXYC5GhnpoAj9RqJ6yAwZ8VI71vQFlq1rmkBRPs+Mt60TOr7DUc/fx309iIQaniB4x3zueOOSdw==", - "dependencies": { - "commander": "^2.19.0", - "debug": "^4.1.1", - "detective-amd": "^3.0.0", - "detective-cjs": "^3.1.1", - "detective-es6": "^2.0.0", - "detective-less": "^1.0.2", - "detective-postcss": "^3.0.0", - "detective-sass": "^3.0.0", - "detective-scss": "^2.0.0", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/precinct/-/precinct-4.3.0.tgz", + "integrity": "sha512-WTgiQDLrJxKIVLOrDWuhGX32d3XdUgblszVbWkUneRVwK8kCOv3aAIc9iixMQ698ERteS/miv8ZylTW4iJUkJg==", + "dependencies": { + "commander": "^2.11.0", + "debug": "^3.0.1", + "detective-amd": "^2.4.0", + "detective-cjs": "^2.0.0", + "detective-es6": "^1.2.0", + "detective-less": "^1.0.1", + "detective-postcss": "^2.1.0", + "detective-sass": "^2.0.0", + "detective-scss": "^1.0.0", "detective-stylus": "^1.0.0", - "detective-typescript": "^4.1.2", - "module-definition": "^3.1.0", - "node-source-walk": "^4.2.0" + "detective-typescript": "^3.0.0", + "module-definition": "^2.2.4", + "node-source-walk": "^3.3.0" }, "bin": { "precinct": "bin/cli.js" @@ -16045,6 +15749,14 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/precinct/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/preferred-pm": { "version": "3.0.3", "dev": true, @@ -16742,11 +16454,6 @@ "esprima": "~4.0.0" } }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" - }, "node_modules/regenerator-runtime": { "version": "0.13.9", "dev": true, @@ -16909,44 +16616,26 @@ } }, "node_modules/requirejs-config-file": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-3.1.2.tgz", - "integrity": "sha512-sdLWywcDuNz7EIOhenSbRfT4YF84nItDv90coN2htbokjmU2QeyQuSBZILQUKNksepl8UPVU+hgYySFaDxbJPQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-3.0.0.tgz", + "integrity": "sha512-pssKfw0KhafnpOHA1+qWlDXcCEgf0p+qfTI8xOhqOhhdtz7m7VqhEorauKZOqv1GGA8ML3eKFU3sxGq5p4ZaEw==", "dependencies": { "esprima": "^4.0.0", - "make-dir": "^2.1.0", + "fs-extra": "^5.0.0", "stringify-object": "^3.2.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=4.0.0" } }, - "node_modules/requirejs-config-file/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/requirejs-config-file/node_modules/fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/requirejs-config-file/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/requirejs-config-file/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "node_modules/requires-port": { @@ -16974,12 +16663,9 @@ "license": "MIT" }, "node_modules/resolve-dependency-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz", - "integrity": "sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w==", - "engines": { - "node": ">=6.0.0" - } + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-1.0.2.tgz", + "integrity": "sha512-oiRQNK+0iJGazDKGTcxrlW6JpE1yzVqeyhMCKxZ1RU90J2T7A+BKLlED0lI8N5MMfLniKHRL2h+RPse7t+AxGA==" }, "node_modules/resolve-from": { "version": "5.0.0", @@ -17165,9 +16851,9 @@ "license": "BSD-3-Clause" }, "node_modules/sass-lookup": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-3.0.0.tgz", - "integrity": "sha512-TTsus8CfFRn1N44bvdEai1no6PqdmDiQUiqW5DlpmtT+tYnIt1tXtDIph5KA1efC+LmioJXSnCtUVpcK9gaKIg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-2.0.0.tgz", + "integrity": "sha512-DZEg7g605XNZX3rxQMkndPmlSzaGR3ld33Rvx3XPTxP8hXBPErmCTrL2CPItzjCJqvjgt9kXhxQrzkbdJZToaA==", "dependencies": { "commander": "^2.16.0" }, @@ -17175,7 +16861,7 @@ "sass-lookup": "bin/cli.js" }, "engines": { - "node": ">=6.0.0" + "node": ">=4.0.0" } }, "node_modules/sass-lookup/node_modules/commander": { @@ -17208,7 +16894,8 @@ "node_modules/semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true }, "node_modules/send": { "version": "0.18.0", @@ -17309,6 +16996,7 @@ }, "node_modules/sha.js": { "version": "2.4.11", + "dev": true, "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.1", @@ -17654,14 +17342,6 @@ "version": "1.1.2", "license": "BSD-3-Clause" }, - "node_modules/sqlstring": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/sqs-consumer": { "version": "5.8.0", "license": "Apache-2.0", @@ -17723,21 +17403,10 @@ }, "node_modules/stack-utils/node_modules/escape-string-regexp": { "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/stats-lite": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stats-lite/-/stats-lite-2.2.0.tgz", - "integrity": "sha512-/Kz55rgUIv2KP2MKphwYT/NCuSfAlbbMRv2ZWw7wyXayu230zdtzhxxuXXcvsc6EmmhS8bSJl3uS1wmMHFumbA==", - "dependencies": { - "isnumber": "~1.0.0" - }, + "dev": true, + "license": "MIT", "engines": { - "node": ">=2.0.0" + "node": ">=8" } }, "node_modules/statuses": { @@ -17941,18 +17610,18 @@ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, "node_modules/stylus-lookup": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-3.0.2.tgz", - "integrity": "sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-2.0.0.tgz", + "integrity": "sha512-ZPwVUITlzCIgq1NBNl1xVX1grfFnJUBq9zzG9YOj/V3GrOCnpWuxGh6zUL8JTaVs0nMS9Eyok1qgOW9mUFx9kg==", "dependencies": { "commander": "^2.8.1", - "debug": "^4.1.0" + "debug": "^3.1.0" }, "bin": { "stylus-lookup": "bin/cli.js" }, "engines": { - "node": ">=6.0.0" + "node": ">=4.0.0" } }, "node_modules/stylus-lookup/node_modules/commander": { @@ -17960,6 +17629,14 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/stylus-lookup/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/superagent": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", @@ -20541,14 +20218,6 @@ "dev": true, "license": "MIT" }, - "node_modules/traverse": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", - "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/traverse-chain": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz", @@ -20776,383 +20445,53 @@ "optional": true, "peer": true }, - "node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "dev": true, - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typeorm": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.17.tgz", - "integrity": "sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==", - "dependencies": { - "@sqltools/formatter": "^1.2.5", - "app-root-path": "^3.1.0", - "buffer": "^6.0.3", - "chalk": "^4.1.2", - "cli-highlight": "^2.1.11", - "date-fns": "^2.29.3", - "debug": "^4.3.4", - "dotenv": "^16.0.3", - "glob": "^8.1.0", - "mkdirp": "^2.1.3", - "reflect-metadata": "^0.1.13", - "sha.js": "^2.4.11", - "tslib": "^2.5.0", - "uuid": "^9.0.0", - "yargs": "^17.6.2" - }, - "bin": { - "typeorm": "cli.js", - "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", - "typeorm-ts-node-esm": "cli-ts-node-esm.js" - }, - "engines": { - "node": ">= 12.9.0" - }, - "funding": { - "url": "https://opencollective.com/typeorm" - }, - "peerDependencies": { - "@google-cloud/spanner": "^5.18.0", - "@sap/hana-client": "^2.12.25", - "better-sqlite3": "^7.1.2 || ^8.0.0", - "hdb-pool": "^0.1.6", - "ioredis": "^5.0.4", - "mongodb": "^5.2.0", - "mssql": "^9.1.1", - "mysql2": "^2.2.5 || ^3.0.1", - "oracledb": "^5.1.0", - "pg": "^8.5.1", - "pg-native": "^3.0.0", - "pg-query-stream": "^4.0.0", - "redis": "^3.1.1 || ^4.0.0", - "sql.js": "^1.4.0", - "sqlite3": "^5.0.3", - "ts-node": "^10.7.0", - "typeorm-aurora-data-api-driver": "^2.0.0" - }, - "peerDependenciesMeta": { - "@google-cloud/spanner": { - "optional": true - }, - "@sap/hana-client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "hdb-pool": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "mongodb": { - "optional": true - }, - "mssql": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-native": { - "optional": true - }, - "pg-query-stream": { - "optional": true - }, - "redis": { - "optional": true - }, - "sql.js": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "ts-node": { - "optional": true - }, - "typeorm-aurora-data-api-driver": { - "optional": true - } - } - }, - "node_modules/typeorm-aurora-data-api-driver": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/typeorm-aurora-data-api-driver/-/typeorm-aurora-data-api-driver-2.4.4.tgz", - "integrity": "sha512-EqrdoXr0FbUrAMmkNQQuPwlhUGM7SJnpwUlWTWNlK2mOhOUyM+33fhm1f1hz3nnJJV8fTxzS3kTDq6pkVASLAw==", - "dependencies": { - "data-api-client": "^1.3.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/typeorm/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/typeorm/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typeorm/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/typeorm/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/typeorm/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/typeorm/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/typeorm/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/typeorm/node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" - } - }, - "node_modules/typeorm/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typeorm/node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/typeorm/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typeorm/node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typeorm/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/typeorm/node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/typeorm/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/type-fest": { + "version": "0.21.3", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typeorm/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "node_modules/type-is": { + "version": "1.6.18", + "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, "engines": { - "node": ">=12" + "node": ">= 0.6" } }, - "node_modules/typeorm/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" } }, "node_modules/typescript": { @@ -21167,15 +20506,15 @@ } }, "node_modules/typescript-eslint-parser": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-18.0.0.tgz", - "integrity": "sha512-Pn/A/Cw9ysiXSX5U1xjBmPQlxtWGV2o7jDNiH/u7KgBO2yC/y37wNFl2ogSrGZBQFuglLzGq0Xl0Bt31Jv44oA==", + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-15.0.1.tgz", + "integrity": "sha512-HqwfNJ+/gDBzvxkfSbvxBwSFhRq82oXNpeKOogYR2Bwezik8GqY+rIDI1g7w91ZmwiXFDtpEIphL5BimgRRA6g==", "dependencies": { "lodash.unescape": "4.0.1", "semver": "5.5.0" }, "engines": { - "node": ">=6.14.0" + "node": ">=4" }, "peerDependencies": { "typescript": "*" @@ -21516,11 +20855,10 @@ "license": "MIT" }, "node_modules/walk-sync": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.3.4.tgz", - "integrity": "sha512-ttGcuHA/OBnN2pcM6johpYlEms7XpO5/fyKIr48541xXedan4roO8cS1Q2S/zbbjGH/BarYDAMeS2Mi9HE5Tig==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.2.3.tgz", + "integrity": "sha512-Naj4sVCshbVFNl0ii45NxuBghJF14qrZcSKz+SuqYgqNfuhx8iauG8BjDnM3lV82AJpbzpZsjIWuk50B8U+whw==", "dependencies": { - "ensure-posix-path": "^1.0.0", "matcher-collection": "^1.0.0" } }, @@ -21647,6 +20985,14 @@ "wipe-node-cache": "^2.1.0" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "license": "MIT" @@ -21968,18 +21314,244 @@ "textextensions": "^5.12.0", "untildify": "^4.0.0" }, - "bin": { - "yoe": "cli/index.js" + "bin": { + "yoe": "cli/index.js" + }, + "engines": { + "node": ">=12.10.0" + }, + "peerDependencies": { + "mem-fs": "^1.2.0 || ^2.0.0", + "mem-fs-editor": "^8.1.2 || ^9.0.0" + } + }, + "node_modules/yeoman-environment/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/yeoman-environment/node_modules/arrify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yeoman-environment/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/yeoman-environment/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/yeoman-environment/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/yeoman-environment/node_modules/diff": { + "version": "5.1.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/yeoman-environment/node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/yeoman-environment/node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yeoman-environment/node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yeoman-generator": { + "version": "5.7.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "chalk": "^4.1.0", + "dargs": "^7.0.0", + "debug": "^4.1.1", + "execa": "^5.1.1", + "github-username": "^6.0.0", + "lodash": "^4.17.11", + "minimist": "^1.2.5", + "read-pkg-up": "^7.0.1", + "run-async": "^2.0.0", + "semver": "^7.2.1", + "shelljs": "^0.8.5", + "sort-keys": "^4.2.0", + "text-table": "^0.2.0" }, "engines": { "node": ">=12.10.0" }, "peerDependencies": { - "mem-fs": "^1.2.0 || ^2.0.0", - "mem-fs-editor": "^8.1.2 || ^9.0.0" + "yeoman-environment": "^3.2.0" + }, + "peerDependenciesMeta": { + "yeoman-environment": { + "optional": true + } } }, - "node_modules/yeoman-environment/node_modules/ansi-styles": { + "node_modules/yeoman-generator/node_modules/ansi-styles": { "version": "4.3.0", "dev": true, "license": "MIT", @@ -21993,15 +21565,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/yeoman-environment/node_modules/arrify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yeoman-environment/node_modules/chalk": { + "node_modules/yeoman-generator/node_modules/chalk": { "version": "4.1.2", "dev": true, "license": "MIT", @@ -22016,7 +21580,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/yeoman-environment/node_modules/color-convert": { + "node_modules/yeoman-generator/node_modules/color-convert": { "version": "2.0.1", "dev": true, "license": "MIT", @@ -22027,31 +21591,12 @@ "node": ">=7.0.0" } }, - "node_modules/yeoman-environment/node_modules/color-name": { + "node_modules/yeoman-generator/node_modules/color-name": { "version": "1.1.4", "dev": true, "license": "MIT" }, - "node_modules/yeoman-environment/node_modules/diff": { - "version": "5.1.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/yeoman-environment/node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yeoman-environment/node_modules/execa": { + "node_modules/yeoman-generator/node_modules/execa": { "version": "5.1.1", "dev": true, "license": "MIT", @@ -22073,14 +21618,10 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/yeoman-environment/node_modules/find-up": { - "version": "5.0.0", + "node_modules/yeoman-generator/node_modules/get-stream": { + "version": "6.0.1", "dev": true, "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { "node": ">=10" }, @@ -22088,35 +21629,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yeoman-environment/node_modules/get-stream": { - "version": "6.0.1", + "node_modules/yeoman-generator/node_modules/is-stream": { + "version": "2.0.1", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yeoman-environment/node_modules/is-stream": { - "version": "2.0.1", + "node_modules/yeoman-generator/node_modules/npm-run-path": { + "version": "4.0.1", "dev": true, "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yeoman-environment/node_modules/locate-path": { - "version": "6.0.0", + "node_modules/yeoman-generator/node_modules/supports-color": { + "version": "7.2.0", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "has-flag": "^4.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -22124,88 +21680,200 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yeoman-environment/node_modules/npm-run-path": { - "version": "4.0.1", + "node_modules/yosay": { + "version": "2.0.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "ansi-regex": "^2.0.0", + "ansi-styles": "^3.0.0", + "chalk": "^1.0.0", + "cli-boxes": "^1.0.0", + "pad-component": "0.0.1", + "string-width": "^2.0.0", + "strip-ansi": "^3.0.0", + "taketalk": "^1.0.0", + "wrap-ansi": "^2.0.0" + }, + "bin": { + "yosay": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yosay/node_modules/chalk": { + "version": "1.1.3", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.0.0" + "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" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/yeoman-environment/node_modules/p-limit": { - "version": "3.1.0", + "node_modules/yosay/node_modules/chalk/node_modules/ansi-styles": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yosay/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yosay/node_modules/string-width": { + "version": "2.1.1", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" + } + }, + "node_modules/yosay/node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yosay/node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yosay/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yosay/node_modules/supports-color": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/yosay/node_modules/wrap-ansi": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yosay/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yosay/node_modules/wrap-ansi/node_modules/string-width": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/yeoman-environment/node_modules/p-locate": { - "version": "5.0.0", - "dev": true, + "node_modules/zip-stream": { + "version": "4.1.0", "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "archiver-utils": "^2.1.0", + "compress-commons": "^4.1.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 10" } }, - "node_modules/yeoman-environment/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "3.6.0", "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/yeoman-generator": { - "version": "5.7.0", + "node_modules/zx": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/zx/-/zx-4.3.0.tgz", + "integrity": "sha512-KuEjpu5QFIMx0wWfzknDRhY98s7a3tWNRmYt19XNmB7AfOmz5zISA4+3Q8vlJc2qguxMn89uSxhPDCldPa3YLA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "chalk": "^4.1.0", - "dargs": "^7.0.0", - "debug": "^4.1.1", - "execa": "^5.1.1", - "github-username": "^6.0.0", - "lodash": "^4.17.11", + "@types/fs-extra": "^9.0.12", + "@types/minimist": "^1.2.2", + "@types/node": "^16.6", + "@types/node-fetch": "^2.5.12", + "chalk": "^4.1.2", + "fs-extra": "^10.0.0", + "globby": "^12.0.1", "minimist": "^1.2.5", - "read-pkg-up": "^7.0.1", - "run-async": "^2.0.0", - "semver": "^7.2.1", - "shelljs": "^0.8.5", - "sort-keys": "^4.2.0", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=12.10.0" + "node-fetch": "^2.6.1", + "ps-tree": "^1.2.0", + "which": "^2.0.2" }, - "peerDependencies": { - "yeoman-environment": "^3.2.0" + "bin": { + "zx": "zx.mjs" }, - "peerDependenciesMeta": { - "yeoman-environment": { - "optional": true - } + "engines": { + "node": ">= 14.13.1" } }, - "node_modules/yeoman-generator/node_modules/ansi-styles": { + "node_modules/zx/node_modules/@types/node": { + "version": "16.11.28", + "dev": true, + "license": "MIT" + }, + "node_modules/zx/node_modules/ansi-styles": { "version": "4.3.0", "dev": true, "license": "MIT", @@ -22219,7 +21887,18 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/yeoman-generator/node_modules/chalk": { + "node_modules/zx/node_modules/array-union": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zx/node_modules/chalk": { "version": "4.1.2", "dev": true, "license": "MIT", @@ -22234,7 +21913,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/yeoman-generator/node_modules/color-convert": { + "node_modules/zx/node_modules/color-convert": { "version": "2.0.1", "dev": true, "license": "MIT", @@ -22245,67 +21924,42 @@ "node": ">=7.0.0" } }, - "node_modules/yeoman-generator/node_modules/color-name": { + "node_modules/zx/node_modules/color-name": { "version": "1.1.4", "dev": true, "license": "MIT" }, - "node_modules/yeoman-generator/node_modules/execa": { - "version": "5.1.1", + "node_modules/zx/node_modules/globby": { + "version": "12.2.0", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/yeoman-generator/node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yeoman-generator/node_modules/is-stream": { - "version": "2.0.1", + "node_modules/zx/node_modules/slash": { + "version": "4.0.0", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yeoman-generator/node_modules/npm-run-path": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yeoman-generator/node_modules/supports-color": { + "node_modules/zx/node_modules/supports-color": { "version": "7.2.0", "dev": true, "license": "MIT", @@ -22316,406 +21970,455 @@ "node": ">=8" } }, - "node_modules/yn": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yosay": { - "version": "2.0.2", - "dev": true, - "license": "BSD-2-Clause", + "packages/artillery": { + "version": "2.0.0-38", + "license": "MPL-2.0", "dependencies": { - "ansi-regex": "^2.0.0", - "ansi-styles": "^3.0.0", - "chalk": "^1.0.0", - "cli-boxes": "^1.0.0", - "pad-component": "0.0.1", - "string-width": "^2.0.0", - "strip-ansi": "^3.0.0", - "taketalk": "^1.0.0", - "wrap-ansi": "^2.0.0" + "@artilleryio/int-commons": "*", + "@artilleryio/int-core": "*", + "@aws-sdk/credential-providers": "^3.127.0", + "@oclif/core": "^2.8.11", + "@oclif/plugin-help": "^5.2.11", + "@oclif/plugin-not-found": "^2.3.1", + "archiver": "^5.3.1", + "artillery-engine-playwright": "*", + "artillery-plugin-apdex": "*", + "artillery-plugin-ensure": "*", + "artillery-plugin-expect": "*", + "artillery-plugin-metrics-by-endpoint": "*", + "artillery-plugin-publish-metrics": "*", + "async": "^2.6.4", + "aws-sdk": "^2.1338.0", + "chalk": "^2.4.2", + "ci-info": "^3.8.0", + "cli-table3": "^0.6.0", + "cross-spawn": "^7.0.3", + "csv-parse": "^4.16.3", + "debug": "^4.3.1", + "dependency-tree": "^6.1.0", + "detective": "^5.1.0", + "dotenv": "^16.0.1", + "eventemitter3": "^4.0.4", + "fs-extra": "^10.1.0", + "ip": "^1.1.8", + "is-builtin-module": "^2.0.0", + "joi": "^17.6.0", + "js-yaml": "^3.13.1", + "jsonwebtoken": "^9.0.1", + "lodash": "^4.17.19", + "moment": "^2.29.4", + "nanoid": "^3.3.4", + "ora": "^4.0.4", + "posthog-node": "^2.2.3", + "sqs-consumer": "5.8.0", + "temp": "^0.9.4", + "tmp": "0.2.1", + "try-require": "^1.2.1", + "walk-sync": "^0.2.3", + "yaml-js": "^0.2.3" }, "bin": { - "yosay": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/yosay/node_modules/chalk": { - "version": "1.1.3", - "dev": true, - "license": "MIT", - "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" + "artillery": "bin/run" + }, + "devDependencies": { + "@hapi/hapi": "^20.1.3", + "eslint": "^8.6.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "execa": "^0.10.0", + "get-bin-path": "^5.1.0", + "rewiremock": "^3.14.3", + "sinon": "^4.5.0", + "tap": "^16.3.7", + "zx": "^4.2.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yosay/node_modules/chalk/node_modules/ansi-styles": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": ">= 18.16.1" } }, - "node_modules/yosay/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "dev": true, + "packages/artillery-cli": { + "name": "oclif-hello-world", + "version": "0.0.0", "license": "MIT", + "dependencies": { + "@oclif/core": "^2", + "@oclif/plugin-help": "^5", + "@oclif/plugin-plugins": "^2.4.2" + }, + "bin": { + "oex": "bin/run" + }, + "devDependencies": { + "@oclif/test": "^2.3.10", + "@types/chai": "^4", + "@types/mocha": "^9.0.0", + "@types/node": "^16.18.16", + "chai": "^4", + "eslint": "^7.32.0", + "eslint-config-oclif": "^4", + "eslint-config-oclif-typescript": "^1.0.3", + "mocha": "^9", + "oclif": "^3", + "shx": "^0.3.3", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "typescript": "^4.9.5" + }, "engines": { - "node": ">=4" + "node": ">=12.0.0" } }, - "node_modules/yosay/node_modules/string-width": { - "version": "2.1.1", + "packages/artillery-cli/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, - "license": "MIT", "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" + "@babel/highlight": "^7.10.4" } }, - "node_modules/yosay/node_modules/string-width/node_modules/ansi-regex": { - "version": "3.0.1", + "packages/artillery-cli/node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, - "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=4" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/yosay/node_modules/string-width/node_modules/strip-ansi": { - "version": "4.0.0", + "packages/artillery-cli/node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-regex": "^3.0.0" + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=4" + "node": ">=10.10.0" } }, - "node_modules/yosay/node_modules/strip-ansi": { - "version": "3.0.1", - "dev": true, - "license": "MIT", + "packages/artillery-cli/node_modules/@oclif/core": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.15.0.tgz", + "integrity": "sha512-fNEMG5DzJHhYmI3MgpByTvltBOMyFcnRIUMxbiz2ai8rhaYgaTHMG3Q38HcosfIvtw9nCjxpcQtC8MN8QtVCcA==", "dependencies": { - "ansi-regex": "^2.0.0" + "@types/cli-progress": "^3.11.0", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.12.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=14.0.0" } }, - "node_modules/yosay/node_modules/supports-color": { - "version": "2.0.0", + "packages/artillery-cli/node_modules/@types/node": { + "version": "16.18.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.60.tgz", + "integrity": "sha512-ZUGPWx5vKfN+G2/yN7pcSNLkIkXEvlwNaJEd4e0ppX7W2S8XAkdc/37hM4OUNJB9sa0p12AOvGvxL4JCPiz9DA==", + "dev": true + }, + "packages/artillery-cli/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, - "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=0.8.0" + "node": ">=0.4.0" } }, - "node_modules/yosay/node_modules/wrap-ansi": { - "version": "2.1.0", - "dev": true, - "license": "MIT", + "packages/artillery-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/yosay/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "dev": true, - "license": "MIT", + "packages/artillery-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "number-is-nan": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/yosay/node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "dev": true, - "license": "MIT", + "packages/artillery-cli/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/zip-stream": { - "version": "4.1.0", - "license": "MIT", + "packages/artillery-cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "archiver-utils": "^2.1.0", - "compress-commons": "^4.1.0", - "readable-stream": "^3.6.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 10" + "node": ">=7.0.0" } }, - "node_modules/zip-stream/node_modules/readable-stream": { - "version": "3.6.0", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, + "packages/artillery-cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "packages/artillery-cli/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/zx": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/zx/-/zx-4.3.0.tgz", - "integrity": "sha512-KuEjpu5QFIMx0wWfzknDRhY98s7a3tWNRmYt19XNmB7AfOmz5zISA4+3Q8vlJc2qguxMn89uSxhPDCldPa3YLA==", + "packages/artillery-cli/node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "dependencies": { - "@types/fs-extra": "^9.0.12", - "@types/minimist": "^1.2.2", - "@types/node": "^16.6", - "@types/node-fetch": "^2.5.12", - "chalk": "^4.1.2", - "fs-extra": "^10.0.0", - "globby": "^12.0.1", - "minimist": "^1.2.5", - "node-fetch": "^2.6.1", - "ps-tree": "^1.2.0", - "which": "^2.0.2" + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" }, "bin": { - "zx": "zx.mjs" + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 14.13.1" + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/zx/node_modules/@types/node": { - "version": "16.11.28", + "packages/artillery-cli/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "license": "MIT" + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } }, - "node_modules/zx/node_modules/ansi-styles": { - "version": "4.3.0", + "packages/artillery-cli/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, - "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "eslint-visitor-keys": "^1.1.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/zx/node_modules/array-union": { - "version": "3.0.1", + "packages/artillery-cli/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/zx/node_modules/chalk": { - "version": "4.1.2", + "packages/artillery-cli/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/zx/node_modules/color-convert": { - "version": "2.0.1", + "packages/artillery-cli/node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, - "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" }, "engines": { - "node": ">=7.0.0" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/zx/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/zx/node_modules/globby": { - "version": "12.2.0", + "packages/artillery-cli/node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^3.0.1", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.7", - "ignore": "^5.1.9", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/zx/node_modules/slash": { - "version": "4.0.0", + "packages/artillery-cli/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.0" } }, - "node_modules/zx/node_modules/supports-color": { - "version": "7.2.0", + "packages/artillery-cli/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "packages/artillery": { - "version": "2.0.0-38", - "license": "MPL-2.0", - "dependencies": { - "@artilleryio/int-commons": "*", - "@artilleryio/int-core": "*", - "@artilleryio/platform-fargate": "^2.2.0", - "@aws-sdk/credential-providers": "^3.127.0", - "@oclif/core": "^2.8.11", - "@oclif/plugin-help": "^5.2.11", - "@oclif/plugin-not-found": "^2.3.1", - "archiver": "^5.3.1", - "artillery-engine-playwright": "*", - "artillery-plugin-apdex": "*", - "artillery-plugin-ensure": "*", - "artillery-plugin-expect": "*", - "artillery-plugin-metrics-by-endpoint": "*", - "artillery-plugin-publish-metrics": "*", - "async": "^2.6.4", - "aws-sdk": "^2.1338.0", - "chalk": "^2.4.2", - "ci-info": "^3.8.0", - "cli-table3": "^0.6.2", - "cross-spawn": "^7.0.3", - "csv-parse": "^4.16.3", - "debug": "^4.3.1", - "dotenv": "^16.0.1", - "eventemitter3": "^4.0.4", - "fs-extra": "^10.1.0", - "ip": "^1.1.8", - "joi": "^17.6.0", - "js-yaml": "^3.13.1", - "lodash": "^4.17.19", - "moment": "^2.29.4", - "nanoid": "^3.3.4", - "ora": "^4.0.4", - "posthog-node": "^2.2.3", - "sqs-consumer": "5.8.0", - "temp": "^0.9.4", - "tmp": "0.2.1", - "try-require": "^1.2.1" - }, - "bin": { - "artillery": "bin/run" - }, - "devDependencies": { - "@hapi/hapi": "^20.1.3", - "eslint": "^8.6.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^4.0.0", - "execa": "^0.10.0", - "get-bin-path": "^5.1.0", - "rewiremock": "^3.14.3", - "sinon": "^4.5.0", - "tap": "^16.3.7", - "zx": "^4.2.0" - }, + "packages/artillery-cli/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, "engines": { - "node": ">= 18.16.1" + "node": ">= 4" } }, - "packages/artillery-cli": { - "name": "oclif-hello-world", - "version": "0.0.0", - "extraneous": true, - "license": "MIT", + "packages/artillery-cli/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dependencies": { - "@oclif/core": "^2", - "@oclif/plugin-help": "^5", - "@oclif/plugin-plugins": "^2.4.2" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, - "bin": { - "oex": "bin/run" + "engines": { + "node": ">=10" }, - "devDependencies": { - "@oclif/test": "^2.3.10", - "@types/chai": "^4", - "@types/mocha": "^9.0.0", - "@types/node": "^16.18.16", - "chai": "^4", - "eslint": "^7.32.0", - "eslint-config-oclif": "^4", - "eslint-config-oclif-typescript": "^1.0.3", - "mocha": "^9", - "oclif": "^3", - "shx": "^0.3.3", - "ts-node": "^10.9.1", - "tslib": "^2.5.0", - "typescript": "^4.9.5" + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "packages/artillery-cli/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "packages/artillery-engine-playwright": { @@ -23741,6 +23444,29 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "packages/artillery/node_modules/builtin-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-2.0.0.tgz", + "integrity": "sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg==", + "engines": { + "node": ">=4" + } + }, + "packages/artillery/node_modules/cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, "packages/artillery/node_modules/color-convert": { "version": "2.0.1", "license": "MIT", @@ -23755,6 +23481,26 @@ "version": "1.1.4", "license": "MIT" }, + "packages/artillery/node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "packages/artillery/node_modules/is-builtin-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-2.0.0.tgz", + "integrity": "sha512-G2jLHphOywpgrL/AaJKWDXpdpGR9X4V1PCkB+EwG5Z28z8EukgdWnAUFAS2wdBtIpwHhHBIiq0NBOWEbSXN0Rg==", + "dependencies": { + "builtin-modules": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "packages/artillery/node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", diff --git a/packages/artillery/lib/cmds/run-fargate.js b/packages/artillery/lib/cmds/run-fargate.js index 86669bb961..26dece8bb3 100644 --- a/packages/artillery/lib/cmds/run-fargate.js +++ b/packages/artillery/lib/cmds/run-fargate.js @@ -6,8 +6,7 @@ const { Command, Flags, Args } = require('@oclif/core'); const telemetry = require('../telemetry').init(); const { Plugin: CloudPlugin } = require('../platform/cloud/cloud'); -const tryRequire = require('try-require'); -const PlatformFargateLegacy = tryRequire('@artilleryio/platform-fargate'); +const runCluster = require('../platform/aws-ecs/legacy/run-cluster'); const PlatformECS = require('../platform/aws-ecs/ecs'); const { ECS_WORKER_ROLE_NAME } = require('../platform/aws/constants'); @@ -18,6 +17,11 @@ class RunCommand extends Command { async run() { const { flags, _argv, args } = await this.parse(RunCommand); + + flags['platform-opt'] = [`region=${flags.region}`]; + + flags.platform = 'aws:ecs'; + new CloudPlugin(null, null, { flags }); const ECS = new PlatformECS(null, null, {}, { testRunId: 'foo' }); @@ -34,14 +38,105 @@ class RunCommand extends Command { }); // Delegate the rest to existing implementation: - PlatformFargateLegacy.commands.runCluster(args.script, flags); + runCluster(args.script, flags); } } -if (PlatformFargateLegacy) { - RunCommand.description = PlatformFargateLegacy.oclif.runTest.description; - RunCommand.flags = PlatformFargateLegacy.oclif.runTest.flags; - RunCommand.args = PlatformFargateLegacy.oclif.runTest.args; -} +const runTestDescriptions = { + count: 'Number of load generator workers to launch', + cluster: 'Name of the Fargate/ECS cluster to run the test on', + region: 'The AWS region to run in', + packages: + 'Path to package.json file which lists dependencies for the test script', + maxDuration: 'Maximum duration of the test run', + dotenv: 'Path to a .env file to load environment variables from' +}; + +RunCommand.description = `launch a test using AWS ECS/Fargate + +Examples: + + To launch a test with 10 load generating workers using AWS Fargate in us-east-1: + + $ artillery run:fargate --count 10 --region us-east-1 my-test.yml +`; + +RunCommand.flags = { + count: Flags.integer({ + description: runTestDescriptions.count + }), + cluster: Flags.string({ + description: runTestDescriptions.cluster + }), + region: Flags.string({ + char: 'r', + description: runTestDescriptions.region + }), + secret: Flags.string({ + multiple: true + }), + // TODO: Descriptions + 'launch-type': Flags.string({}), + 'launch-config': Flags.string({}), + 'subnet-ids': Flags.string({}), + 'security-group-ids': Flags.string({}), + 'task-role-name': Flags.string({}), + target: Flags.string({ + char: 't', + description: + 'Set target endpoint. Overrides the target already set in the test script' + }), + output: Flags.string({ + char: 'o', + description: 'Write a JSON report to file' + }), + insecure: Flags.boolean({ + char: 'k', + description: 'Allow insecure TLS connections; do not use in production' + }), + environment: Flags.string({ + char: 'e', + description: 'Use one of the environments specified in config.environments' + }), + config: Flags.string({ + description: 'Read configuration for the test from the specified file' + }), + 'scenario-name': Flags.string({ + description: 'Name of the specific scenario to run' + }), + overrides: Flags.string({ + description: 'Dynamically override values in the test script; a JSON object' + }), + input: Flags.string({ + char: 'i', + description: 'Input script file', + multiple: true, + hidden: true + }), + tags: Flags.string({ + description: + 'Comma-separated list of tags in key:value format to tag the test run, for example: --tags team:sre,service:foo' + }), + note: Flags.string({}), // TODO: description + packages: Flags.string({ + description: runTestDescriptions.packages + }), + 'max-duration': Flags.string({ + description: runTestDescriptions.maxDuration + }), + dotenv: Flags.string({ + description: runTestDescriptions.dotenv + }), + record: Flags.boolean({ + description: 'Record test run to Artillery Cloud' + }), + key: Flags.string({ + description: 'API key for Artillery Cloud' + }) +}; + +RunCommand.args = { + script: Args.string() +}; module.exports = RunCommand; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/aws-util.js b/packages/artillery/lib/platform/aws-ecs/legacy/aws-util.js new file mode 100644 index 0000000000..8c55daf733 --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/aws-util.js @@ -0,0 +1,126 @@ +const AWS = require('aws-sdk'); +const debug = require('debug')('util'); + +module.exports = { + // ECS: + ecsDescribeTasks, + + // AWS SSM: + ensureParameterExists, + parameterExists, + putParameter, + getParameter +}; + +// Wraps ecs.describeTasks to support more than 100 task ARNs in params.tasks +async function ecsDescribeTasks(params, ecs) { + const taskArnChunks = splitIntoSublists(params.tasks, 100); + const results = { tasks: [], failures: [] }; + for (let i = 0; i < taskArnChunks.length; i++) { + const params2 = Object.assign({}, params, { tasks: taskArnChunks[i] }); + try { + const ecsData = await ecs.describeTasks(params2).promise(); + results.tasks = results.tasks.concat(ecsData.tasks); + results.failures = results.failures.concat(ecsData.failures); + } catch (err) { + throw err; + } + } + return results; +} + +// Slice input list into several lists, where each list has no more than maxGroupSize elements +function splitIntoSublists(list, maxGroupSize) { + const result = []; + const numGroups = Math.ceil(list.length / maxGroupSize); + for (let i = 0; i < numGroups; i++) { + result.push(list.slice(i * maxGroupSize, i * maxGroupSize + maxGroupSize)); + } + return result; +} + +// ******************** +// AWS SSM helpers +// In future these will be parameter-store agnostic, and work with Kubernetes +// ConfigMaps or Azure/GCP native equivalents. +// ******************** + +// If parameter exists, do nothing; otherwise set the value +async function ensureParameterExists(ssmPath, defaultValue, type, region) { + if (region) AWS.config.update({ region }); + + try { + const exists = await parameterExists(ssmPath); + if (exists) { + return Promise.resolve(); + } + return putParameter(ssmPath, defaultValue, type); + } catch (err) { + return Promise.reject(err); + } +} + +async function parameterExists(path, region) { + if (region) AWS.config.update({ region }); + const ssm = new AWS.SSM({ apiVersion: '2014-11-06' }); + const getParams = { + Name: path, + WithDecryption: true + }; + + try { + const ssmResponse = await ssm.getParameter(getParams).promise(); + return Promise.resolve(true); + } catch (ssmErr) { + if (ssmErr.code === 'ParameterNotFound') { + return Promise.resolve(false); + } else { + return Promise.reject(ssmErr); + } + } +} + +async function putParameter(path, value, type, region) { + if (region) AWS.config.update({ region }); + const ssm = new AWS.SSM({ apiVersion: '2014-11-06' }); + + const putParams = { + Name: path, + Type: type, + Value: value, + Overwrite: true + }; + + try { + const ssmResponse = await ssm.putParameter(putParams).promise(); + return Promise.resolve(); + } catch (ssmErr) { + return Promise.reject(ssmErr); + } +} + +async function getParameter(path, region) { + if (region) { + AWS.config.update({ region }); + } + + const ssm = new AWS.SSM({ apiVersion: '2014-11-06' }); + + try { + const ssmResponse = await ssm + .getParameter({ + Name: path, + WithDecryption: true + }) + .promise(); + + debug({ ssmResponse }); + return ssmResponse.Parameter && ssmResponse.Parameter.Value; + } catch (ssmErr) { + if (ssmErr.code === 'ParameterNotFound') { + return false; + } else { + throw ssmErr; + } + } +} diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/bom.js b/packages/artillery/lib/platform/aws-ecs/legacy/bom.js new file mode 100644 index 0000000000..d436e0405a --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/bom.js @@ -0,0 +1,447 @@ +const path = require('path'); +const fs = require('fs'); +const A = require('async'); + +const isBuiltinModule = require('is-builtin-module'); +const detective = require('detective'); +const depTree = require('dependency-tree'); + +const walkSync = require('walk-sync'); +const debug = require('debug')('bom'); +const _ = require('lodash'); +const BUILTIN_PLUGINS = require('./plugins').getAllPluginNames(); +const BUILTIN_ENGINES = require('./plugins').getOfficialEngines(); + +const Table = require('cli-table3'); + +// NOTE: Code below presumes that all paths are absolute + +function createBOM(absoluteScriptPath, extraFiles, opts, callback) { + A.waterfall( + [ + A.constant(absoluteScriptPath), + global.artillery.__util.readScript, + global.artillery.__util.parseScript, + (scriptData, next) => { + return next(null, { + opts: { + scriptData, + absoluteScriptPath + }, + localFilePaths: [absoluteScriptPath], + npmModules: [] + }); + }, + getPlugins, + getCustomEngines, + getCustomJsDependencies, + getVariableDataFiles, + getFileUploadPluginFiles, + getExtraFiles, + expandDirectories + ], + + function (err, context) { + if (err) { + return callback(err, null); + } + + context.localFilePaths = context.localFilePaths.concat(extraFiles); + + // TODO: Entries in localFilePaths may be directories + + // How many entries do we have here? If we have only one entry, the string itself + // will be the common prefix, meaning that when we substring() on it later, we'll + // get an empty string, ending up with a manifest like: + // { files: + // [ { orig: '/Users/h/tmp/artillery/hello.yaml', noPrefix: '' } ], + // modules: [] } + // + let prefix = ''; + if (context.localFilePaths.length === 1) { + prefix = context.localFilePaths[0].substring( + 0, + context.localFilePaths[0].length - + path.basename(context.localFilePaths[0]).length + ); + // This may still be an empty string if the script path is just 'hello.yml': + prefix = prefix.length === 0 ? context.localFilePaths[0] : prefix; + } else { + prefix = commonPrefix(context.localFilePaths); + } + + debug('prefix', prefix); + + // + // include package.json / package-lock.json / yarn.lock + // + let packageDescriptionFiles = ['.npmrc']; + if (opts.packageJsonPath) { + packageDescriptionFiles.push(opts.packageJsonPath); + } else { + packageDescriptionFiles = packageDescriptionFiles.concat([ + 'package.json', + 'package-lock.json', + 'yarn.lock' + ]); + } + const dependencyFiles = packageDescriptionFiles.map((s) => + path.join(prefix, s) + ); + + debug(dependencyFiles); + + dependencyFiles.forEach(function (p) { + try { + if (fs.statSync(p)) { + context.localFilePaths.push(p); + } + } catch (ignoredErr) {} + }); + + const files = context.localFilePaths.map((p) => { + return { orig: p, noPrefix: p.substring(prefix.length, p.length) }; + }); + + const pkgPath = _.find(files, (f) => { + return f.noPrefix === 'package.json'; + }); + + if (pkgPath) { + const pkg = JSON.parse(fs.readFileSync(pkgPath.orig, 'utf8')); + const pkgDeps = [].concat( + Object.keys(pkg.dependencies || {}), + Object.keys(pkg.devDependencies || {}) + ); + context.pkgDeps = pkgDeps; + context.npmModules = _.uniq(context.npmModules.concat(pkgDeps)).sort(); + } else { + context.pkgDeps = []; + } + + return callback(null, { + files: _.uniqWith(files, _.isEqual), + modules: _.uniq(context.npmModules), + pkgDeps: context.pkgDeps + }); + } + ); +} + +function isLocalModule(modName) { + // NOTE: Absolute paths not supported + return modName.startsWith('.'); +} + +function getPlugins(context, next) { + let environmentPlugins = _.reduce( + _.get(context, 'opts.scriptData.config.environments', {}), + function getEnvironmentPlugins(acc, envSpec, envName) { + acc = acc.concat(Object.keys(envSpec.plugins || [])); + return acc; + }, + [] + ); + const pluginNames = Object.keys( + _.get(context, 'opts.scriptData.config.plugins', {}) + ).concat(environmentPlugins); + + const pluginPackages = _.uniq( + pluginNames + .filter((p) => BUILTIN_PLUGINS.indexOf(p) === -1) + .map((p) => `artillery-plugin-${p}`) + ); + debug(pluginPackages); + context.npmModules = context.npmModules.concat(pluginPackages); + + return next(null, context); +} + +function getCustomEngines(context, next) { + let environmentEngines = _.reduce( + _.get(context, 'opts.scriptData.config.environments', {}), + function getEnvironmentEngines(acc, envSpec, envName) { + acc = acc.concat(Object.keys(envSpec.engines || [])); + return acc; + }, + [] + ); + + const engineNames = Object.keys( + _.get(context, 'opts.scriptData.config.engines', {}) + ).concat(environmentEngines); + + const enginePackages = _.uniq( + engineNames + .filter((p) => BUILTIN_ENGINES.indexOf(p) === -1) + .map((p) => `artillery-engine-${p}`) + ); + + context.npmModules = context.npmModules.concat(enginePackages); + + return next(null, context); +} + +function getCustomJsDependencies(context, next) { + if ( + context.opts.scriptData.config && + context.opts.scriptData.config.processor + ) { + // + // Path to the main processor file: + // + const procPath = path.resolve( + path.dirname(context.opts.absoluteScriptPath), + context.opts.scriptData.config.processor + ); + context.localFilePaths.push(procPath); + + // Get the tree of requires from the main processor file: + const tree = depTree.toList({ + filename: procPath, + directory: path.dirname(context.opts.absoluteScriptPath), + filter: (path) => path.indexOf('node_modules') === -1 // optional + }); + + debug('tree'); + debug(tree); + + function getNpmDependencies(filename) { + const src = fs.readFileSync(filename); + const requires = detective(src); + const npmPackages = requires + .filter( + (requireString) => + !isBuiltinModule(requireString) && !isLocalModule(requireString) + ) + .map((requireString) => { + return requireString.startsWith('@') + ? requireString.split('/')[0] + '/' + requireString.split('/')[1] + : requireString.split('/')[0]; + }); + return npmPackages; + } + + const allNpmDeps = tree.map(getNpmDependencies); + debug(allNpmDeps); + const reduced = allNpmDeps.reduce((acc, deps) => { + deps.forEach((d) => { + if (acc.findIndex((x) => x === d) === -1) { + acc.push(d); + } + }); + return acc; + }, []); + debug(reduced); + + // + // Any other local JS files and npm packages: + // + const procSrc = fs.readFileSync(procPath); + const processorRequires = detective(procSrc); + // TODO: Look for and load dir/index.js and get its dependencies, + // rather than just grabbing the entire directory. + // NOTE: Some of these may be directories (with an index.js inside) + // Could be JSON files too. + context.localFilePaths = context.localFilePaths.concat(tree); + context.npmModules = context.npmModules.concat(reduced); + debug('got custom JS dependencies'); + return next(null, context); + } else { + debug('no custom JS dependencies'); + return next(null, context); + } +} + +function getVariableDataFiles(context, next) { + // NOTE: assuming that context.opts.scriptData contains both the config and + // the scenarios section here. + + // Iterate over environments + + function resolvePayloadPaths(obj) { + let result = []; + if (obj.payload) { + if (_.isArray(obj.payload)) { + obj.payload.forEach((payloadSpec) => { + result.push( + path.resolve( + path.dirname(context.opts.absoluteScriptPath), + payloadSpec.path + ) + ); + }); + } else if (_.isObject(obj.payload)) { + // isObject returns true for arrays, so this branch must come second + result.push( + path.resolve( + path.dirname(context.opts.absoluteScriptPath), + obj.payload.path + ) + ); + } + } + return result; + } + + context.localFilePaths = context.localFilePaths.concat( + resolvePayloadPaths(context.opts.scriptData.config) + ); + context.opts.scriptData.config.environments = + context.opts.scriptData.config.environments || {}; + Object.keys(context.opts.scriptData.config.environments).forEach( + (envName) => { + const envSpec = context.opts.scriptData.config.environments[envName]; + context.localFilePaths = context.localFilePaths.concat( + resolvePayloadPaths(envSpec) + ); + } + ); + + return next(null, context); +} + +function getFileUploadPluginFiles(context, next) { + if ( + context.opts.scriptData.config && + context.opts.scriptData.config.plugins && + context.opts.scriptData.config.plugins['http-file-uploads'] + ) { + // Append filePaths array if it's there: + + if (context.opts.scriptData.config.plugins['http-file-uploads'].filePaths) { + const absPaths = context.opts.scriptData.config.plugins[ + 'http-file-uploads' + ].filePaths.map((p) => { + return path.resolve(path.dirname(context.opts.absoluteScriptPath), p); + }); + context.localFilePaths = context.localFilePaths.concat(absPaths); + } + return next(null, context); + } else { + return next(null, context); + } +} + +function getExtraFiles(context, next) { + if ( + context.opts.scriptData.config && + context.opts.scriptData.config.includeFiles + ) { + const absPaths = _.map(context.opts.scriptData.config.includeFiles, (p) => { + const includePath = path.resolve( + path.dirname(context.opts.absoluteScriptPath), + p + ); + debug('includeFile:', includePath); + return includePath; + }); + context.localFilePaths = context.localFilePaths.concat(absPaths); + return next(null, context); + } else { + return next(null, context); + } +} + +function expandDirectories(context, next) { + // This can potentially lead to VERY unexpected behaviour, when used + // without due care with the file upload plugin (if filePaths is pointed at + // a directory that contains files OTHER than those to be used with the + // plugin) + // + // TODO: Warn if there are too many files in the directory + // TODO: Only allow specific filenames or globs, not directories + debug(context.localFilePaths); + // FIXME: Don't need to scan twice: + const dirs = context.localFilePaths.filter((p) => { + let result = false; + try { + result = fs.statSync(p).isDirectory(); + } catch (fsErr) {} + return result; + }); + // Remove directories from the list: + context.localFilePaths = context.localFilePaths.filter((p) => { + let result = true; + try { + result = !fs.statSync(p).isDirectory(); + } catch (fsErr) {} + return result; + }); + + debug('Dirs to expand'); + debug(dirs); + dirs.forEach((d) => { + const entries = walkSync.entries(d, { directories: false }); + debug(entries); + context.localFilePaths = context.localFilePaths.concat( + entries.map((e) => { + return path.resolve(d, e.relativePath); + }) + ); + }); + + return next(null, context); +} + +function commonPrefix(paths, separator) { + if ( + !paths || + paths.length === 0 || + paths.filter((s) => typeof s !== 'string').length > 0 + ) { + return ''; + } + + if (paths.includes('/')) { + return '/'; + } + + const sep = separator ? separator : path.sep; + + const splitPaths = paths.map((p) => p.split(sep)); + const shortestPath = splitPaths.reduce((a, b) => { + return a.length < b.length ? a : b; + }, splitPaths[0]); + + let furthestIndex = shortestPath.length; + + for (const p of splitPaths) { + for (let i = 0; i < furthestIndex; i++) { + if (p[i] !== shortestPath[i]) { + furthestIndex = i; + break; + } + } + } + + const joined = shortestPath.slice(0, furthestIndex).join(sep); + + if (joined.length > 0) { + // Check if joined path already ends with separator which + // will happen when input is a root drive on Windows, e.g. "C:\" + return joined.endsWith(sep) ? joined : joined + sep; + } else { + return ''; + } +} + +function prettyPrint(manifest) { + artillery.logger({ showTimestamp: true }).log('Test bundle prepared...'); + artillery.log('Test bundle contents:'); + const t = new Table({ head: ['Name', 'Type', 'Notes'] }); + for (const f of manifest.files) { + t.push([f.noPrefix, 'file']); + } + for (const m of manifest.modules) { + t.push([ + m, + 'package', + manifest.pkgDeps.indexOf(m) === -1 ? 'not in package.json' : '' + ]); + } + artillery.log(t.toString()); + artillery.log(); +} + +module.exports = { createBOM, commonPrefix, prettyPrint }; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/constants.js b/packages/artillery/lib/platform/aws-ecs/legacy/constants.js new file mode 100644 index 0000000000..3e7b355c4c --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/constants.js @@ -0,0 +1,15 @@ +const DEFAULT_IMAGE_TAG = '1f676ad7a2ecc923e813cdc7ac1bf4a2328daec0'; + +module.exports = { + ARTILLERY_CLUSTER_NAME: 'artilleryio-cluster', + TASK_NAME: 'artilleryio-loadgen-worker', + SQS_QUEUES_NAME_PREFIX: 'artilleryio_test_metrics', + S3_BUCKET_NAME_PREFIX: 'artilleryio-test-data', + LOGGROUP_NAME: 'artilleryio-log-group', + IMAGE_VERSION: process.env.ECR_IMAGE_VERSION || DEFAULT_IMAGE_TAG, + WAIT_TIMEOUT: + typeof process.env.ECS_WAIT_TIMEOUT === 'undefined' + ? 600 + : parseInt(process.env.ECS_WAIT_TIMEOUT, 10), + TEST_RUNS_MAX_TAGS: parseInt(process.env.TEST_RUNS_MAX_TAGS, 10) || 8 +}; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/create-s3-client.js b/packages/artillery/lib/platform/aws-ecs/legacy/create-s3-client.js new file mode 100644 index 0000000000..542df73857 --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/create-s3-client.js @@ -0,0 +1,21 @@ +const S3 = require('aws-sdk/clients/s3'); + +module.exports = createS3Client; + +function createS3Client(opts) { + let defaultOpts = { + apiVersion: '2006-03-01' + }; + + defaultOpts = Object.assign(defaultOpts, opts); + + if (process.env.ARTILLERY_S3_OPTS) { + defaultOpts = Object.assign( + defaultOpts, + JSON.parse(process.env.ARTILLERY_S3_OPTS) + ); + } + + const s3 = new S3(defaultOpts); + return s3; +} diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/create-test.js b/packages/artillery/lib/platform/aws-ecs/legacy/create-test.js new file mode 100644 index 0000000000..b0be8b8fb7 --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/create-test.js @@ -0,0 +1,203 @@ +'use strict'; + +const A = require('async'); +const debug = require('debug')('commands:create-test'); + +const util = require('./util'); +const { getBucketName } = require('./util'); +const createS3Client = require('./create-s3-client'); +const setDefaultAWSCredentials = require('../../aws/aws-set-default-credentials'); + +const path = require('path'); + +const fs = require('fs'); +const _ = require('lodash'); + +const chalk = require('chalk'); + +const { createBOM, prettyPrint } = require('./bom'); + +function tryCreateTest(scriptPath, options) { + createTest(scriptPath, options); +} + +async function createTest(scriptPath, options, callback) { + const absoluteScriptPath = path.resolve(process.cwd(), scriptPath); + + const contextPath = options.context + ? path.resolve(options.context) + : path.dirname(absoluteScriptPath); + + debug('script:', absoluteScriptPath); + debug('root:', contextPath); + + let context = { + contextDir: contextPath, + scriptPath: absoluteScriptPath, + originalScriptPath: scriptPath, + name: options.name, // test name, eg simple-bom or aht_$UUID + manifestPath: options.manifestPath, + packageJsonPath: options.packageJsonPath + }; + + if (typeof options.config === 'string') { + const absoluteConfigPath = path.resolve(process.cwd(), options.config); + context.configPath = absoluteConfigPath; + } + + await setDefaultAWSCredentials(); + + A.waterfall( + [ + A.constant(context), + async function (context) { + context.s3Bucket = await getBucketName(); + return context; + }, + prepareManifest, + printManifest, + syncS3, + writeTestMetadata + ], + function (err, context) { + if (err) { + console.log(err); + return; + } + + callback(err, context); + } + ); +} + +function prepareManifest(context, callback) { + let fileToAnalyse = context.scriptPath; + let extraFiles = []; + if (context.configPath) { + debug('context has been provided; extraFiles =', extraFiles); + fileToAnalyse = context.configPath; + extraFiles.push(context.scriptPath); + } + + createBOM( + fileToAnalyse, + extraFiles, + { packageJsonPath: context.packageJsonPath }, + (err, bom) => { + debug(err); + debug(bom); + context.manifest = bom; + return callback(err, context); + } + ); +} + +function printManifest(context, callback) { + prettyPrint(context.manifest); + return callback(null, context); +} + +function syncS3(context, callback) { + const plainS3 = createS3Client(); + const prefix = `tests/${context.name}`; + + context.s3Prefix = prefix; + + debug('Will try syncing to:', context.s3Bucket); + + debug('Manifest: ', context.manifest); + + // Iterate through manifest, for each element: has orig (local source) and noPrefix (S3 + // destination) properties + A.eachLimit( + context.manifest.files, + 3, + (item, eachDone) => { + // If we can't read the file, it may have been specified with a + // template in its name, e.g. a payload file like: + // {{ $environment }}-users.csv + // If so, ignore it, hope config.includeFiles was used, and let + // "artillery run" in the worker deal with it. + let body; + try { + body = fs.readFileSync(item.orig); + } catch (fsErr) { + debug(fsErr); + } + + if (!body) { + return eachDone(null, context); + } + + const key = context.s3Prefix + '/' + item.noPrefix; + plainS3.putObject( + { + Bucket: context.s3Bucket, + Key: key, + // TODO: stream, not readFileSync + Body: body + }, + (s3Err, s3Resp) => { + if (s3Err) { + // TODO: retry if needed + return eachDone(s3Err, context); + } + debug(`Uploaded ${key}`); + return eachDone(null, context); + } + ); + }, + (bomUploadErr) => { + return callback(bomUploadErr, context); + } + ); +} + +// create just overwrites an existing test for now +function writeTestMetadata(context, callback) { + const metadata = { + createdOn: Date.now(), + name: context.name, + modules: context.manifest.modules + }; + + // Here we need to provide config information (if given) -- so that the worker knows how to load it + if (context.configPath) { + const res = context.manifest.files.filter((o) => { + return o.orig === context.configPath; + }); + const newConfigPath = res[0].noPrefix; // if we have been given a config, we must have an entry + metadata.configPath = newConfigPath; + } + + const newScriptPath = context.manifest.files.filter((o) => { + return o.orig === context.scriptPath; + })[0].noPrefix; + metadata.scriptPath = newScriptPath; + + debug('metadata', metadata); + + const s3 = createS3Client(); + const key = context.s3Prefix + '/metadata.json'; // TODO: Rename to something less likely to clash + debug('metadata location:', `${context.s3Bucket}/${key}`); + s3.putObject( + { + Body: JSON.stringify(metadata), + Bucket: context.s3Bucket, + Key: key + }, + function (s3Err, s3Resp) { + if (s3Err) { + return callback(s3Err, context); + } + return callback(null, context); + } + ); +} + +module.exports = { + tryCreateTest, + createTest, + syncS3, + prepareManifest +}; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/errors.js b/packages/artillery/lib/platform/aws-ecs/legacy/errors.js new file mode 100644 index 0000000000..edabe0cbde --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/errors.js @@ -0,0 +1,34 @@ +class TestNotFoundError extends Error { + constructor(message) { + super(message); + this.name = 'TestNotFoundError'; + } +} + +class NoAvailableQueueError extends Error { + constructor(message) { + super(message); + this.name = 'NoAvailableQueueError'; + } +} + +class ClientServerVersionMismatchError extends Error { + constructor(message) { + super(message); + this.name = 'ClientServerMismatchError'; + } +} + +class ConsoleOutputSerializeError extends Error { + constructor(message) { + super(message); + this.name = 'OutputSerializeError'; + } +} + +module.exports = { + TestNotFoundError, + NoAvailableQueueError, + ClientServerVersionMismatchError, + ConsoleOutputSerializeError +}; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/find-public-subnets.js b/packages/artillery/lib/platform/aws-ecs/legacy/find-public-subnets.js new file mode 100644 index 0000000000..630a3e749b --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/find-public-subnets.js @@ -0,0 +1,157 @@ +const assert = require('assert').strict; +const AWS = require('aws-sdk'); + +class VPCSubnetFinder { + constructor(opts) { + this.ec2 = new AWS.EC2(opts); + } + + async getRouteTables(vpcId) { + try { + const rts = await this.ec2 + .describeRouteTables({ + Filters: [ + { + Name: 'vpc-id', + Values: [vpcId] + } + ] + }) + .promise(); + + return rts.RouteTables; + } catch (err) { + throw err; + } + } + + async findDefaultVpc() { + try { + const vpcRes = await this.ec2 + .describeVpcs({ + Filters: [ + { + Name: 'isDefault', + Values: ['true'] + } + ] + }) + .promise(); + + assert.ok(vpcRes.Vpcs.length <= 1); + + if (vpcRes.Vpcs.length !== 1) { + return null; + } else { + return vpcRes.Vpcs[0].VpcId; + } + } catch (err) { + throw err; + } + } + + async getSubnets(vpcId) { + // Get subnets fileterd by VPC id + try { + const subRes = await this.ec2 + .describeSubnets({ + Filters: [ + { + Name: 'vpc-id', + Values: [vpcId] + } + ] + }) + .promise(); + + return subRes.Subnets; + } catch (err) { + throw err; + } + } + + isSubnetPublic(routeTables, subnetId) { + // + // Inspect associations of each route table (of a specific VPC). A route + // table record has an Associations field, which is a list of association + // objects. There are two types of those: + // + // 1. An implicit association, which is indicated by field Main set to + // true and no explicit subnet id. + // 2. An explicit association, which is indicated by field Main set to + // false, and a SubnetId field containing a subnet id. + // + + // Route table for the subnet - can there only be one? + let subnetTable = routeTables.filter((rt) => { + const explicitAssoc = rt.Associations.filter((assoc) => { + return assoc.SubnetId && assoc.SubnetId === subnetId; + }); + + assert.ok(explicitAssoc.length <= 1); + + return explicitAssoc.length === 1; + }); + + if (subnetTable.length === 0) { + // There is no explicit association for this subnet so it will be implicitly + // associated with the VPC's main routing table. + subnetTable = routeTables.filter((rt) => { + const implicitAssoc = rt.Associations.filter((assoc) => { + return assoc.Main === true; + }); + + assert.ok(implicitAssoc.length <= 1); + + return implicitAssoc.length === 1; + }); + } + + if (subnetTable.length !== 1) { + throw new Error( + `Could not locate routing table for subnet: subnet id: ${subnetId}` + ); + } + + const igwRoutes = subnetTable[0].Routes.filter((route) => { + // NOTE: there may be no IGW attached to route + return route.GatewayId && route.GatewayId.startsWith('igw-'); + }); + + return igwRoutes.length > 0; + } + + // TODO: Distinguish between there being no default VPC, + // or being given an invalid VPC ID, and no public subnets + // existing in a VPC that definitely exists. + async findPublicSubnets(vpcId) { + if (!vpcId) { + vpcId = await this.findDefaultVpc(); + } + const rts = await this.getRouteTables(vpcId); + const subnets = await this.getSubnets(vpcId); + + const publicSubnets = subnets.filter((subnet) => { + return this.isSubnetPublic(rts, subnet.SubnetId); + }); + + return publicSubnets; + } +} + +async function main() { + const f = new VPCSubnetFinder({ region: process.env.REGION }); + + try { + const publicSubnets = await f.findPublicSubnets(process.env.VPC_ID); + console.log(publicSubnets.map((s) => s.SubnetId).join('\n')); + } catch (err) { + console.log(err); + } +} + +if (require.main == module) { + main(); +} + +module.exports = { VPCSubnetFinder }; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/plugins.js b/packages/artillery/lib/platform/aws-ecs/legacy/plugins.js new file mode 100644 index 0000000000..143f79641d --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/plugins.js @@ -0,0 +1,21 @@ +module.exports.getAllPluginNames = function () { + return [...this.getOfficialPlugins(), ...this.getProPlugins()]; +}; + +module.exports.getOfficialPlugins = function () { + return [ + 'ensure', + 'expect', + 'metrics-by-endpoint', + 'publish-metrics', + 'apdex' + ]; +}; + +module.exports.getOfficialEngines = function () { + return ['playwright']; +}; + +module.exports.getProPlugins = function () { + return ['http-ssl-auth', 'http-file-uploads', 'sqs-reporter']; +}; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/run-cluster.js b/packages/artillery/lib/platform/aws-ecs/legacy/run-cluster.js new file mode 100644 index 0000000000..0e8d40406c --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/run-cluster.js @@ -0,0 +1,1817 @@ +/* eslint-disable no-warning-comments */ + +const AWS = require('aws-sdk'); +// Normal debugging for messages, summaries, and errors: +const debug = require('debug')('commands:run-test'); +// Verbose debugging for responses from AWS API calls, large objects etc: +const debugVerbose = require('debug')('commands:run-test:v'); +const debugErr = require('debug')('commands:run-test:errors'); +const A = require('async'); +const { customAlphabet } = require('nanoid'); +const path = require('path'); +const fs = require('fs'); +const chalk = require('chalk'); +const defaultOptions = require('rc')('artillery'); +const moment = require('moment'); + +const EnsurePlugin = require('artillery-plugin-ensure'); + +const EventEmitter = require('events'); + +const _ = require('lodash'); + +const pkg = require('../../../../package.json'); +const { parseTags } = require('./tags'); +const { Timeout, sleep, timeStringToMs } = require('./time'); +const { SqsReporter } = require('./sqs-reporter'); + +const awaitOnEE = require('../../../../lib/util/await-on-ee'); + +const { VPCSubnetFinder } = require('./find-public-subnets'); +const awsUtil = require('./aws-util'); +const { createTest } = require('./create-test'); + +const { TestBundle } = require('./test-object'); +const createS3Client = require('./create-s3-client'); +const { getBucketName } = require('./util'); +const getAccountId = require('../../aws/aws-get-account-id'); + +const dotenvParse = require('dotenv').parse; + +const util = require('./util'); + +const setDefaultAWSCredentials = require('../../aws/aws-set-default-credentials'); + +module.exports = runCluster; + +let consoleReporter = { + toggleSpinner: () => {} +}; + +const { + TASK_NAME, + SQS_QUEUES_NAME_PREFIX, + LOGGROUP_NAME, + IMAGE_VERSION, + WAIT_TIMEOUT, + ARTILLERY_CLUSTER_NAME, + TEST_RUNS_MAX_TAGS +} = require('./constants'); + +const { + TestNotFoundError, + NoAvailableQueueError, + ClientServerVersionMismatchError +} = require('./errors'); + +let IS_FARGATE = false; + +const TEST_RUN_STATUS = require('./test-run-status'); + +function setupConsoleReporter() { + const reporterOpts = { + outputFormat: 'classic', + printPeriod: false + }; + + if ( + global.artillery && + global.artillery.version && + global.artillery.version.startsWith('2') + ) { + delete reporterOpts.outputFormat; + delete reporterOpts.printPeriod; + } + + const reporterEvents = new EventEmitter(); + consoleReporter = global.artillery.__createReporter( + reporterEvents, + reporterOpts + ); + + // // Disable spinner on v1 + if ( + global.artillery && + global.artillery.version && + !global.artillery.version.startsWith('2') + ) { + consoleReporter.spinner.stop(); + consoleReporter.spinner.clear(); + consoleReporter.spinner = { + start: () => {}, + stop: () => {}, + clear: () => {} + }; + } + + return { + reporterEvents + }; +} + +function runCluster(scriptPath, options) { + if (process.env.DEBUG) { + AWS.config.logger = console; + } + + const artilleryReporter = setupConsoleReporter(); + + // camelCase all flag names, e.g. `launch-config` becomes launchConfig + const options2 = {}; + for (const [k, v] of Object.entries(options)) { + options2[_.camelCase(k)] = v; + } + tryRunCluster(scriptPath, options2, artilleryReporter); +} + +function logProgress(msg, opts = {}) { + if (typeof opts.showTimestamp === 'undefined') { + opts.showTimestamp = true; + } + if (global.artillery && global.artillery.log) { + artillery.logger(opts).log(msg); + } else { + consoleReporter.toggleSpinner(); + artillery.log( + `${msg} ${chalk.gray('[' + moment().format('HH:mm:ss') + ']')}` + ); + consoleReporter.toggleSpinner(); + } +} + +async function tryRunCluster(scriptPath, options, artilleryReporter) { + const MAX_RETAINED_LOG_SIZE_MB = Number( + process.env.MAX_RETAINED_LOG_SIZE_MB || '50' + ); + const MAX_RETAINED_LOG_SIZE = MAX_RETAINED_LOG_SIZE_MB * 1024 * 1024; + + let currentSize = 0; + // Override console.log so as not to interfere with the spinner + let outputLines = []; + let truncated = false; + + console.log = (function () { + let orig = console.log; + return function () { + try { + orig.apply(console, arguments); + + if (currentSize < MAX_RETAINED_LOG_SIZE) { + outputLines = outputLines.concat(arguments); + for (const x of arguments) { + currentSize += String(x).length; + } + } else { + if (!truncated) { + truncated = true; + const msg = `[WARNING] Artillery: maximum retained log size exceeded, max size: ${MAX_RETAINED_LOG_SIZE_MB}MB. Further logs won't be retained.\n\n`; + process.stdout.write(msg); + outputLines = outputLines.concat([msg]); + } + } + } catch (err) { + debug(err); + } + }; + })(); + + console.error = (function () { + let orig = console.error; + return function () { + try { + orig.apply(console, arguments); + + if (currentSize < MAX_RETAINED_LOG_SIZE) { + outputLines = outputLines.concat(arguments); + for (const x of arguments) { + currentSize += String(x).length; + } + } else { + if (!truncated) { + truncated = true; + const msg = `[WARNING] Artillery: maximum retained log size exceeded, max size: ${MAX_RETAINED_LOG_SIZE_MB}MB. Further logs won't be retained.\n\n`; + process.stdout.write(msg); + outputLines = outputLines.concat([msg]); + } + } + } catch (err) { + debug(err); + } + }; + })(); + + try { + await setDefaultAWSCredentials(AWS); + } catch (err) { + console.error(err); + process.exit(1); + } + + let context = {}; + let absoluteScriptPath; + if (typeof scriptPath !== 'undefined') { + absoluteScriptPath = path.resolve(process.cwd(), scriptPath); + context.namedTest = false; + + try { + fs.statSync(absoluteScriptPath); + } catch (statErr) { + artillery.log('Could not read file:', scriptPath); + process.exit(1); + } + } + + if (options.dotenv) { + const contents = fs.readFileSync( + path.resolve(process.cwd(), options.dotenv) + ); + context.dotenv = dotenvParse(contents); + } + + if (options.bundle) { + context.namedTest = true; + } + + if (options.maxDuration) { + try { + const maxDurationMs = timeStringToMs(options.maxDuration); + context.maxDurationMs = maxDurationMs; + } catch (err) { + throw err; + } + } + + context.tags = parseTags(options.tags); + + if (context.tags.length > TEST_RUNS_MAX_TAGS) { + console.error( + chalk.red( + `A maximum of ${TEST_RUNS_MAX_TAGS} tags is allowed per test run` + ) + ); + + process.exit(1); + } + + // Set name tag if not already set: + if (context.tags.filter((t) => t.name === 'name').length === 0) { + if (typeof scriptPath !== 'undefined') { + context.tags.push({ + name: 'name', + value: path.basename(scriptPath) + }); + } else { + context.tags.push({ + name: 'name', + value: options.bundle + }); + } + } + + context.extraSecrets = options.secret || []; + + const idf = customAlphabet('3456789abcdefghjkmnpqrtwxyz'); + context.testId = `t${idf(4)}_${idf(29)}_${idf(4)}`; + + if (context.namedTest) { + context.s3Prefix = options.bundle; + debug(`Trying to run a named test: ${context.s3Prefix}`); + } + + if (!context.namedTest) { + const contextPath = options.context + ? path.resolve(options.context) + : path.dirname(absoluteScriptPath); + + debugVerbose('script:', absoluteScriptPath); + debugVerbose('root:', contextPath); + + const containerScriptPath = path.join( + path.relative(contextPath, path.dirname(absoluteScriptPath)), + path.basename(absoluteScriptPath) + ); + + if (containerScriptPath.indexOf('..') !== -1) { + artillery.log( + chalk.red( + 'Test script must reside inside the context dir. See Artillery docs for more details.' + ) + ); + process.exit(1); + } + + // FIXME: These need clearer names. dir vs path and local vs container. + context.contextDir = contextPath; + context.newScriptPath = containerScriptPath; + + debug('container script path:', containerScriptPath); + } + + const count = Number(options.count) || 1; + + if (typeof options.taskRoleName !== 'undefined') { + let customRoleName = options.taskRoleName; + // Allow ARNs for convenience + // https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html + // We split by :role because role names may contain slash characters (subpaths) + if (customRoleName.startsWith('arn:aws:iam')) { + customRoleName = customRoleName.split(':role/')[1]; + } + context.customTaskRoleName = customRoleName; + } + + const clusterName = options.cluster || ARTILLERY_CLUSTER_NAME; + if (options.launchConfig) { + let launchConfig; + try { + launchConfig = JSON.parse(options.launchConfig); + } catch (parseErr) { + debug(parseErr); + } + + if (!launchConfig) { + artillery.log( + chalk.red( + "Launch config could not be parsed. Please check that it's valid JSON." + ) + ); + process.exit(1); + } + + if (launchConfig.ulimits && !Array.isArray(launchConfig.ulimits)) { + // TODO: Proper schema validation for the object + artillery.log(chalk.red('ulimits must be an array of objects')); + artillery.log( + 'Please see AWS documentation for more information:\nhttps://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_Ulimit.html' + ); + process.exit(1); + } + options.launchConfig = launchConfig; + } + + // check launch type is valid: + if (typeof options.launchType !== 'undefined') { + if ( + options.launchType !== 'ecs:fargate' && + options.launchType !== 'ecs:ec2' + ) { + artillery.log( + 'Invalid launch type - the value of --launch-type needs to be ecs:fargate or ecs:ec2' + ); + process.exit(1); + } + } + + if (typeof options.fargate !== 'undefined') { + console.error( + 'The --fargate flag is deprecated, use --launch-type ecs:fargate instead' + ); + } + + if (options.fargate && options.launchType) { + console.error( + 'Either --fargate or --launch-type flag should be set, not both' + ); + process.exit(1); + } + + if ( + typeof options.fargate === 'undefined' && + typeof options.launchType === 'undefined' + ) { + options.launchType = 'ecs:fargate'; + } + + IS_FARGATE = + typeof options.fargate !== 'undefined' || // --fargate set + typeof options.publicSubnetIds !== 'undefined' || // --public-subnet-ids set + (typeof options.launchType !== 'undefined' && + options.launchType === 'ecs:fargate') || // --launch-type ecs:fargate + typeof options.launchType === 'undefined'; + + global.artillery.globalEvents.emit('test:init', { + flags: options, + testRunId: context.testId, + tags: context.tags, + metadata: { + testId: context.testId, + startedAt: Date.now(), + count, + tags: context.tags, + launchType: options.launchType + } + }); + + let packageJsonPath; + if (options.packages) { + packageJsonPath = path.resolve(process.cwd(), options.packages); + try { + // TODO: Check that filename is package.json + + JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + } catch (err) { + console.error('Could not load package dependency list'); + console.error('Trying to read from:', packageJsonPath); + console.error(err); + } + } + + context = Object.assign(context, { + scriptPath: absoluteScriptPath, + originalScriptPath: scriptPath, + count: count, + taskName: `${TASK_NAME}_${ + IS_FARGATE ? 'fargate' : '' + }_${clusterName}_${IMAGE_VERSION}_${Math.floor(Math.random() * 1e6)}`, + clusterName: clusterName, + logGroupName: LOGGROUP_NAME, + cliOptions: options, + isFargate: IS_FARGATE, + configTableName: '', + status: TEST_RUN_STATUS.INITIALIZING, + packageJsonPath, + taskArns: [] + }); + + if ( + typeof options.region !== 'undefined' && + util.supportedRegions.indexOf(options.region) === -1 + ) { + console.log( + `Unsupported region (${ + options.region + }) provided. Please specify one of: ${util.supportedRegions.join(', ')} ` + ); + process.exit(1); + } + + if (typeof options.region !== 'undefined') { + context.region = options.region; + } + + let subnetIds = []; + if (options.publicSubnetIds) { + console.error( + `${chalk.yellow( + 'Warning' + )}: --public-subnet-ids will be deprecated. Use --subnet-ids instead.` + ); + + subnetIds = options.publicSubnetIds.split(','); + } + if (options.subnetIds) { + subnetIds = options.subnetIds.split(','); + } + + if (IS_FARGATE) { + context.fargatePublicSubnetIds = subnetIds; + context.fargateSecurityGroupIds = + typeof options.securityGroupIds !== 'undefined' + ? options.securityGroupIds.split(',') + : []; + } + + if (global.artillery && global.artillery.telemetry) { + global.artillery.telemetry.capture('run-test', { + version: global.artillery.version, + proVersion: pkg.version, + count: count, + launchPlatform: IS_FARGATE ? 'ecs:fargate' : 'ecs:ec2', + usesTags: context.tags.length > 0, + region: context.region, + crossRegion: context.region !== context.backendRegion + }); + } + + async function newWaterfall(artilleryReporter) { + let testRunCompletedSuccessfully = true; + + async function initCleanup() { + artillery.log('Stopping test run...'); + context.status = TEST_RUN_STATUS.TERMINATING; + await sleep(5 * 1000); + artillery.log('Cleaning up...'); + await sleep(5 * 1000); + context.status = TEST_RUN_STATUS.EARLY_STOP; + await cleanup(context, { clean: false }); + process.exit(1); + } + + let lastCtrlC = Date.now(); + process.on('SIGINT', async () => { + const delta = Date.now() - lastCtrlC; + if (delta > 500) { + artillery.log('Press Ctrl+C twice to stop the test'); + lastCtrlC = Date.now(); + } else { + await initCleanup(); + } + }); + process.on('SIGTERM', initCleanup); + + // Messages from SQS reporter created later will be relayed via this EE + context.reporterEvents = artilleryReporter.reporterEvents; + + try { + logProgress('Checking AWS connectivity...'); + + context.accountId = await getAccountId(); + await Promise.all([ + (async function (context) { + const bucketName = await getBucketName(); + context.s3Bucket = bucketName; + return context; + })(context) + ]); + + logProgress('Checking cluster...'); + const clusterExists = await checkTargetCluster(context); + if (!clusterExists) { + if (typeof context.cliOptions.cluster === 'undefined') { + // User did not specify a cluster with --cluster, and ARTILLERY_CLUSTER_NAME + // does not exist, so create it + await createArtilleryCluster(context); + } else { + // User specified a cluster, but it's not there + throw new Error( + `Could not find cluster ${context.clusterName} in ${context.region}` + ); + } + } + + if (context.tags.length > 0) { + logProgress( + 'Tags: ' + context.tags.map((t) => t.name + ':' + t.value).join(', ') + ); + } + logProgress(`Test run ID: ${context.testId}`); + + logProgress('Preparing launch platform...'); + + await maybeGetSubnetIdsForFargate(context); + + logProgress( + `Environment: + Account: ${context.accountId} + Region: ${context.region} + Count: ${context.count} + Cluster: ${context.clusterName} + Launch type: ${context.cliOptions.launchType} +`, + { showTimestamp: false } + ); + + await createQueue(context); + await checkCustomTaskRole(context); + await ensureTaskExists(context); + logProgress('Preparing test bundle...'); + await createTestBundle(context); + await getManifest(context); + await generateTaskOverrides(context); + + logProgress('Launching workers...'); + await setupDefaultECSParams(context); + + // Set up SQS listener: + listen(context, artilleryReporter.reporterEvents); + + if ( + context.status !== TEST_RUN_STATUS.EARLY_STOP && + context.status !== TEST_RUN_STATUS.TERMINATING + ) { + await launchLeadTask(context); + } + + if ( + context.status !== TEST_RUN_STATUS.EARLY_STOP && + context.status !== TEST_RUN_STATUS.TERMINATING + ) { + logProgress( + context.isFargate ? 'Waiting for Fargate...' : 'Waiting for ECS...' + ); + await ecsRunTask(context); + } + + if ( + context.status !== TEST_RUN_STATUS.EARLY_STOP && + context.status !== TEST_RUN_STATUS.TERMINATING + ) { + await waitForTasks2(context); + } + + if ( + context.status !== TEST_RUN_STATUS.EARLY_STOP && + context.status !== TEST_RUN_STATUS.TERMINATING + ) { + logProgress('Waiting for workers to come online...'); + await waitForWorkerSync(context); + await sendGoSignal(context); + logProgress('Workers are running, waiting for reports...'); + + if (context.maxDurationMs && context.maxDurationMs > 0) { + logProgress( + `Max duration for test run is set to: ${context.cliOptions.maxDuration}` + ); + const testDurationTimeout = new Timeout(context.maxDurationMs); + testDurationTimeout.start(); + testDurationTimeout.on('timeout', async () => { + artillery.log( + `Max duration of test run exceeded: ${context.cliOptions.maxDuration}\n` + ); + await initCleanup(); + }); + } + + context.status = TEST_RUN_STATUS.RECEIVING_REPORTS; + } + + // Need to wait for all reports to be over here, not exit + const workerState = await awaitOnEE( + artilleryReporter.reporterEvents, + 'workersDone' + ); + debug(workerState); + + logProgress(`Test run completed: ${context.testId}`); + + context.status = TEST_RUN_STATUS.COMPLETED; + + let checks = []; + global.artillery.globalEvents.once('checks', async (results) => { + checks = results; + }); + + if (context.ensureSpec) { + new EnsurePlugin.Plugin({ config: { ensure: context.ensureSpec } }); + } + + for (const e of global.artillery.extensionEvents) { + const ps = []; + const testInfo = { endTime: Date.now() }; + if (e.ext === 'beforeExit') { + ps.push( + e.method({ + report: context.aggregateReport, + flags: context.cliOptions, + runnerOpts: { + environment: context.cliOptions?.environment, + scriptPath: '', + absoluteScriptPath: '' + }, + testInfo + }) + ); + } + await Promise.allSettled(ps); + } + + if (context.cliOptions.output) { + let logfile = getLogFilename( + context.cliOptions.output, + defaultOptions.logFilenameFormat + ); + + for (const ix of context.intermediateReports) { + delete ix.histograms; + ix.histograms = ix.summaries; + } + delete context.aggregateReport.histograms; + context.aggregateReport.histograms = context.aggregateReport.summaries; + + const jsonReport = { + intermediate: context.intermediateReports, + aggregate: context.aggregateReport, + testId: context.testId, + metadata: { + tags: context.tags, + count: context.count, + region: context.region, + cluster: context.clusterName, + artilleryVersion: { + core: global.artillery.version, + pro: pkg.version + } + }, + ensure: checks.map((c) => { + return { + condition: c.original, + success: c.result === 1, + strict: c.strict + }; + }) + }; + + fs.writeFileSync(logfile, JSON.stringify(jsonReport, null, 2), { + flag: 'w' + }); + } + debug(context.testId, 'done'); + } catch (err) { + debug(err); + if (err.code === 'InvalidParameterException') { + if ( + err.message + .toLowerCase() + .indexOf('no container instances were found') !== -1 + ) { + artillery.log( + chalk.yellow( + 'The cluster has no active instances. We need some instances.' + ) + ); + } else { + artillery.log(err); + } + } else if (err instanceof TestNotFoundError) { + artillery.log(`Test ${context.s3Prefix} not found`); + } else if ( + err instanceof NoAvailableQueueError || + err instanceof ClientServerVersionMismatchError + ) { + artillery.log(chalk.red('Error:', err.message)); + } else { + artillery.log(util.formatError(err)); + artillery.log(err); + artillery.log(err.stack); + } + testRunCompletedSuccessfully = false; + global.artillery.suggestedExitCode = 1; + } finally { + if (!testRunCompletedSuccessfully) { + logProgress('Cleaning up...'); + context.status = TEST_RUN_STATUS.ERROR; + await cleanup(context, { clean: false }); + } else { + await cleanup(context, { clean: true }); + } + + const shutdownOpts = { exitCode: global.artillery.suggestedExitCode }; + global.artillery.globalEvents.emit('shutdown:start', shutdownOpts); + for (const e of global.artillery.extensionEvents) { + const ps = []; + if (e.ext === 'onShutdown') { + ps.push(e.method(shutdownOpts)); + } + await Promise.allSettled(ps); + } + + process.exit(global.artillery.suggestedExitCode); + } + } + + await newWaterfall(artilleryReporter); +} + +async function cleanup(context, opts) { + try { + if (context.sqsReporter) { + context.sqsReporter.stop(); + } + + for (const taskArn of context.taskArns) { + try { + const ecs = new AWS.ECS({ + apiVersion: '2014-11-13', + region: context.region + }); + await ecs + .stopTask({ + task: taskArn, + cluster: context.clusterName, + reason: 'Test cleanup' + }) + .promise(); + } catch (err) { + // TODO: Retry if appropriate, give the user more information + // to be able to fall back to manual intervention if possible. + // TODO: Consumer has no idea if this succeeded or not + debug(err); + } + } + + // TODO: Should either retry, or not throw in any of these + await Promise.all([ + deleteQueue(context), + deregisterTaskDefinition(context), + gcQueues(context) + ]); + + if (opts.clean) { + context.status = TEST_RUN_STATUS.COMPLETED; + } else { + // Retain ERROR status if already set + if (context.status !== TEST_RUN_STATUS.ERROR) { + context.status = TEST_RUN_STATUS.EARLY_STOP; + } + } + } catch (err) { + artillery.log(err); + } +} + +function checkFargateResourceConfig(cpu, memory) { + const FARGATE_VALID_CONFIGS = { + 256: [512, 1024, 2048], + 512: [1024, 2048, 3072, 4096], + 1024: [2048, 3072, 4096, 5120, 6144, 7168, 8192], + 2048: [ + 4096, + 5120, + 6144, + 7168, + 8192, + 8192 + 1024 * 2, + 8192 + 1024 * 3, + 8192 + 1024 * 4 + ], + 4096: [8192, 8192 + 1024, 8192 + 1024 * 2, 8192 + 1024 * 3, 8192 + 1024 * 4] + }; + + if (!FARGATE_VALID_CONFIGS[cpu]) { + return new Error( + `Unsupported cpu override for Fargate. Must be one of: ${Object.keys( + FARGATE_VALID_CONFIGS + ).join(', ')}` + ); + } + + if (FARGATE_VALID_CONFIGS[cpu].indexOf(memory) < 0) { + return new Error( + `Fargate memory override for cpu = ${cpu} must be one of: ${FARGATE_VALID_CONFIGS[ + cpu + ].join(', ')}` + ); + } + + return null; +} + +async function createArtilleryCluster(context) { + const ecs = new AWS.ECS({ apiVersion: '2014-11-13', region: context.region }); + try { + await ecs + .createCluster({ + clusterName: ARTILLERY_CLUSTER_NAME, + capacityProviders: ['FARGATE'] + }) + .promise(); + + let retries = 0; + while (retries < 12) { + const clusterActive = await checkTargetCluster(context); + if (clusterActive) { + break; + } + retries++; + await sleep(10 * 1000); + } + } catch (err) { + throw err; + } +} + +// +// Check that ECS cluster exists: +// +async function checkTargetCluster(context) { + const ecs = new AWS.ECS({ apiVersion: '2014-11-13', region: context.region }); + try { + const response = await ecs + .describeClusters({ clusters: [context.clusterName] }) + .promise(); + debug(response); + if (response.clusters.length === 0 || response.failures.length > 0) { + debugVerbose(response); + return false; + } else { + const activeClusters = response.clusters.filter( + (c) => c.status === 'ACTIVE' + ); + return activeClusters.length > 0; + } + } catch (err) { + debugVerbose(err); + return false; + } +} + +async function maybeGetSubnetIdsForFargate(context) { + if (!context.isFargate) { + return context; + } + + // TODO: Sanity check that subnets actually exist before trying to use them in test definitions + + if (context.fargatePublicSubnetIds.length > 0) { + return context; + } + + debug('Subnet IDs not provided, looking up default VPC'); + + const f = new VPCSubnetFinder({ region: context.region }); + const publicSubnets = await f.findPublicSubnets(); + + if (publicSubnets.length === 0) { + throw new Error('Could not find public subnets in default VPC'); + } + + context.fargatePublicSubnetIds = publicSubnets.map((s) => s.SubnetId); + + debug('Found public subnets:', context.fargatePublicSubnetIds.join(', ')); + + return context; +} + +async function createTestBundle(context) { + return new Promise((resolve, reject) => { + createTest( + context.scriptPath, + { + name: context.testId, + config: context.cliOptions.config, + packageJsonPath: context.packageJsonPath + }, + function (err, _result) { + if (err) { + return reject(err); + } else { + return resolve(context); + } + } + ); + }); +} + +async function ensureTaskExists(context) { + return new Promise((resolve, reject) => { + const ecs = new AWS.ECS({ + apiVersion: '2014-11-13', + region: context.region + }); + + // Note: these are integers for container definitions, and strings for task definitions (on Fargate) + // Defaults have to be Fargate-compatible + // https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size + let cpu = 4096; + let memory = 8192; + + const defaultUlimits = { + nofile: { + softLimit: 8192, + hardLimit: 8192 + } + }; + let ulimits = []; + + if (context.cliOptions.launchConfig) { + const lc = context.cliOptions.launchConfig; + if (lc.cpu) { + cpu = parseInt(lc.cpu, 10); + } + if (lc.memory) { + memory = parseInt(lc.memory, 10); + } + + if (lc.ulimits) { + lc.ulimits.forEach((u) => { + if (!defaultUlimits[u.name]) { + defaultUlimits[u.name] = {}; + } + defaultUlimits[u.name] = { + softLimit: u.softLimit, + hardLimit: + typeof u.hardLimit == 'number' ? u.hardLimit : u.softLimit + }; + }); + } + + // TODO: Check this earlier to return an error faster. + if (context.isFargate) { + const configErr = checkFargateResourceConfig(cpu, memory); + if (configErr) { + return reject(configErr); + } + } + } + + ulimits = Object.keys(defaultUlimits).map((name) => { + return { + name: name, + softLimit: defaultUlimits[name].softLimit, + hardLimit: defaultUlimits[name].hardLimit + }; + }); + + const imageUrl = + process.env.WORKER_IMAGE_URL || + `301676560329.dkr.ecr.${context.region}.amazonaws.com/artillery-pro/aws-ecs-node:v2-${IMAGE_VERSION}`; + + // ['NPM_TOKEN', 'NPM_REGISTRY', 'NPM_SCOPE', 'NPM_SCOPE_REGISTRY', 'NPMRC', 'ARTIFACTORY_AUTH', 'ARTIFACTORY_EMAIL'] + const secrets = [].concat(context.extraSecrets).map((secretName) => { + return { + name: secretName, + valueFrom: `arn:aws:ssm:${context.backendRegion}:${context.accountId}:parameter/artilleryio/${secretName}` + }; + }); + + let taskDefinition = { + family: context.taskName, + containerDefinitions: [ + { + name: 'artillery', + image: imageUrl, + cpu: cpu, + command: [], + memory: memory, + secrets: secrets, + ulimits: ulimits, + essential: true, + logConfiguration: { + logDriver: 'awslogs', + options: { + 'awslogs-group': `${context.logGroupName}/${context.clusterName}`, + 'awslogs-region': context.region, + 'awslogs-stream-prefix': `artilleryio/${context.testId}`, + 'awslogs-create-group': 'true', + mode: 'non-blocking' + } + } + } + ], + executionRoleArn: context.taskRoleArn + }; + context.taskDefinition = taskDefinition; + + if (context.isFargate) { + taskDefinition.networkMode = 'awsvpc'; + taskDefinition.requiresCompatibilities = ['FARGATE']; + taskDefinition.cpu = String(cpu); + taskDefinition.memory = String(memory); + // NOTE: This role must exist. + // This value cannot be an override, meaning it's hardcoded into the task definition. + // That in turn means that if the role is updated then the task definition needs to be + // recreated too + taskDefinition.executionRoleArn = context.taskRoleArn; // TODO: A separate role for Fargate + } + + const params = { + taskDefinition: context.taskName + }; + + debug('Task definition\n', JSON.stringify(taskDefinition, null, 4)); + + ecs.describeTaskDefinition(params, function (err, _data) { + if (err) { + ecs.registerTaskDefinition(taskDefinition, function (err, response) { + if (err) { + artillery.log(err); + artillery.log('Could not create ECS task, please try again'); + return reject(err); + } else { + debug('OK: ECS task registered'); + debugVerbose(JSON.stringify(response, null, 4)); + context.taskDefinitionArn = + response.taskDefinition.taskDefinitionArn; + debug(`Task definition ARN: ${context.taskDefinitionArn}`); + return resolve(context); + } + }); + } else { + debug('OK: ECS task exists'); + if (process.env.ECR_IMAGE_VERSION) { + debug( + 'ECR_IMAGE_VERSION is set, but the task definition was already in place.' + ); + } + return resolve(context); + } + }); + }); +} + +async function checkCustomTaskRole(context) { + if (!context.customTaskRoleName) { + return; + } + + const iam = new AWS.IAM(); + try { + const roleData = await iam + .getRole({ RoleName: context.customTaskRoleName }) + .promise(); + context.customRoleArn = roleData.Role.Arn; + context.taskRoleArn = roleData.Role.Arn; + debug(roleData); + } catch (err) { + throw err; + } +} + +async function gcQueues(context) { + const sqs = new AWS.SQS({ + region: context.region + }); + + let data; + try { + data = await sqs + .listQueues({ + QueueNamePrefix: SQS_QUEUES_NAME_PREFIX, + MaxResults: 1000 + }) + .promise(); + } catch (err) { + debug(err); + } + + if (data && data.QueueUrls && data.QueueUrls.length > 0) { + for (const qu of data.QueueUrls) { + try { + const data = await sqs + .getQueueAttributes({ + QueueUrl: qu, + AttributeNames: ['CreatedTimestamp'] + }) + .promise(); + const ts = Number(data.Attributes['CreatedTimestamp']) * 1000; + // Delete after 96 hours + if (Date.now() - ts > 96 * 60 * 60 * 1000) { + await sqs.deleteQueue({ QueueUrl: qu }).promise(); + } + } catch (err) { + // TODO: Filter on errors which may be ignored, e.g.: + // AWS.SimpleQueueService.NonExistentQueue: The specified queue does not exist + // which can happen if another test ends between calls to listQueues and getQueueAttributes. + // Sometimes SQS returns recently deleted queues to ListQueues too. + debug(err); + } + } + } +} + +async function deleteQueue(context) { + if (!context.sqsQueueUrl) { + return; + } + + const sqs = new AWS.SQS({ + region: context.region + }); + + try { + await sqs.deleteQueue({ QueueUrl: context.sqsQueueUrl }).promise(); + } catch (err) { + console.error(`Unable to clean up SQS queue. URL: ${context.sqsQueueUrl}`); + debug(err); + } +} + +async function createQueue(context) { + const sqs = new AWS.SQS({ + region: context.region + }); + + const queueName = `${SQS_QUEUES_NAME_PREFIX}_${context.testId.slice( + 0, + 30 + )}.fifo`; + const params = { + QueueName: queueName, + Attributes: { + FifoQueue: 'true', + ContentBasedDeduplication: 'false', + MessageRetentionPeriod: '1800', + VisibilityTimeout: '600' // 10 minutes + } + }; + + try { + const result = await sqs.createQueue(params).promise(); + context.sqsQueueUrl = result.QueueUrl; + } catch (err) { + throw err; + } + + // Wait for the queue to be available: + let waited = 0; + let ok = false; + while (waited < 120 * 1000) { + try { + const results = await sqs + .listQueues({ QueueNamePrefix: queueName }) + .promise(); + if (results.QueueUrls && results.QueueUrls.length === 1) { + debug('SQS queue created:', queueName); + ok = true; + break; + } else { + await sleep(10 * 1000); + waited += 10 * 1000; + } + } catch (err) { + await sleep(10 * 1000); + waited += 10 * 1000; + } + } + + if (!ok) { + debug('Time out waiting for SQS queue:', queueName); + throw new Error('SQS queue could not be created'); + } +} + +async function getManifest(context) { + try { + const testBundle = new TestBundle( + context.namedTest ? context.s3Prefix : context.testId + ); + const metadata = await testBundle.getManifest(); + + context.newScriptPath = metadata.scriptPath; + + if (metadata.configPath) { + context.configPath = metadata.configPath; + } + + return context; + } catch (err) { + if (err.code === 'NoSuchKey') { + throw new TestNotFoundError(); + } else { + throw err; + } + } +} + +async function generateTaskOverrides(context) { + const cliArgs = ['run'].concat( + context.cliOptions.environment + ? ['--environment', context.cliOptions.environment] + : [], + context.cliOptions['scenario-name'] + ? ['--scenario-name', context.cliOptions['scenario-name']] + : [], + context.cliOptions.insecure ? ['-k'] : [], + context.cliOptions.target ? ['-t', context.cliOptions.target] : [], + context.cliOptions.overrides + ? ['--overrides', context.cliOptions.overrides] + : [], + context.configPath ? ['--config', context.configPath] : [] + ); + // NOTE: This MUST come last: + cliArgs.push(context.newScriptPath); + + debug('cliArgs', cliArgs, cliArgs.join(' ')); + + const s3path = `s3://${context.s3Bucket}/tests/${ + context.namedTest ? context.s3Prefix : context.testId + }`; + + const overrides = { + containerOverrides: [ + { + name: 'artillery', + command: [ + '-p', + s3path, + '-a', + util.btoa(JSON.stringify(cliArgs)), + '-r', + context.region || context.backendRegion, + '-q', + process.env.SQS_QUEUE_URL || context.sqsQueueUrl, + '-i', + context.testId, + '-d', + `s3://${context.s3Bucket}/test-runs`, + '-t', + String(WAIT_TIMEOUT) + ], + environment: [ + { + name: 'AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE', + value: '1' + } + ] + } + ], + taskRoleArn: context.taskRoleArn + }; + + if (context.customRoleArn) { + overrides.taskRoleArn = context.customRoleArn; + } + + overrides.containerOverrides[0].environment.push({ + name: 'USE_V2', + value: 'true' + }); + + if (context.dotenv) { + let extraEnv = []; + for (const [name, value] of Object.entries(context.dotenv)) { + extraEnv.push({ name, value }); + } + overrides.containerOverrides[0].environment = + overrides.containerOverrides[0].environment.concat(extraEnv); + } + + if (context.cliOptions.launchConfig) { + const lc = context.cliOptions.launchConfig; + if (lc.environment) { + overrides.containerOverrides[0].environment = + overrides.containerOverrides[0].environment.concat(lc.environment); + } + + // + // Not officially supported: + // + if (lc.taskRoleArn) { + overrides.taskRoleArn = lc.taskRoleArn; + } + if (lc.command) { + overrides.containerOverrides[0].command = lc.command; + } + } + + debug('OK: Overrides generated'); + debugVerbose(JSON.stringify(overrides, null, 4)); + + context.taskOverrides = overrides; + + return context; +} + +async function setupDefaultECSParams(context) { + const defaultParams = { + taskDefinition: context.taskName, + cluster: context.clusterName, + overrides: context.taskOverrides + }; + + if (context.isFargate) { + // Networking config: private subnets of the VPC that the ECS cluster + // is in. Don't need public subnets. + defaultParams.launchType = 'FARGATE'; + defaultParams.networkConfiguration = { + awsvpcConfiguration: { + // https://github.com/aws/amazon-ecs-agent/issues/1128 + assignPublicIp: 'ENABLED', + securityGroups: context.fargateSecurityGroupIds, + subnets: context.fargatePublicSubnetIds + } + }; + } + + context.defaultECSParams = defaultParams; + return context; +} + +async function launchLeadTask(context) { + const metadata = { + testId: context.testId, + startedAt: Date.now(), + cluster: context.clusterName, + region: context.region, + launchType: context.cliOptions.launchType, + count: context.count, + sqsQueueUrl: context.sqsQueueUrl, + tags: context.tags, + secrets: JSON.stringify( + Array.isArray(context.extraSecrets) + ? context.extraSecrets + : [context.extraSecrets] + ), + platformConfig: JSON.stringify({ + memory: context.taskDefinition.containerDefinitions[0].memory, + cpu: context.taskDefinition.containerDefinitions[0].cpu + }), + artilleryVersion: JSON.stringify({ + core: global.artillery.version + }) + }; + + artillery.globalEvents.emit('metadata', metadata); + + context.status = TEST_RUN_STATUS.LAUNCHING_WORKERS; + + const ecs = new AWS.ECS({ apiVersion: '2014-11-13', region: context.region }); + + const leaderParams = Object.assign( + { count: 1 }, + JSON.parse(JSON.stringify(context.defaultECSParams)) + ); + leaderParams.overrides.containerOverrides[0].environment.push({ + name: 'IS_LEADER', + value: 'true' + }); + + try { + const runData = await ecs.runTask(leaderParams).promise(); + if (runData.failures.length > 0) { + if (runData.failures.length === context.count) { + artillery.log('ERROR: Worker start failure'); + const uniqueReasons = [ + ...new Set(runData.failures.map((f) => f.reason)) + ]; + artillery.log('Reason:', uniqueReasons); + throw new Error('Could not start workers'); + } else { + artillery.log('WARNING: Some workers failed to start'); + artillery.log(chalk.red(JSON.stringify(runData.failures, null, 4))); + throw new Error('Not enough capacity - terminating'); + } + } + + context.taskArns = context.taskArns.concat( + runData.tasks.map((task) => task.taskArn) + ); + artillery.globalEvents.emit('metadata', { + platformMetadata: { taskArns: context.taskArns } + }); + } catch (runErr) { + throw runErr; + } + + return context; +} + +// TODO: When launching >20 containers on Fargate, adjust WAIT_TIMEOUT dynamically to +// add extra time spent in waiting between runTask calls: WAIT_TIMEOUT + worker_count. +async function ecsRunTask(context) { + const ecs = new AWS.ECS({ apiVersion: '2014-11-13', region: context.region }); + let tasksRemaining = context.count - 1; + let retries = 0; + + while ( + tasksRemaining > 0 && + context.status !== TEST_RUN_STATUS.TERMINATING && + context.status !== TEST_RUN_STATUS.EARLY_STOP + ) { + if (retries >= 10) { + artillery.log('Max retries for ECS (10) exceeded'); + throw new Error('Max retries exceeded'); + } + + let launchCount = tasksRemaining <= 10 ? tasksRemaining : 10; + let params = Object.assign( + { count: launchCount }, + JSON.parse(JSON.stringify(context.defaultECSParams)) + ); + + params.overrides.containerOverrides[0].environment.push({ + name: 'IS_LEADER', + value: 'false' + }); + + try { + const runData = await ecs.runTask(params).promise(); + if (runData.failures.length > 0) { + artillery.log('Some workers failed to start'); + const uniqueReasons = [ + ...new Set(runData.failures.map((f) => f.reason)) + ]; + artillery.log(chalk.red(uniqueReasons)); + artillery.log('Retrying...'); + await sleep(10 * 1000); + throw new Error('Not enough ECS capacity'); + } + + if (runData.tasks?.length > 0) { + context.taskArns = context.taskArns.concat( + runData.tasks.map((task) => task.taskArn) + ); + debug(`Launched ${launchCount} tasks`); + tasksRemaining -= launchCount; + await sleep(250); + } else { + retries++; + } + } catch (runErr) { + if (runErr.code === 'ThrottlingException') { + artillery.log('ThrottlingException returned from ECS, retrying'); + await sleep(2000 * retries); + debug('runTask throttled, retrying'); + debug(runErr); + } else if (runErr.message.match(/Not enough ECS capacity/gi)) { + // Do nothing + } else { + artillery.log(runErr); + } + + retries++; + if (retries >= 10) { + artillery.log('Max retries for ECS (10) exceeded'); + throw runErr; + } + } + } + return context; +} + +async function waitForTasks2(context) { + const ecs = new AWS.ECS({ apiVersion: '2014-11-13', region: context.region }); + + const params = { + tasks: context.taskArns, + cluster: context.clusterName + }; + + let failedTasks = []; + let stoppedTasks = []; + let maybeErr = null; + + const silentWaitTimeout = new Timeout(30 * 1000).start(); // wait this long before updating the user + const waitTimeout = new Timeout(60 * 1000).start(); // wait for up to 1 minute + while (context.status !== TEST_RUN_STATUS.TERMINATING) { + let ecsData; + try { + ecsData = await awsUtil.ecsDescribeTasks(params, ecs); + } catch (err) { + // TODO: Inspect err for any conditions in which we may want to abort immediately. + // Otherwise, let the timeout run to completion. + debug(err); + await sleep(5000); + continue; + } + + // All tasks are RUNNING, proceed: + if (_.every(ecsData.tasks, (s) => s.lastStatus === 'RUNNING')) { + logProgress('All workers started...'); + debug('All tasks in RUNNING state'); + break; + } + + // If there are STOPPED tasks, we need to stop: + stoppedTasks = ecsData.tasks.filter((t) => t.lastStatus === 'STOPPED'); + if (stoppedTasks.length > 0) { + debug('Some tasks in STOPPED state'); + debugErr(stoppedTasks); + // TODO: Stop RUNNING tasks and clean up (release queue lock, deregister task definition) + // TODO: Provide more information here, e.g. task ARNs, or CloudWatch log group ID + maybeErr = new Error('Worker init failure, aborting test'); + break; + } + + // If some tasks failed to start altogether, abort: + if (ecsData.failures.length > 0) { + failedTasks = ecsData.failures; + debug('Some tasks failed to start'); + debugErr(ecsData.failures); + maybeErr = new Error('Worker start up failure, aborting test'); + break; + } + + // If there are PENDING, update progress bar + debug('Waiting on pending tasks'); + if (silentWaitTimeout.timedout()) { + const statusCounts = _.countBy(ecsData.tasks, 'lastStatus'); + let statusSummary = _.map(statusCounts, (count, status) => { + const displayStatus = + status === 'RUNNING' ? 'ready' : status.toLowerCase(); + let displayStatusChalked = displayStatus; + if (displayStatus === 'ready') { + displayStatusChalked = chalk.green(displayStatus); + } else if (displayStatus === 'pending') { + displayStatusChalked = chalk.yellow(displayStatus); + } + + return `${displayStatusChalked}: ${count}`; + }).join(' / '); + + logProgress('Waiting for workers to start: ' + statusSummary); + } + + if (waitTimeout.timedout()) { + // TODO: Clean up RUNNING tasks etc + break; + } + await sleep(10 * 1000); + } // while + waitTimeout.stop(); + + if (maybeErr) { + if (stoppedTasks.length > 0) { + artillery.log(stoppedTasks); + } + if (failedTasks.length > 0) { + artillery.log(failedTasks); + } + throw maybeErr; + } + + return context; +} + +async function waitForWorkerSync(context) { + return new Promise((resolve, reject) => { + const MAGIC_PREFIX = 'synced_'; + const prefix = `test-runs/${context.testId}/${MAGIC_PREFIX}`; + + const intervalSec = 10; + const times = WAIT_TIMEOUT / intervalSec; + + A.retry( + { times: times, interval: intervalSec * 1000 }, + // we wrap the function since async#retry will retry ONLY when an + // error is returned + function wrapForRetry(next) { + util.listAllObjectsWithPrefix( + context.s3Bucket, + prefix, + (err, objects) => { + // NOTE: err here is an S3 error + if (err) { + next(err); + } else { + debug({ objects }); + + // TODO: context.count is how many we requested, but we need to handle the case when not everything started + if (objects.length !== context.count) { + debug( + `expected ${context.count} sync acks but got ${objects.length}` + ); + // this tells async#retry to retry + return next(new Error('Timed out waiting for workers to sync')); + } else { + return next(null); + } + } + } + ); + }, + (err) => { + if (err) { + return reject(err); + } else { + debug('all workers synced'); + return resolve(context); + } + } + ); // A.retry + }); +} + +async function sendGoSignal(context) { + const s3 = createS3Client(); + try { + await s3 + .putObject({ + Body: context.testId, + Bucket: context.s3Bucket, + Key: `test-runs/${context.testId}/go.json` + }) + .promise(); + } catch (err) { + throw err; + } + + return context; +} + +async function listen(context, ee) { + return new Promise((resolve, _reject) => { + context.intermediateReports = []; + context.aggregateReport = null; + + const r = new SqsReporter(context); + context.sqsReporter = r; + r.on('workersDone', (state) => { + ee.emit('workersDone', state); + return resolve(context); + }); + r.on('done', (stats) => { + if (stats.report) { + context.aggregateReport = stats.report(); + } else { + context.aggregateReport = stats; + } + + global.artillery.globalEvents.emit('done', stats); + ee.emit('done', stats); + }); + r.on('error', (err) => { + // Ignore SQS errors + // ee.emit('error', err); + // return reject(err); + debug(err); + }); + + r.on('workerDone', (body, attrs) => { + if (process.env.LOG_WORKER_MESSAGES) { + artillery.log( + chalk.green( + `[${attrs.workerId.StringValue} ${JSON.stringify(body, null, 4)}]` + ) + ); + } + }); + r.on('workerError', (body, attrs) => { + if (process.env.LOG_WORKER_MESSAGES) { + artillery.log( + chalk.red( + `[${attrs.workerId.StringValue} ${JSON.stringify(body, null, 4)}]` + ) + ); + } + artillery.log( + chalk.yellow( + `Worker exited with an error, worker ID = ${attrs.workerId.StringValue}` + ) + ); + // TODO: Copy log over and print path to log file so that user may inspect it - in a temporary location + global.artillery.suggestedExitCode = body.exitCode || 1; + }); + + r.on('workerMessage', (body, attrs) => { + if (process.env.LOG_WORKER_MESSAGES) { + artillery.log( + chalk.yellow( + `[${attrs.workerId.StringValue}] ${body.msg} ${body.type}` + ) + ); + } + + if (body.type === 'stopped') { + if (context.status !== TEST_RUN_STATUS.EARLY_STOP) { + artillery.log('Test run has been requested to stop'); + } + context.status = TEST_RUN_STATUS.EARLY_STOP; + } + + if (body.type === 'ensure') { + try { + context.ensureSpec = JSON.parse(util.atob(body.msg)); + } catch (parseErr) { + console.error('Error processing ensure directive'); + } + } + }); + + r.on('stats', async (stats) => { + let report; + if (stats.report) { + report = stats.report(); + context.intermediateReports.push(report); + } else { + context.intermediateReports.push(stats); + report = stats; + } + + global.artillery.globalEvents.emit('stats', stats); + ee.emit('stats', stats); + }); + + r.start(); + }); +} + +async function deregisterTaskDefinition(context) { + if (!context.taskDefinitionArn) { + return; + } + + const ecs = new AWS.ECS({ apiVersion: '2014-11-13', region: context.region }); + try { + await ecs + .deregisterTaskDefinition({ taskDefinition: context.taskDefinitionArn }) + .promise(); + debug(`Deregistered ${context.taskDefinitionArn}`); + } catch (err) { + artillery.log(err); + debug(err); + } + + return context; +} + +// TODO: Remove - duplicated in run.js +function getLogFilename(output, userDefaultFilenameFormat) { + let logfile; + + // is the destination a directory that exists? + let isDir = false; + if (output) { + try { + isDir = fs.statSync(output).isDirectory(); + } catch (err) { + // ENOENT, don't need to do anything + } + } + + const defaultFormat = '[artillery_report_]YMMDD_HHmmSS[.json]'; + if (!isDir && output) { + // -o is set with a filename (existing or not) + logfile = output; + } else if (!isDir && !output) { + // no -o set + } else { + // -o is set with a directory + logfile = path.join( + output, + moment().format(userDefaultFilenameFormat || defaultFormat) + ); + } + + return logfile; +} diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/sqs-reporter.js b/packages/artillery/lib/platform/aws-ecs/legacy/sqs-reporter.js new file mode 100644 index 0000000000..71cbb977aa --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/sqs-reporter.js @@ -0,0 +1,351 @@ +const EventEmitter = require('events'); +const https = require('https'); + +const AWS = require('aws-sdk'); +const { Consumer } = require('sqs-consumer'); +const driftless = require('driftless'); +const debug = require('debug')('sqs-reporter'); +const debugV = require('debug')('sqs-reporter:v'); + +const _ = require('lodash'); + +class SqsReporter extends EventEmitter { + constructor(opts) { + super(); + + this.sqsQueueUrl = opts.sqsQueueUrl; + this.region = opts.region; + this.testId = opts.testId; + this.count = opts.count; + + this.periodsReportedFor = []; + + this.ee = new EventEmitter(); + + this.workerState = {}; + this.lastIntermediateReportAt = 0; + this.taskWatcher = null; + + this.metricsByPeriod = {}; // individual intermediates by worker + this.mergedPeriodMetrics = []; // merged intermediates for a period + + // Debug info: + this.messagesProcessed = {}; + this.metricsMessagesFromWorkers = {}; + } + + _allWorkersDone() { + return Object.keys(this.workerState).length === this.count; + } + + stop() { + debug('stopping'); + for (const sqsConsumer of this.sqsConsumers) { + sqsConsumer.stop(); + } + } + + start() { + debug('starting'); + const self = this; + + self.sqsDebugInterval = driftless.setDriftlessInterval(() => { + debug(self.messagesProcessed); + let total = 0; + for (const [k, v] of Object.entries(self.messagesProcessed)) { + total += v; + } + debug('total:', total); + }, 10 * 1000); + + self.intermediateReporterInterval = driftless.setDriftlessInterval(() => { + if (Object.keys(self.metricsByPeriod).length === 0) { + return; // nothing received yet + } + + // We always look at the earliest period available so that reports come in chronological order + const earliestPeriodAvailable = Object.keys(self.metricsByPeriod) + .filter((x) => self.periodsReportedFor.indexOf(x) === -1) + .sort()[0]; + + // TODO: better name. One above is earliestNotAlreadyReported + const earliest = Object.keys(self.metricsByPeriod).sort()[0]; + if (self.periodsReportedFor.indexOf(earliest) > -1) { + global.artillery.log( + 'Warning: multiple batches of metrics for period', + earliest, + new Date(Number(earliest)) + ); + delete self.metricsByPeriod[earliest]; // FIXME: need to merge them in for the final report + } + + // We can process SQS messages in batches of 10 at a time, so + // when there are more workers, we need to wait longer: + const MAX_WAIT_FOR_PERIOD_MS = + (Math.ceil(self.count / 10) * 2 + 20) * 1000; + + if ( + typeof earliestPeriodAvailable !== 'undefined' && + (self.metricsByPeriod[earliestPeriodAvailable].length === self.count || + Date.now() - Number(earliestPeriodAvailable) > MAX_WAIT_FOR_PERIOD_MS) + ) { + // TODO: autoscaling. Handle workers that drop off as the first case - self.count needs to be updated dynamically + debug( + 'have metrics from all workers for period or MAX_WAIT_FOR_PERIOD reached', + earliestPeriodAvailable + ); + + debug( + 'Report @', + new Date(Number(earliestPeriodAvailable)), + 'made up of items:', + self.metricsByPeriod[String(earliestPeriodAvailable)].length + ); + + // TODO: Track how many workers provided metrics in the metrics report + const stats = global.artillery.__SSMS.mergeBuckets( + self.metricsByPeriod[String(earliestPeriodAvailable)] + )[String(earliestPeriodAvailable)]; + self.mergedPeriodMetrics.push(stats); + // summarize histograms for console reporter + stats.summaries = {}; + for (const [name, value] of Object.entries(stats.histograms || {})) { + const summary = global.artillery.__SSMS.summarizeHistogram(value); + stats.summaries[name] = summary; + delete self.metricsByPeriod[String(earliestPeriodAvailable)]; + } + + self.periodsReportedFor.push(earliestPeriodAvailable); + + debug('Emitting stats event'); + self.emit('stats', stats); + } else { + debug('Waiting for more workerStats before emitting stats event'); + } + }, 5 * 1000); + + self.workersDoneWatcher = driftless.setDriftlessInterval(() => { + if (!self._allWorkersDone()) { + return; + } + + // Have we received and processed all intermediate metrics? + if (Object.keys(self.metricsByPeriod).length > 0) { + debug( + 'All workers done but still waiting on some intermediate reports' + ); + return; + } + + debug('ready to emit done event'); + debug('mergedPeriodMetrics'); + debug(self.mergedPeriodMetrics); + + // Merge by period, then compress and emit + const stats = global.artillery.__SSMS.pack(self.mergedPeriodMetrics); + stats.summaries = {}; + for (const [name, value] of Object.entries(stats.histograms || {})) { + const summary = global.artillery.__SSMS.summarizeHistogram(value); + stats.summaries[name] = summary; + } + + if (process.env.DEBUG === 'sqs-reporter:v') { + for (const [workerId, metrics] of Object.entries( + self.metricsMessagesFromWorkers + )) { + debugV('worker', workerId, '->', metrics.length, 'items'); + } + // fs.writeFileSync('worker-metrics-dump.json', JSON.stringify(self.metricsMessagesFromWorkers)); + } + + self.emit('done', stats); + + driftless.clearDriftless(self.intermediateReporterInterval); + driftless.clearDriftless(self.workersDoneWatcher); + driftless.clearDriftless(self.sqsDebugInterval); + + for (const sqsConsumer of self.sqsConsumers) { + sqsConsumer.stop(); + } + + self.emit('workersDone', self.workerState); + }, 5 * 1000); + + this.ee.on('message', function (body, attrs) { + const workerId = attrs.workerId?.StringValue; + + if (!workerId) { + debug('Got message with no workerId'); + debug(body); + return; + } + if (body.event === 'workerDone' || body.event === 'workerError') { + self.workerState[workerId] = body.event; + self.emit(body.event, body, attrs); + + debug(workerId, body.event); + return; + } + + // 'done' event is from SQS Plugin - unused for now + if (body.event === 'done') { + return; + } + + if (body.msg) { + self.emit('workerMessage', body, attrs); + return; + } + + if (body.event === 'workerStats') { + // v2 SSMS stats + const workerStats = global.artillery.__SSMS.deserializeMetrics( + body.stats + ); + const period = workerStats.period; + + debug( + 'processing workerStats event, worker:', + workerId, + 'period', + period + ); + + debugV(workerStats); + if (typeof self.metricsByPeriod[period] === 'undefined') { + self.metricsByPeriod[period] = []; + } + self.metricsByPeriod[period].push(workerStats); + + if (process.env.DEBUG === 'sqs-reporter:v') { + if ( + typeof self.metricsMessagesFromWorkers[workerId] === 'undefined' + ) { + self.metricsMessagesFromWorkers[workerId] = []; + } + self.metricsMessagesFromWorkers[workerId].push(workerStats); + } + + debugV('metricsByPeriod:'); + debugV(self.metricsByPeriod); + debug('number of periods processed'); + debug(Object.keys(self.metricsByPeriod)); + debug('number of metrics collections for period:', period, ':'); + debug(self.metricsByPeriod[period].length, 'expecting:', self.count); + } + }); + + this.ee.on('messageReceiveTimeout', () => { + // TODO: 10 polls with no results, e.g. if all workers crashed + }); + + const createConsumer = function (i) { + return Consumer.create({ + queueUrl: process.env.SQS_QUEUE_URL || self.sqsQueueUrl, + region: self.region, + waitTimeSeconds: 10, + messageAttributeNames: ['testId', 'workerId'], + visibilityTimeout: 60, + batchSize: 10, + sqs: new AWS.SQS({ + httpOptions: { + agent: new https.Agent({ + keepAlive: true + }) + }, + region: self.region + }), + handleMessage: async (message) => { + let body = null; + try { + body = JSON.parse(message.Body); + } catch (err) { + console.error(err); + console.log(message.Body); + } + + // + // Ignore any messages that are invalid or not tagged properly. + // + + if (process.env.LOG_SQS_MESSAGES) { + console.log(message); + } + + if (!body) { + throw new Error(); + } + + const attrs = message.MessageAttributes; + if (!attrs || !attrs.testId) { + throw new Error(); + } + + if (self.testId !== attrs.testId.StringValue) { + throw new Error(); + } + + if (!self.messagesProcessed[i]) { + self.messagesProcessed[i] = 0; + } + self.messagesProcessed[i] += 1; + + process.nextTick(function () { + self.ee.emit('message', body, attrs); + }); + } + }); + }; + + this.sqsConsumers = []; + for (let i = 0; i < 30; i++) { + const sqsConsumer = createConsumer(i); + + sqsConsumer.on('error', (err) => { + // TODO: Ignore "SQSError: SQS delete message failed:" errors + if (err.message && err.message.match(/ReceiptHandle.+expired/i)) { + debug(err.name, err.message); + } else { + artillery.log(err); + sqsConsumer.stop(); + self.emit('error', err); + } + }); + + let empty = 0; + sqsConsumer.on('empty', () => { + empty++; + if (empty > 10) { + self.ee.emit('messageReceiveTimeout'); // TODO: + } + }); + sqsConsumer.start(); + + self.sqsConsumers.push(sqsConsumer); + } + } + + // Given a (combined) stats object, what's the difference between the + // time of earliest and latest requests made? + calculateSpread(stats) { + const period = _.reduce( + stats._requestTimestamps, + (acc, ts) => { + acc.min = Math.min(acc.min, ts); + acc.max = Math.max(acc.max, ts); + return acc; + }, + { min: Infinity, max: 0 } + ); + + const spread = round((period.max - period.min) / 1000, 1); + return spread; + } +} + +function round(number, decimals) { + const m = Math.pow(10, decimals); + return Math.round(number * m) / m; +} + +module.exports = { SqsReporter }; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/tags.js b/packages/artillery/lib/platform/aws-ecs/legacy/tags.js new file mode 100644 index 0000000000..ea520e634e --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/tags.js @@ -0,0 +1,22 @@ +'use strict'; + +function parseTags(input) { + let tags = []; + if (input) { + const tagList = input.split(',').map((x) => x.trim()); + for (const t of tagList) { + const cs = t.split(':'); + if (cs.length !== 2) { + console.error(`Invalid tag, skipping: ${t}`); + } else { + tags.push({ name: cs[0].trim(), value: cs[1].trim() }); + } + } + } + + return tags; +} + +module.exports = { + parseTags +}; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/test-object.js b/packages/artillery/lib/platform/aws-ecs/legacy/test-object.js new file mode 100644 index 0000000000..2585b7d8fc --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/test-object.js @@ -0,0 +1,33 @@ +const { getBucketName } = require('./util'); +const createS3Client = require('./create-s3-client'); + +class TestBundle { + constructor(id) { + this.id = id; + this.manifest = null; + } + + async getManifest() { + if (this.manifest) { + return this.manifest; + } + + const s3 = createS3Client(); + + const bucketName = await getBucketName(); + + const params = { + Bucket: bucketName, + Key: `tests/${this.id}/metadata.json` + }; + + const s3Data = await s3.getObject(params).promise(); + this.manifest = JSON.parse(s3Data.Body); + + return this.manifest; + } +} + +module.exports = { + TestBundle +}; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/test-run-status.js b/packages/artillery/lib/platform/aws-ecs/legacy/test-run-status.js new file mode 100644 index 0000000000..efe9d7c529 --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/test-run-status.js @@ -0,0 +1,9 @@ +module.exports = { + INITIALIZING: 1, + LAUNCHING_WORKERS: 2, + RECEIVING_REPORTS: 3, + TERMINATING: 4, + EARLY_STOP: 5, + COMPLETED: 6, + ERROR: 7 +}; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/time.js b/packages/artillery/lib/platform/aws-ecs/legacy/time.js new file mode 100644 index 0000000000..825ddd26c2 --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/time.js @@ -0,0 +1,68 @@ +'use strict'; + +const EventEmitter = require('events'); +const driftless = require('driftless'); + +async function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +class Timeout extends EventEmitter { + constructor(duration) { + super(); + this._startedAt = null; + this._duration = duration; + return this; + } + + start() { + this._startedAt = Date.now(); + this._timeout = driftless.setDriftlessTimeout(() => { + this.emit('timeout'); + }, this._duration); + return this; + } + + stop() { + driftless.clearDriftless(this._timeout); + return this; + } + + timedout() { + return Date.now() - this._startedAt > this._duration; + } +} + +// Turn a string like 2m into number of milliseconds +// Supported units: ms, s, m, h +function timeStringToMs(timeStr) { + let rx = /^([0-9]+).+$/i; + + if (!rx.test(timeStr)) { + throw new Error(`Invalid time string: ${timeStr}`); + } + + let multiplier = 0; + if (timeStr.endsWith('ms')) { + multiplier = 1; + } else if (timeStr.endsWith('s')) { + multiplier = 1000; + } else if (timeStr.endsWith('m')) { + multiplier = 60 * 1000; + } else if (timeStr.endsWith('h')) { + multiplier = 60 * 60 * 1000; + } else { + throw new Error( + `Unknown unit suffix in ${timeStr}. Supported units: ms, s, m, h` + ); + } + + const n = parseInt(timeStr.match(rx)[0], 10); + return n * multiplier; +} + +module.exports = { + Timeout, + sleep, + timeStringToMs +}; diff --git a/packages/artillery/lib/platform/aws-ecs/legacy/util.js b/packages/artillery/lib/platform/aws-ecs/legacy/util.js new file mode 100644 index 0000000000..c1eeafa8c1 --- /dev/null +++ b/packages/artillery/lib/platform/aws-ecs/legacy/util.js @@ -0,0 +1,132 @@ +'use strict'; + +const debug = require('debug')('artillery:util'); + +const AWS = require('aws-sdk'); + +const chalk = require('chalk'); + +const _ = require('lodash'); + +const A = require('async'); + +const createS3Client = require('./create-s3-client'); + +// NOTE: When updating this list remember to update create-worker-images too +const supportedRegions = [ + 'us-east-1', + // 'us-east-2', + 'us-west-1', + // 'us-west-2', + // 'ca-central-1', + 'eu-west-1', + // 'eu-west-2', + // 'eu-west-3', + 'eu-central-1', + 'ap-south-1', + // 'ap-east-1', + // 'ap-northeast-2', + // 'ap-southeast-1', + // 'ap-southeast-2', + 'ap-northeast-1' + // 'me-south-1', +]; + +const getAccountId = require('../../aws/aws-get-account-id'); + +const { S3_BUCKET_NAME_PREFIX } = require('./constants'); + +function atob(data) { + return Buffer.from(data, 'base64').toString('ascii'); +} +function btoa(data) { + return Buffer.from(data).toString('base64'); +} + +async function getBucketName() { + if (process.env.ARTILLERY_S3_BUCKET) { + return process.env.ARTILLERY_S3_BUCKET; + } + + const accountId = await getAccountId(); + const bucketName = `${S3_BUCKET_NAME_PREFIX}-${accountId}`; + // const bucketArn = `arn:aws:s3:::${bucketName}`; + return bucketName; +} + +function formatError(err) { + return ( + `${chalk.red('Error')}: ${err.message}` + (err.code ? ` (${err.code})` : '') + ); +} + +// lists all objects - responsibility for checking the count is on the caller +// TODO: prefix should be a parameter +function listAllObjectsWithPrefix(bucket, prefix, cb) { + const s3 = createS3Client(); + + const MAGIC_LIMIT = 100; + + let result = []; + + let params = { + Bucket: bucket, + MaxKeys: MAGIC_LIMIT, + Prefix: prefix + }; + + A.doWhilst( + function iteratee(next) { + s3.listObjectsV2(params, (s3Err, s3Data) => { + if (s3Err) { + return next(s3Err); + } else { + debug(`listObjectsV2: IsTruncated: ${s3Data.IsTruncated}`); + debug( + `listObjectsV2: KeyCount: ${s3Data.KeyCount} keys in the response` + ); + + result = result.concat(s3Data.Contents); + + if (s3Data.IsTruncated) { + params.ContinuationToken = s3Data.NextContinuationToken; + return next(null, true); + } else { + return next(null, false); + } + } + }); + }, + function test(shouldContinue) { + return shouldContinue; + }, + function finished(err) { + if (err) { + return cb(err); + } else { + debug(`listAllObjectsWithPrefix: returning ${result.length} results`); + return cb(null, result); + } + } + ); +} // listAllObjectsWithPrefix + +function credentialsProvided(cb) { + const credsProvided = new AWS.Config().credentials !== null; + if (cb) { + return cb(credsProvided); + } else { + return credsProvided; + } +} + +module.exports = { + supportedRegions, + getAccountId, + atob, + btoa, + formatError, + listAllObjectsWithPrefix, + credentialsProvided, + getBucketName +}; diff --git a/packages/artillery/lib/platform/aws-lambda/index.js b/packages/artillery/lib/platform/aws-lambda/index.js index a8b62f2a3f..31eb35bf00 100644 --- a/packages/artillery/lib/platform/aws-lambda/index.js +++ b/packages/artillery/lib/platform/aws-lambda/index.js @@ -33,6 +33,10 @@ const crypto = require('node:crypto'); const prices = require('./prices'); const { STATES } = require('../local/artillery-worker-local'); +const { SQS_QUEUES_NAME_PREFIX } = require('../aws/constants'); + +const createSQSQueue = require('../aws/aws-create-sqs-queue'); + // https://stackoverflow.com/a/66523153 function memoryToVCPU(memMB) { if (memMB < 832) { @@ -302,7 +306,14 @@ class PlatformLambda { const s3path = await this.uploadLambdaZip(bucketName, zipfile); debug({ s3path }); this.lambdaZipPath = s3path; - const sqsQueueUrl = await this.createSQSQueue(this.region); + + // 36 is length of a UUUI v4 string + const queueName = `${SQS_QUEUES_NAME_PREFIX}_${this.testRunId.slice( + 0, + 36 + )}.fifo`; + + const sqsQueueUrl = await createSQSQueue(this.region, queueName); this.sqsQueueUrl = sqsQueueUrl; if (typeof this.lambdaRoleArn === 'undefined') { @@ -431,7 +442,7 @@ class PlatformLambda { consumer.on('error', (err) => { artillery.log(err); }); - consumer.on('empty', (err) => { + consumer.on('empty', (_err) => { debug('queueEmpty:', queueEmpty); queueEmpty++; }); @@ -543,7 +554,7 @@ class PlatformLambda { this.functionName ); throw new Error( - `Timeout waiting for lambda function to be ready for invocation` + 'Timeout waiting for lambda function to be ready for invocation' ); } @@ -651,67 +662,6 @@ class PlatformLambda { return awsAccountId; } - // TODO: Add timestamp to SQS queue name for automatic GC - async createSQSQueue() { - const sqs = new AWS.SQS({ - region: this.region - }); - - const SQS_QUEUES_NAME_PREFIX = 'artilleryio_test_metrics'; - - // 36 is length of a UUUI v4 string - const queueName = `${SQS_QUEUES_NAME_PREFIX}_${this.testRunId.slice( - 0, - 36 - )}.fifo`; - const params = { - QueueName: queueName, - Attributes: { - FifoQueue: 'true', - ContentBasedDeduplication: 'false', - MessageRetentionPeriod: '1800', - VisibilityTimeout: '600' - } - }; - - let sqsQueueUrl; - try { - const result = await sqs.createQueue(params).promise(); - sqsQueueUrl = result.QueueUrl; - } catch (err) { - throw err; - } - - // Wait for the queue to be available: - let waited = 0; - let ok = false; - while (waited < 120 * 1000) { - try { - const results = await sqs - .listQueues({ QueueNamePrefix: queueName }) - .promise(); - if (results.QueueUrls && results.QueueUrls.length === 1) { - debug('SQS queue created:', queueName); - ok = true; - break; - } else { - await sleep(10 * 1000); - waited += 10 * 1000; - } - } catch (err) { - await sleep(10 * 1000); - waited += 10 * 1000; - } - } - - if (!ok) { - debug('Time out waiting for SQS queue:', queueName); - throw new Error(`SQS queue could not be created`); - } - - return sqsQueueUrl; - } - async createLambdaRole() { const ROLE_NAME = 'artilleryio-default-lambda-role-20230116'; const POLICY_NAME = 'artilleryio-lambda-policy-20230116'; diff --git a/packages/artillery/lib/platform/aws/aws-create-sqs-queue.js b/packages/artillery/lib/platform/aws/aws-create-sqs-queue.js new file mode 100644 index 0000000000..10feae7197 --- /dev/null +++ b/packages/artillery/lib/platform/aws/aws-create-sqs-queue.js @@ -0,0 +1,59 @@ +const AWS = require('aws-sdk'); +const debug = require('debug')('artillery:aws-create-sqs-queue'); +const sleep = require('../../util/sleep'); + +// TODO: Add timestamp to SQS queue name for automatic GC +async function createSQSQueue(region, queueName) { + const sqs = new AWS.SQS({ + region + }); + + const params = { + QueueName: queueName, + Attributes: { + FifoQueue: 'true', + ContentBasedDeduplication: 'false', + MessageRetentionPeriod: '1800', + VisibilityTimeout: '600' + } + }; + + let sqsQueueUrl; + try { + const result = await sqs.createQueue(params).promise(); + sqsQueueUrl = result.QueueUrl; + } catch (err) { + throw err; + } + + // Wait for the queue to be available: + let waited = 0; + let ok = false; + while (waited < 120 * 1000) { + try { + const results = await sqs + .listQueues({ QueueNamePrefix: queueName }) + .promise(); + if (results.QueueUrls && results.QueueUrls.length === 1) { + debug('SQS queue created:', queueName); + ok = true; + break; + } else { + await sleep(10 * 1000); + waited += 10 * 1000; + } + } catch (err) { + await sleep(10 * 1000); + waited += 10 * 1000; + } + } + + if (!ok) { + debug('Time out waiting for SQS queue:', queueName); + throw new Error('SQS queue could not be created'); + } + + return sqsQueueUrl; +} + +module.exports = createSQSQueue; diff --git a/packages/artillery/lib/platform/aws/constants.js b/packages/artillery/lib/platform/aws/constants.js index c60b178369..2ca842004b 100644 --- a/packages/artillery/lib/platform/aws/constants.js +++ b/packages/artillery/lib/platform/aws/constants.js @@ -1,4 +1,7 @@ module.exports = { + SQS_QUEUES_NAME_PREFIX: 'artilleryio_test_metrics', S3_BUCKET_NAME_PREFIX: 'artilleryio-test-data', - ECS_WORKER_ROLE_NAME: 'artilleryio-ecs-worker-role' + ECS_WORKER_ROLE_NAME: 'artilleryio-ecs-worker-role', + + ARTILLERY_CLUSTER_NAME: 'artilleryio-cluster' }; diff --git a/packages/artillery/package.json b/packages/artillery/package.json index 984a034647..fb91ef24cd 100644 --- a/packages/artillery/package.json +++ b/packages/artillery/package.json @@ -85,7 +85,6 @@ "dependencies": { "@artilleryio/int-commons": "*", "@artilleryio/int-core": "*", - "@artilleryio/platform-fargate": "^2.2.0", "@aws-sdk/credential-providers": "^3.127.0", "@oclif/core": "^2.8.11", "@oclif/plugin-help": "^5.2.11", @@ -101,16 +100,20 @@ "aws-sdk": "^2.1338.0", "chalk": "^2.4.2", "ci-info": "^3.8.0", - "cli-table3": "^0.6.2", + "cli-table3": "^0.6.0", "cross-spawn": "^7.0.3", "csv-parse": "^4.16.3", "debug": "^4.3.1", + "dependency-tree": "^6.1.0", + "detective": "^5.1.0", "dotenv": "^16.0.1", "eventemitter3": "^4.0.4", "fs-extra": "^10.1.0", "ip": "^1.1.8", + "is-builtin-module": "^2.0.0", "joi": "^17.6.0", "js-yaml": "^3.13.1", + "jsonwebtoken": "^9.0.1", "lodash": "^4.17.19", "moment": "^2.29.4", "nanoid": "^3.3.4", @@ -119,7 +122,9 @@ "sqs-consumer": "5.8.0", "temp": "^0.9.4", "tmp": "0.2.1", - "try-require": "^1.2.1" + "try-require": "^1.2.1", + "walk-sync": "^0.2.3", + "yaml-js": "^0.2.3" }, "devDependencies": { "@hapi/hapi": "^20.1.3",