From ed23a7b057add1efb77cd317b0b92a670bd4da8d Mon Sep 17 00:00:00 2001 From: Ivo Yankov Date: Mon, 30 Dec 2024 14:21:26 +0200 Subject: [PATCH] feat: Implement IOC (#1010) Signed-off-by: Jeromy Cannon Co-authored-by: Jeromy Cannon --- examples/README.md | 2 +- package-lock.json | 142 ++++++++++++----- package.json | 7 +- src/commands/base.ts | 4 +- src/commands/node/handlers.ts | 1 - src/core/account_manager.ts | 15 +- src/core/certificate_manager.ts | 21 +-- src/core/chart_manager.ts | 15 +- src/core/config/local_config.ts | 20 ++- .../config/remote/remote_config_manager.ts | 28 ++-- src/core/config_manager.ts | 10 +- src/core/container_helper.ts | 34 ++++ src/core/container_init.ts | 150 ++++++++++++++++++ .../dependency_managers/dependency_manager.ts | 24 +-- .../helm_dependency_manager.ts | 37 ++--- src/core/helm.ts | 15 +- src/core/k8.ts | 25 ++- src/core/key_manager.ts | 9 +- src/core/lease/interval_lease_renewal.ts | 2 + src/core/lease/lease_manager.ts | 39 ++--- src/core/logging.ts | 29 ++-- src/core/package_downloader.ts | 9 +- src/core/platform_installer.ts | 29 ++-- src/core/profile_manager.ts | 24 +-- src/core/shell_runner.ts | 9 +- src/core/zippy.ts | 9 +- src/index.ts | 50 +++--- test/e2e/commands/account.test.ts | 33 ++-- test/e2e/commands/node_local_hedera.test.ts | 17 +- test/e2e/commands/node_local_ptt.test.ts | 7 +- test/e2e/e2e_node_util.ts | 3 +- test/e2e/integration/commands/init.test.ts | 31 ++-- .../integration/core/account_manager.test.ts | 11 +- .../integration/core/chart_manager.test.ts | 4 +- .../helm_dependency_manager.test.ts | 11 +- test/e2e/integration/core/k8_e2e.test.ts | 5 +- test/e2e/integration/core/lease.test.ts | 5 +- .../integration/core/lease_renewal.test.ts | 7 +- .../core/platform_installer_e2e.test.ts | 17 +- .../core/remote_config_manager.test.ts | 25 +-- .../core/remote_config_validator.test.ts | 10 +- test/setup.ts | 5 + test/test_container.ts | 24 +++ test/test_util.ts | 51 +++--- test/unit/commands/base.test.ts | 44 +++-- test/unit/commands/cluster.test.ts | 16 +- test/unit/commands/context.test.ts | 12 +- test/unit/commands/network.test.ts | 71 +++++---- test/unit/core/certificate_manager.test.ts | 10 +- test/unit/core/config_manager.test.ts | 21 ++- .../dependency_manager.test.ts | 21 ++- test/unit/core/helm.test.ts | 11 +- test/unit/core/k8.test.ts | 9 +- test/unit/core/key_manager.test.ts | 5 +- test/unit/core/local_config.test.ts | 12 +- test/unit/core/platform_installer.test.ts | 23 ++- test/unit/core/profile_manager.test.ts | 38 +++-- test/unit/core/shell_runner.test.ts | 11 +- 58 files changed, 834 insertions(+), 495 deletions(-) create mode 100644 src/core/container_helper.ts create mode 100644 src/core/container_init.ts create mode 100644 test/test_container.ts diff --git a/examples/README.md b/examples/README.md index 115753a54..ce383a6d5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,7 +18,7 @@ NOTES: ## Customizing the examples * take a look at the Taskfile.yml sitting in the subdirectory for the deployment you want to run -* make sure your cluster can handle the number in SOLO_NETWORK_SIZE, if not, then you will have to update that and make it match the number of nodes in the `init-containers-values.yaml`: `hedera.nodes[]` +* make sure your cluster can handle the number in SOLO\_NETWORK\_SIZE, if not, then you will have to update that and make it match the number of nodes in the `init-containers-values.yaml`: `hedera.nodes[]` * take a look at the `init-containers-values.yaml` file and make sure the values are correct for your deployment with special attention to: * resources * nodeSelector diff --git a/package-lock.json b/package-lock.json index f229a3799..ef9b255f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "win32" ], "dependencies": { - "@eslint/eslintrc": "^3.2.0", "@hashgraph/sdk": "^2.56.0", "@kubernetes/client-node": "^0.22.3", "@listr2/prompt-adapter-enquirer": "^2.0.12", @@ -33,12 +32,11 @@ "inquirer": "^12.3.0", "ip": "^2.0.1", "js-base64": "^3.7.7", - "jsdoc": "^4.0.4", "listr2": "^8.2.5", "semver": "^7.6.3", "stream-buffers": "^3.0.3", "tar": "^7.4.3", - "tsx": "^4.19.2", + "tsyringe-neo": "^5.1.0", "uuid": "^11.0.3", "validator": "^13.12.0", "winston": "^3.17.0", @@ -50,6 +48,7 @@ "solo": "dist/solo.js" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.17.0", "@types/adm-zip": "^0.5.7", "@types/chai": "^5.0.1", @@ -88,6 +87,7 @@ "eslint-plugin-tsdoc": "^0.4.0", "globals": "^15.14.0", "jest-mock": "^29.7.0", + "jsdoc": "^4.0.4", "madge": "^8.0.0", "mocha": "^11.0.1", "mocha-each": "^2.0.1", @@ -102,6 +102,7 @@ "remark-preset-lint-recommended": "^7.0.0", "sinon": "^19.0.2", "sinon-chai": "^4.0.0", + "tsx": "^4.19.2", "typedoc": "^0.27.6", "typescript": "^5.7.2", "typescript-eslint": "^8.18.1" @@ -137,6 +138,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -145,6 +147,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -230,6 +233,7 @@ "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "dev": true, "dependencies": { "@babel/types": "^7.26.3" }, @@ -244,6 +248,7 @@ "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -301,7 +306,7 @@ "cpu": [ "ppc64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "aix" @@ -317,7 +322,7 @@ "cpu": [ "arm" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "android" @@ -333,7 +338,7 @@ "cpu": [ "arm64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "android" @@ -349,7 +354,7 @@ "cpu": [ "x64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "android" @@ -365,7 +370,7 @@ "cpu": [ "arm64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "darwin" @@ -381,7 +386,7 @@ "cpu": [ "x64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "darwin" @@ -397,7 +402,7 @@ "cpu": [ "arm64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "freebsd" @@ -413,7 +418,7 @@ "cpu": [ "x64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "freebsd" @@ -429,7 +434,7 @@ "cpu": [ "arm" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -445,7 +450,7 @@ "cpu": [ "arm64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -461,7 +466,7 @@ "cpu": [ "ia32" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -477,7 +482,7 @@ "cpu": [ "loong64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -493,7 +498,7 @@ "cpu": [ "mips64el" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -509,7 +514,7 @@ "cpu": [ "ppc64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -525,7 +530,7 @@ "cpu": [ "riscv64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -541,7 +546,7 @@ "cpu": [ "s390x" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -557,7 +562,7 @@ "cpu": [ "x64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "linux" @@ -573,7 +578,7 @@ "cpu": [ "x64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "netbsd" @@ -589,7 +594,7 @@ "cpu": [ "arm64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "openbsd" @@ -605,7 +610,7 @@ "cpu": [ "x64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "openbsd" @@ -621,7 +626,7 @@ "cpu": [ "x64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "sunos" @@ -637,7 +642,7 @@ "cpu": [ "arm64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "win32" @@ -653,7 +658,7 @@ "cpu": [ "ia32" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "win32" @@ -669,7 +674,7 @@ "cpu": [ "x64" ], - "license": "MIT", + "dev": true, "optional": true, "os": [ "win32" @@ -734,7 +739,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", - "license": "MIT", + "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -757,6 +762,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -772,6 +778,7 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, "engines": { "node": ">=18" }, @@ -782,7 +789,8 @@ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/@eslint/js": { "version": "9.17.0", @@ -1796,9 +1804,10 @@ } }, "node_modules/@jsdoc/salty": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.8.tgz", - "integrity": "sha512-5e+SFVavj1ORKlKaKr2BmTOekmXbelU7dC0cDkQLqag7xfuTPuGMUFx7KWJuv4bYZrTsoL2Z18VVCOKYxzoHcg==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", + "dev": true, "dependencies": { "lodash": "^4.17.21" }, @@ -2645,12 +2654,14 @@ "node_modules/@types/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==" + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true }, "node_modules/@types/markdown-it": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" @@ -2668,7 +2679,8 @@ "node_modules/@types/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==" + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true }, "node_modules/@types/mocha": { "version": "10.0.10", @@ -3300,6 +3312,7 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -3311,6 +3324,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -3744,7 +3758,8 @@ "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true }, "node_modules/bn.js": { "version": "5.2.1", @@ -3929,6 +3944,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -3942,6 +3958,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, "dependencies": { "lodash": "^4.17.15" }, @@ -4523,6 +4540,7 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5064,6 +5082,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "engines": { "node": ">=0.12" }, @@ -5234,8 +5253,8 @@ "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -5822,6 +5841,7 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", @@ -5838,6 +5858,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -6288,6 +6309,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -6422,6 +6444,7 @@ "version": "4.8.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -6582,7 +6605,8 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", @@ -6863,6 +6887,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, "engines": { "node": ">= 4" } @@ -6871,6 +6896,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -6886,6 +6912,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } @@ -7574,6 +7601,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, "dependencies": { "xmlcreate": "^2.0.4" } @@ -7587,6 +7615,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", + "dev": true, "dependencies": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", @@ -7615,6 +7644,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, "engines": { "node": ">=8" } @@ -7716,6 +7746,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, "dependencies": { "graceful-fs": "^4.1.9" } @@ -7747,6 +7778,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, "dependencies": { "uc.micro": "^2.0.0" } @@ -7872,7 +7904,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -8222,6 +8255,7 @@ "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -8238,6 +8272,7 @@ "version": "8.6.7", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, "peerDependencies": { "@types/markdown-it": "*", "markdown-it": "*" @@ -8247,6 +8282,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, "bin": { "marked": "bin/marked.js" }, @@ -8435,7 +8471,8 @@ "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true }, "node_modules/merge2": { "version": "1.4.1", @@ -9082,6 +9119,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -9802,6 +9840,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -10270,6 +10309,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, "engines": { "node": ">=6" } @@ -11197,6 +11237,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, "dependencies": { "lodash": "^4.17.21" } @@ -11236,6 +11277,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -12239,7 +12281,7 @@ "version": "4.19.2", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", - "license": "MIT", + "dev": true, "dependencies": { "esbuild": "~0.23.0", "get-tsconfig": "^4.7.5" @@ -12265,6 +12307,17 @@ "node": ">= 6.0.0" } }, + "node_modules/tsyringe-neo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tsyringe-neo/-/tsyringe-neo-5.1.0.tgz", + "integrity": "sha512-x6QAckY85YLGE980TIR5Xle3s76nhBNYu0kr6UlLAWiPqt4S6PgbbvNV4cjZlItv3ka8adcGYG2Wy/I2yxcqdg==", + "dependencies": { + "tslib": "^2.6.3" + }, + "engines": { + "node": ">= 18.0.0" + } + }, "node_modules/tsyringe/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -12507,7 +12560,8 @@ "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true }, "node_modules/unbox-primitive": { "version": "1.0.2", @@ -12527,7 +12581,8 @@ "node_modules/underscore": { "version": "1.13.7", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", - "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==" + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true }, "node_modules/undici-types": { "version": "6.19.8", @@ -13259,7 +13314,8 @@ "node_modules/xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==" + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true }, "node_modules/y18n": { "version": "5.0.8", diff --git a/package.json b/package.json index ac4c2123e..8bafdd4b7 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "author": "Swirlds Labs", "license": "Apache2.0", "dependencies": { - "@eslint/eslintrc": "^3.2.0", "@hashgraph/sdk": "^2.56.0", "@kubernetes/client-node": "^0.22.3", "@listr2/prompt-adapter-enquirer": "^2.0.12", @@ -65,12 +64,11 @@ "inquirer": "^12.3.0", "ip": "^2.0.1", "js-base64": "^3.7.7", - "jsdoc": "^4.0.4", "listr2": "^8.2.5", "semver": "^7.6.3", "stream-buffers": "^3.0.3", "tar": "^7.4.3", - "tsx": "^4.19.2", + "tsyringe-neo": "^5.1.0", "uuid": "^11.0.3", "validator": "^13.12.0", "winston": "^3.17.0", @@ -79,6 +77,7 @@ "yargs": "^17.7.2" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.17.0", "@types/adm-zip": "^0.5.7", "@types/chai": "^5.0.1", @@ -117,6 +116,7 @@ "eslint-plugin-tsdoc": "^0.4.0", "globals": "^15.14.0", "jest-mock": "^29.7.0", + "jsdoc": "^4.0.4", "madge": "^8.0.0", "mocha": "^11.0.1", "mocha-each": "^2.0.1", @@ -131,6 +131,7 @@ "remark-preset-lint-recommended": "^7.0.0", "sinon": "^19.0.2", "sinon-chai": "^4.0.0", + "tsx": "^4.19.2", "typedoc": "^0.27.6", "typescript": "^5.7.2", "typescript-eslint": "^8.18.1" diff --git a/src/commands/base.ts b/src/commands/base.ts index c83d24864..cecdcbaca 100644 --- a/src/commands/base.ts +++ b/src/commands/base.ts @@ -48,7 +48,6 @@ export abstract class BaseCommand extends ShellRunner { protected readonly remoteConfigManager: RemoteConfigManager; constructor(opts: Opts) { - if (!opts || !opts.logger) throw new Error('An instance of core/SoloLogger is required'); if (!opts || !opts.helm) throw new Error('An instance of core/Helm is required'); if (!opts || !opts.k8) throw new Error('An instance of core/K8 is required'); if (!opts || !opts.chartManager) throw new Error('An instance of core/ChartManager is required'); @@ -57,8 +56,7 @@ export abstract class BaseCommand extends ShellRunner { if (!opts || !opts.localConfig) throw new Error('An instance of core/LocalConfig is required'); if (!opts || !opts.remoteConfigManager) throw new Error('An instance of core/config/RemoteConfigManager is required'); - - super(opts.logger); + super(); this.helm = opts.helm; this.k8 = opts.k8; diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index 2f5077a14..89313f85c 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -655,7 +655,6 @@ export class NodeCommandHandlers implements CommandHandlers { const action = this.parent.commandActionBuilder( [ this.tasks.initialize(argv, keysConfigBuilder.bind(this), null), - RemoteConfigTasks.loadRemoteConfig.bind(this)(argv), this.tasks.generateGossipKeys(), this.tasks.generateGrpcTlsKeys(), this.tasks.finalize(), diff --git a/src/core/account_manager.ts b/src/core/account_manager.ts index 4af1b9987..7e1666bc5 100644 --- a/src/core/account_manager.ts +++ b/src/core/account_manager.ts @@ -40,13 +40,15 @@ import type {NetworkNodeServices} from './network_node_services.js'; import {NetworkNodeServicesBuilder} from './network_node_services.js'; import path from 'path'; -import {type SoloLogger} from './logging.js'; -import {type K8} from './k8.js'; +import {SoloLogger} from './logging.js'; +import {K8} from './k8.js'; import {type AccountIdWithKeyPairObject, type ExtendedNetServer} from '../types/index.js'; import {type NodeAlias, type PodName, type SdkNetworkEndpoint} from '../types/aliases.js'; import {IGNORED_NODE_ACCOUNT_ID} from './constants.js'; import {sleep} from './helpers.js'; import {Duration} from './time/duration.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; const REASON_FAILED_TO_GET_KEYS = 'failed to get keys for accountId'; const REASON_SKIPPED = 'skipped since it does not have a genesis key'; @@ -55,16 +57,17 @@ const REASON_FAILED_TO_CREATE_K8S_S_KEY = 'failed to create k8s scrt key'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; +@injectable() export class AccountManager { private _portForwards: ExtendedNetServer[]; public _nodeClient: Client | null; constructor( - private readonly logger: SoloLogger, - private readonly k8: K8, + @inject(SoloLogger) private readonly logger?: SoloLogger, + @inject(K8) private readonly k8?: K8, ) { - if (!logger) throw new Error('An instance of core/SoloLogger is required'); - if (!k8) throw new Error('An instance of core/K8 is required'); + this.logger = patchInject(logger, SoloLogger, this.constructor.name); + this.k8 = patchInject(k8, K8, this.constructor.name); this._portForwards = []; this._nodeClient = null; diff --git a/src/core/certificate_manager.ts b/src/core/certificate_manager.ts index d1e3876a5..6a6408638 100644 --- a/src/core/certificate_manager.ts +++ b/src/core/certificate_manager.ts @@ -20,24 +20,27 @@ import fs from 'fs'; import {Templates} from './templates.js'; import {GrpcProxyTlsEnums} from './enumerations.js'; -import type {ConfigManager} from './config_manager.js'; -import type {K8} from './k8.js'; -import type {SoloLogger} from './logging.js'; +import {ConfigManager} from './config_manager.js'; +import {K8} from './k8.js'; +import {SoloLogger} from './logging.js'; import type {ListrTaskWrapper} from 'listr2'; import type {NodeAlias} from '../types/aliases.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; /** * Used to handle interactions with certificates data and inject it into the K8s cluster secrets */ +@injectable() export class CertificateManager { constructor( - private readonly k8: K8, - private readonly logger: SoloLogger, - private readonly configManager: ConfigManager, + @inject(K8) private readonly k8?: K8, + @inject(SoloLogger) private readonly logger?: SoloLogger, + @inject(ConfigManager) private readonly configManager?: ConfigManager, ) { - if (!k8) throw new MissingArgumentError('an instance of core/K8 is required'); - if (!logger) throw new MissingArgumentError('an instance of core/SoloLogger is required'); - if (!configManager) throw new MissingArgumentError('an instance of core/ConfigManager is required'); + this.k8 = patchInject(k8, K8, this.constructor.name); + this.logger = patchInject(logger, SoloLogger, this.constructor.name); + this.configManager = patchInject(configManager, ConfigManager, this.constructor.name); } /** diff --git a/src/core/chart_manager.ts b/src/core/chart_manager.ts index cab158fa9..5c886ef61 100644 --- a/src/core/chart_manager.ts +++ b/src/core/chart_manager.ts @@ -15,18 +15,21 @@ * */ import * as constants from './constants.js'; -import {type Helm} from './helm.js'; +import {Helm} from './helm.js'; import chalk from 'chalk'; import {SoloError} from './errors.js'; -import {type SoloLogger} from './logging.js'; +import {SoloLogger} from './logging.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; +@injectable() export class ChartManager { constructor( - private readonly helm: Helm, - private readonly logger: SoloLogger, + @inject(Helm) private readonly helm?: Helm, + @inject(SoloLogger) private readonly logger?: SoloLogger, ) { - if (!logger) throw new Error('An instance of core/SoloLogger is required'); - if (!helm) throw new Error('An instance of core/Helm is required'); + this.helm = patchInject(helm, Helm, this.constructor.name); + this.logger = patchInject(logger, SoloLogger, this.constructor.name); } /** diff --git a/src/core/config/local_config.ts b/src/core/config/local_config.ts index a5b5532c5..ce0712568 100644 --- a/src/core/config/local_config.ts +++ b/src/core/config/local_config.ts @@ -26,14 +26,17 @@ import { type LocalConfigData, } from './local_config_data.js'; import {MissingArgumentError, SoloError} from '../errors.js'; -import {type SoloLogger} from '../logging.js'; +import {SoloLogger} from '../logging.js'; import {IsClusterContextMapping, IsDeployments} from '../validator_decorators.js'; -import type {ConfigManager} from '../config_manager.js'; +import {ConfigManager} from '../config_manager.js'; import type {EmailAddress, Namespace} from './remote/types.js'; import {ErrorMessages} from '../error_messages.js'; import {type K8} from '../k8.js'; import {splitFlagInput} from '../helpers.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from '../container_helper.js'; +@injectable() export class LocalConfig implements LocalConfigData { @IsEmail( {}, @@ -69,12 +72,15 @@ export class LocalConfig implements LocalConfigData { private readonly skipPromptTask: boolean = false; public constructor( - private readonly filePath: string, - private readonly logger: SoloLogger, - private readonly configManager: ConfigManager, + @inject('localConfigFilePath') private readonly filePath?: string, + @inject(SoloLogger) private readonly logger?: SoloLogger, + @inject(ConfigManager) private readonly configManager?: ConfigManager, ) { - if (!filePath || filePath === '') throw new MissingArgumentError('a valid filePath is required'); - if (!logger) throw new Error('An instance of core/SoloLogger is required'); + this.filePath = patchInject(filePath, 'localConfigFilePath', this.constructor.name); + this.logger = patchInject(logger, SoloLogger, this.constructor.name); + this.configManager = patchInject(configManager, ConfigManager, this.constructor.name); + + if (!this.filePath || this.filePath === '') throw new MissingArgumentError('a valid filePath is required'); const allowedKeys = ['userEmailAddress', 'deployments', 'currentDeploymentName', 'clusterContextMapping']; if (this.configFileExists()) { diff --git a/src/core/config/remote/remote_config_manager.ts b/src/core/config/remote/remote_config_manager.ts index 6da2cb402..884893056 100644 --- a/src/core/config/remote/remote_config_manager.ts +++ b/src/core/config/remote/remote_config_manager.ts @@ -23,16 +23,18 @@ import {Flags as flags} from '../../../commands/flags.js'; import * as yaml from 'yaml'; import {ComponentsDataWrapper} from './components_data_wrapper.js'; import {RemoteConfigValidator} from './remote_config_validator.js'; -import type {K8} from '../../k8.js'; +import {K8} from '../../k8.js'; import type {Cluster, Namespace} from './types.js'; -import type {SoloLogger} from '../../logging.js'; -import type {ConfigManager} from '../../config_manager.js'; -import type {LocalConfig} from '../local_config.js'; +import {SoloLogger} from '../../logging.js'; +import {ConfigManager} from '../../config_manager.js'; +import {LocalConfig} from '../local_config.js'; import type {DeploymentStructure} from '../local_config_data.js'; import {type ContextClusterStructure} from '../../../types/config_types.js'; import {type EmptyContextConfig, type Optional, type SoloListrTask} from '../../../types/index.js'; import type * as k8s from '@kubernetes/client-node'; import {StatusCodes} from 'http-status-codes'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from '../../container_helper.js'; interface ListrContext { config: {contextCluster: ContextClusterStructure}; @@ -42,6 +44,7 @@ interface ListrContext { * Uses Kubernetes ConfigMaps to manage the remote configuration data by creating, loading, modifying, * and saving the configuration data to and from a Kubernetes cluster. */ +@injectable() export class RemoteConfigManager { /** Stores the loaded remote configuration data. */ private remoteConfig: Optional; @@ -53,11 +56,16 @@ export class RemoteConfigManager { * @param configManager - Manager to retrieve application flags and settings. */ public constructor( - private readonly k8: K8, - private readonly logger: SoloLogger, - private readonly localConfig: LocalConfig, - private readonly configManager: ConfigManager, - ) {} + @inject(K8) private readonly k8?: K8, + @inject(SoloLogger) private readonly logger?: SoloLogger, + @inject(LocalConfig) private readonly localConfig?: LocalConfig, + @inject(ConfigManager) private readonly configManager?: ConfigManager, + ) { + this.k8 = patchInject(k8, K8, this.constructor.name); + this.logger = patchInject(logger, SoloLogger, this.constructor.name); + this.localConfig = patchInject(localConfig, LocalConfig, this.constructor.name); + this.configManager = patchInject(configManager, ConfigManager, this.constructor.name); + } /* ---------- Getters ---------- */ @@ -227,7 +235,7 @@ export class RemoteConfigManager { try { return await this.k8.getNamespacedConfigMap(constants.SOLO_REMOTE_CONFIGMAP_NAME); } catch (error: any) { - if (error.meta.statusCode !== StatusCodes.NOT_FOUND) { + if (error.meta?.statusCode !== StatusCodes.NOT_FOUND) { throw new SoloError('Failed to read remote config from cluster', error); } diff --git a/src/core/config_manager.ts b/src/core/config_manager.ts index 1f6789d93..178264200 100644 --- a/src/core/config_manager.ts +++ b/src/core/config_manager.ts @@ -14,7 +14,8 @@ * limitations under the License. * */ -import {SoloError, MissingArgumentError, IllegalArgumentError} from './errors.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {SoloError, MissingArgumentError} from './errors.js'; import {SoloLogger} from './logging.js'; import {Flags, Flags as flags} from '../commands/flags.js'; import * as paths from 'path'; @@ -22,6 +23,7 @@ import * as helpers from './helpers.js'; import type * as yargs from 'yargs'; import {type CommandFlag} from '../types/flag_types.js'; import {type ListrTaskWrapper} from 'listr2'; +import {patchInject} from './container_helper.js'; /** * ConfigManager cache command flag values so that user doesn't need to enter the same values repeatedly. @@ -29,12 +31,12 @@ import {type ListrTaskWrapper} from 'listr2'; * For example, 'namespace' is usually remains the same across commands once it is entered, and therefore user * doesn't need to enter it repeatedly. However, user should still be able to specify the flag explicitly for any command. */ +@injectable() export class ConfigManager { config!: Record; - constructor(private readonly logger: SoloLogger) { - if (!logger || !(logger instanceof SoloLogger)) - throw new MissingArgumentError('An instance of core/SoloLogger is required'); + constructor(@inject(SoloLogger) private readonly logger?: SoloLogger) { + this.logger = patchInject(logger, SoloLogger, this.constructor.name); this.reset(); } diff --git a/src/core/container_helper.ts b/src/core/container_helper.ts new file mode 100644 index 000000000..dbf73b10b --- /dev/null +++ b/src/core/container_helper.ts @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import {container} from 'tsyringe-neo'; + +/** + * code to patch inject bug with tsyringe: https://github.com/risen228/tsyringe-neo/issues/5 + * @param parameterValue - the value that should have been injected as a parameter in the constructor + * @param registryToken - the token to resolve from the container + * @param callingClassName - the name of the class that is calling this function + */ +export function patchInject(parameterValue: any, registryToken: any, callingClassName: string) { + if (registryToken === undefined || registryToken === null) { + throw new Error(`registryToken is undefined or null, callingClassName: ${callingClassName}`); + } + if (parameterValue === undefined || parameterValue === null) { + return container.resolve(registryToken); + } + + return parameterValue; +} diff --git a/src/core/container_init.ts b/src/core/container_init.ts new file mode 100644 index 000000000..5b16b806a --- /dev/null +++ b/src/core/container_init.ts @@ -0,0 +1,150 @@ +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import {container, Lifecycle} from 'tsyringe-neo'; +import {SoloLogger} from './logging.js'; +import {PackageDownloader} from './package_downloader.js'; +import {Zippy} from './zippy.js'; +import {DependencyManager, HelmDependencyManager} from './dependency_managers/index.js'; +import * as constants from './constants.js'; +import {Helm} from './helm.js'; +import {ChartManager} from './chart_manager.js'; +import {ConfigManager} from './config_manager.js'; +import {K8} from './k8.js'; +import {AccountManager} from './account_manager.js'; +import {PlatformInstaller} from './platform_installer.js'; +import {KeyManager} from './key_manager.js'; +import {ProfileManager} from './profile_manager.js'; +import {IntervalLeaseRenewalService} from './lease/interval_lease_renewal.js'; +import {LeaseManager} from './lease/lease_manager.js'; +import {CertificateManager} from './certificate_manager.js'; +import path from 'path'; +import {LocalConfig} from './config/local_config.js'; +import {RemoteConfigManager} from './config/remote/remote_config_manager.js'; +import os from 'os'; +import * as version from '../../version.js'; + +/** + * Container class to manage the dependency injection container + */ +export class Container { + private static instance: Container = null; + private static isInitialized = false; + + private constructor() {} + + /** + * Get the singleton instance of the container + */ + static getInstance() { + if (!Container.instance) { + Container.instance = new Container(); + } + + return Container.instance; + } + + /** + * Initialize the container with the default dependencies + * @param cacheDir - the cache directory to use, defaults to constants.SOLO_CACHE_DIR + * @param logLevel - the log level to use, defaults to 'debug' + * @param devMode - if true, show full stack traces in error messages + */ + init(cacheDir: string = constants.SOLO_CACHE_DIR, logLevel: string = 'debug', devMode: boolean = false) { + // SoloLogger + container.register('logLevel', {useValue: logLevel}); + container.register('devMode', {useValue: devMode}); + container.register(SoloLogger, {useClass: SoloLogger}, {lifecycle: Lifecycle.Singleton}); + + container.register(PackageDownloader, {useClass: PackageDownloader}, {lifecycle: Lifecycle.Singleton}); + container.register(Zippy, {useClass: Zippy}, {lifecycle: Lifecycle.Singleton}); + container.register(DependencyManager, {useClass: DependencyManager}, {lifecycle: Lifecycle.Singleton}); + + // Helm & HelmDependencyManager + container.register('osPlatform', {useValue: os.platform()}); + container.register(Helm, {useClass: Helm}, {lifecycle: Lifecycle.Singleton}); + + // HelmDependencyManager + container.register('helmInstallationDir', {useValue: path.join(constants.SOLO_HOME_DIR, 'bin')}); + container.register('osArch', {useValue: os.arch()}); + container.register('helmVersion', {useValue: version.HELM_VERSION}); + container.register(HelmDependencyManager, {useClass: HelmDependencyManager}, {lifecycle: Lifecycle.Singleton}); + + container.register(ChartManager, {useClass: ChartManager}, {lifecycle: Lifecycle.Singleton}); + container.register(ConfigManager, {useClass: ConfigManager}, {lifecycle: Lifecycle.Singleton}); + container.register(K8, {useClass: K8}, {lifecycle: Lifecycle.Singleton}); + container.register(AccountManager, {useClass: AccountManager}, {lifecycle: Lifecycle.Singleton}); + container.register(PlatformInstaller, {useClass: PlatformInstaller}, {lifecycle: Lifecycle.Singleton}); + container.register(KeyManager, {useClass: KeyManager}, {lifecycle: Lifecycle.Singleton}); + + // ProfileManager + container.register('cacheDir', {useValue: cacheDir}); + container.register(ProfileManager, {useClass: ProfileManager}, {lifecycle: Lifecycle.Singleton}); + + // LeaseRenewalService + container.register( + 'LeaseRenewalService', + {useClass: IntervalLeaseRenewalService}, + {lifecycle: Lifecycle.Singleton}, + ); + + container.register(LeaseManager, {useClass: LeaseManager}, {lifecycle: Lifecycle.Singleton}); + container.register(CertificateManager, {useClass: CertificateManager}, {lifecycle: Lifecycle.Singleton}); + + // LocalConfig + const localConfigPath = path.join(cacheDir, constants.DEFAULT_LOCAL_CONFIG_FILE); + container.register('localConfigFilePath', {useValue: localConfigPath}); + container.register(LocalConfig, {useClass: LocalConfig}, {lifecycle: Lifecycle.Singleton}); + + container.register(RemoteConfigManager, {useClass: RemoteConfigManager}, {lifecycle: Lifecycle.Singleton}); + + Container.isInitialized = true; + } + + /** + * clears the container registries and re-initializes the container + * @param cacheDir - the cache directory to use, defaults to constants.SOLO_CACHE_DIR + * @param logLevel - the log level to use, defaults to 'debug' + * @param devMode - if true, show full stack traces in error messages + */ + reset(cacheDir?: string, logLevel?: string, devMode?: boolean) { + if (Container.instance && Container.isInitialized) { + container.reset(); + } + Container.getInstance().init(cacheDir, logLevel, devMode); + } + + /** + * clears the container instances, useful for testing when you are using container.registerInstance() + * @param cacheDir - the cache directory to use, defaults to constants.SOLO_CACHE_DIR + * @param logLevel - the log level to use, defaults to 'debug' + * @param devMode - if true, show full stack traces in error messages + */ + clearInstances(cacheDir?: string, logLevel?: string, devMode?: boolean) { + if (Container.instance && Container.isInitialized) { + container.clearInstances(); + } else { + Container.getInstance().init(cacheDir, logLevel, devMode); + } + } + + /** + * only call dispose when you are about to system exit + */ + async dispose() { + await container.dispose(); + } +} diff --git a/src/core/dependency_managers/dependency_manager.ts b/src/core/dependency_managers/dependency_manager.ts index 4e3981956..2898a2992 100644 --- a/src/core/dependency_managers/dependency_manager.ts +++ b/src/core/dependency_managers/dependency_manager.ts @@ -15,20 +15,24 @@ * */ import os from 'os'; -import {SoloError, MissingArgumentError} from '../errors.js'; +import {SoloError} from '../errors.js'; import {ShellRunner} from '../shell_runner.js'; -import {type SoloLogger} from '../logging.js'; -import {type HelmDependencyManager} from './helm_dependency_manager.js'; +import {HelmDependencyManager} from './helm_dependency_manager.js'; import {type ListrTask} from 'listr2'; +import {container, inject, injectable} from 'tsyringe-neo'; +import * as constants from '../constants.js'; +@injectable() export class DependencyManager extends ShellRunner { - constructor( - logger: SoloLogger, - private readonly depManagerMap: Map, - ) { - if (!logger) throw new MissingArgumentError('an instance of core/SoloLogger is required', logger); - super(logger); - if (!depManagerMap) throw new MissingArgumentError('A map of dependency managers are required'); + private readonly depManagerMap: Map; + + constructor(@inject(HelmDependencyManager) helmDepManager?: HelmDependencyManager) { + super(); + if (helmDepManager) { + this.depManagerMap = new Map().set(constants.HELM, helmDepManager); + } else { + this.depManagerMap = new Map().set(constants.HELM, container.resolve(HelmDependencyManager)); + } } /** diff --git a/src/core/dependency_managers/helm_dependency_manager.ts b/src/core/dependency_managers/helm_dependency_manager.ts index 120dd6904..839dbbd26 100644 --- a/src/core/dependency_managers/helm_dependency_manager.ts +++ b/src/core/dependency_managers/helm_dependency_manager.ts @@ -15,20 +15,20 @@ * */ import fs from 'fs'; -import os from 'os'; import path from 'path'; import * as util from 'util'; -import {IllegalArgumentError, MissingArgumentError} from '../errors.js'; +import {MissingArgumentError} from '../errors.js'; import * as helpers from '../helpers.js'; import * as constants from '../constants.js'; -import {type PackageDownloader} from '../package_downloader.js'; -import {type Zippy} from '../zippy.js'; +import {PackageDownloader} from '../package_downloader.js'; +import {Zippy} from '../zippy.js'; import {Templates} from '../templates.js'; import * as version from '../../../version.js'; import {ShellRunner} from '../shell_runner.js'; import * as semver from 'semver'; import {OS_WIN32, OS_WINDOWS} from '../constants.js'; -import {type SoloLogger} from '../logging.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from '../container_helper.js'; // constants required by HelmDependencyManager const HELM_RELEASE_BASE_URL = 'https://get.helm.sh'; @@ -42,6 +42,7 @@ const HELM_ARTIFACT_EXT: Map = new Map() /** * Helm dependency manager installs or uninstalls helm client at SOLO_HOME_DIR/bin directory */ +@injectable() export class HelmDependencyManager extends ShellRunner { private readonly osPlatform: string; private readonly osArch: string; @@ -51,23 +52,23 @@ export class HelmDependencyManager extends ShellRunner { private readonly checksumURL: string; constructor( - private readonly downloader: PackageDownloader, - private readonly zippy: Zippy, - logger: SoloLogger, - private readonly installationDir = path.join(constants.SOLO_HOME_DIR, 'bin'), - osPlatform = os.platform(), - osArch = os.arch(), - private readonly helmVersion = version.HELM_VERSION, + @inject(PackageDownloader) private readonly downloader?: PackageDownloader, + @inject(Zippy) private readonly zippy?: Zippy, + @inject('helmInstallationDir') private readonly installationDir?: string, + @inject('osPlatform') osPlatform?: NodeJS.Platform, + @inject('osArch') osArch?: string, + @inject('helmVersion') private readonly helmVersion?: string, ) { - super(logger); + super(); + this.installationDir = patchInject(installationDir, 'helmInstallationDir', this.constructor.name); + this.osPlatform = patchInject(osPlatform, 'osPlatform', this.constructor.name); + this.osArch = patchInject(osArch, 'osArch', this.constructor.name); + this.helmVersion = patchInject(helmVersion, 'helmVersion', this.constructor.name); - if (!downloader) throw new MissingArgumentError('An instance of core/PackageDownloader is required'); - if (!zippy) throw new MissingArgumentError('An instance of core/Zippy is required'); - if (!logger) throw new IllegalArgumentError('an instance of core/SoloLogger is required', logger); if (!installationDir) throw new MissingArgumentError('installation directory is required'); - this.downloader = downloader; - this.zippy = zippy; + this.downloader = patchInject(downloader, PackageDownloader, this.constructor.name); + this.zippy = patchInject(zippy, Zippy, this.constructor.name); this.installationDir = installationDir; // Node.js uses 'win32' for windows in package.json os field, but helm uses 'windows' if (osPlatform === OS_WIN32) { diff --git a/src/core/helm.ts b/src/core/helm.ts index b2cec27f8..25ed89962 100644 --- a/src/core/helm.ts +++ b/src/core/helm.ts @@ -14,22 +14,19 @@ * limitations under the License. * */ -import os from 'os'; import * as constants from './constants.js'; import {ShellRunner} from './shell_runner.js'; import {Templates} from './templates.js'; -import {IllegalArgumentError} from './errors.js'; -import type {SoloLogger} from './logging.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; +@injectable() export class Helm extends ShellRunner { private readonly helmPath: string; - constructor( - logger: SoloLogger, - private readonly osPlatform: NodeJS.Platform = os.platform(), - ) { - if (!logger) throw new IllegalArgumentError('an instance of core/SoloLogger is required', logger); - super(logger); + constructor(@inject('osPlatform') private readonly osPlatform?: NodeJS.Platform) { + super(); + this.osPlatform = patchInject(osPlatform, 'osPlatform', this.constructor.name); this.helmPath = Templates.installationPath(constants.HELM, this.osPlatform); } diff --git a/src/core/k8.ts b/src/core/k8.ts index c6281ed0f..214058d2b 100644 --- a/src/core/k8.ts +++ b/src/core/k8.ts @@ -28,15 +28,16 @@ import * as stream from 'node:stream'; import type * as http from 'node:http'; import type * as WebSocket from 'ws'; import {getReasonPhrase, StatusCodes} from 'http-status-codes'; - import {sleep} from './helpers.js'; import * as constants from './constants.js'; -import {type ConfigManager} from './config_manager.js'; -import {type SoloLogger} from './logging.js'; +import {ConfigManager} from './config_manager.js'; +import {SoloLogger} from './logging.js'; import {type PodName, type TarCreateFilter} from '../types/aliases.js'; import type {ExtendedNetServer, LocalContextObject} from '../types/index.js'; import {HEDERA_HAPI_PATH, ROOT_CONTAINER, SOLO_LOGS_DIR} from './constants.js'; import {Duration} from './time/duration.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; interface TDirectoryData { directory: boolean; @@ -53,6 +54,7 @@ interface TDirectoryData { * Note: Take care if the same instance is used for parallel execution, as the behaviour may be unpredictable. * For parallel execution, create separate instances by invoking clone() */ +@injectable() export class K8 { private _cachedContexts: Context[]; @@ -65,24 +67,15 @@ export class K8 { private coordinationApiClient: k8s.CoordinationV1Api; constructor( - private readonly configManager: ConfigManager, - public readonly logger: SoloLogger, + @inject(ConfigManager) private readonly configManager?: ConfigManager, + @inject(SoloLogger) public readonly logger?: SoloLogger, ) { - if (!configManager) throw new MissingArgumentError('An instance of core/ConfigManager is required'); - if (!logger) throw new MissingArgumentError('An instance of core/SoloLogger is required'); + this.configManager = patchInject(configManager, ConfigManager, this.constructor.name); + this.logger = patchInject(logger, SoloLogger, this.constructor.name); this.init(); } - /** - * Clone a new instance with the same config manager and logger - * Internally it instantiates a new kube API client - */ - clone() { - const c = new K8(this.configManager, this.logger); - return c.init(); - } - getKubeConfig() { return this.kubeConfig; } diff --git a/src/core/key_manager.ts b/src/core/key_manager.ts index b55500bf6..98ac6759f 100644 --- a/src/core/key_manager.ts +++ b/src/core/key_manager.ts @@ -27,10 +27,13 @@ import chalk from 'chalk'; import {type NodeAlias, type NodeAliases} from '../types/aliases.js'; import {type NodeKeyObject, type PrivateKeyAndCertificateObject} from '../types/index.js'; import type {ListrTask} from 'listr2'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; // @ts-ignore x509.cryptoProvider.set(crypto); +@injectable() export class KeyManager { static SigningKeyAlgo = { name: 'RSASSA-PKCS1-v1_5', @@ -60,10 +63,8 @@ export class KeyManager { hash: 'SHA-384', }; - constructor(private readonly logger: SoloLogger) { - if (!logger || !(logger instanceof SoloLogger)) - throw new MissingArgumentError('An instance of core/SoloLogger is required'); - this.logger = logger; + constructor(@inject(SoloLogger) private readonly logger?: SoloLogger) { + this.logger = patchInject(logger, SoloLogger, this.constructor.name); } /** Convert CryptoKey into PEM string */ diff --git a/src/core/lease/interval_lease_renewal.ts b/src/core/lease/interval_lease_renewal.ts index caf6c039c..c5be27005 100644 --- a/src/core/lease/interval_lease_renewal.ts +++ b/src/core/lease/interval_lease_renewal.ts @@ -16,11 +16,13 @@ */ import {type Lease, type LeaseRenewalService} from './lease.js'; import {Duration} from '../time/duration.js'; +import {injectable} from 'tsyringe-neo'; /** * Implements a lease renewal service which utilizes a setInterval() based approach to renew leases at regular intervals. * The renewal delay is calculated as half the duration of the lease in seconds. */ +@injectable() export class IntervalLeaseRenewalService implements LeaseRenewalService { /** The internal registry used to track all non-cancelled lease renewals. */ private readonly _scheduledLeases: Map; diff --git a/src/core/lease/lease_manager.ts b/src/core/lease/lease_manager.ts index 7d80f867c..754b2594c 100644 --- a/src/core/lease/lease_manager.ts +++ b/src/core/lease/lease_manager.ts @@ -14,47 +14,40 @@ * limitations under the License. * */ -import {MissingArgumentError} from '../errors.js'; import {Flags as flags} from '../../commands/flags.js'; -import type {ConfigManager} from '../config_manager.js'; -import type {K8} from '../k8.js'; -import type {SoloLogger} from '../logging.js'; +import {ConfigManager} from '../config_manager.js'; +import {K8} from '../k8.js'; +import {SoloLogger} from '../logging.js'; import {type Lease, type LeaseRenewalService} from './lease.js'; import {IntervalLease} from './interval_lease.js'; import {LeaseHolder} from './lease_holder.js'; import {LeaseAcquisitionError} from './lease_errors.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from '../container_helper.js'; /** * Manages the acquisition and renewal of leases. */ +@injectable() export class LeaseManager { - /** The injected logger instance. */ - private readonly _logger: SoloLogger; - - /** The injected lease renewal service instance. */ - private readonly _renewalService: LeaseRenewalService; - /** * Creates a new lease manager. * + * @param _renewalService - the lease renewal service. + * @param _logger - the logger. * @param k8 - the Kubernetes client. * @param configManager - the configuration manager. - * @param logger - the logger. - * @param renewalService - the lease renewal service. */ constructor( - private readonly k8: K8, - private readonly configManager: ConfigManager, - logger: SoloLogger, - renewalService: LeaseRenewalService, + @inject('LeaseRenewalService') private readonly _renewalService?: LeaseRenewalService, + @inject(SoloLogger) private readonly _logger?: SoloLogger, + @inject(K8) private readonly k8?: K8, + @inject(ConfigManager) private readonly configManager?: ConfigManager, ) { - if (!k8) throw new MissingArgumentError('an instance of core/K8 is required'); - if (!logger) throw new MissingArgumentError('an instance of core/SoloLogger is required'); - if (!configManager) throw new MissingArgumentError('an instance of core/ConfigManager is required'); - if (!renewalService) throw new MissingArgumentError('an instance of core/LeaseRenewalService is required'); - - this._logger = logger; - this._renewalService = renewalService; + this._renewalService = patchInject(_renewalService, 'LeaseRenewalService', this.constructor.name); + this._logger = patchInject(_logger, SoloLogger, this.constructor.name); + this.k8 = patchInject(k8, K8, this.constructor.name); + this.configManager = patchInject(configManager, ConfigManager, this.constructor.name); } /** diff --git a/src/core/logging.ts b/src/core/logging.ts index 46c71f4d3..eb4358278 100644 --- a/src/core/logging.ts +++ b/src/core/logging.ts @@ -20,6 +20,8 @@ import * as util from 'util'; import chalk from 'chalk'; import path from 'path'; import * as constants from './constants.js'; +import {inject, injectable, Lifecycle, registry, scoped, singleton} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; const customFormat = winston.format.combine( winston.format.label({label: 'SOLO', message: false}), @@ -47,35 +49,28 @@ const customFormat = winston.format.combine( winston.format(data => (data.private ? false : data))(), ); +@injectable() export class SoloLogger { private winstonLogger: winston.Logger; private traceId?: string; /** - * @param [level] - logging level as supported by winston library: - * @param [devMode] - if true, show full stack traces in error messages + * @param logLevel - the log level to use + * @param devMode - if true, show full stack traces in error messages */ constructor( - level = 'debug', - private devMode = false, + @inject('logLevel') logLevel?: string, + @inject('devMode') private devMode?: boolean | null, ) { + logLevel = patchInject(logLevel, 'logLevel', this.constructor.name); + this.devMode = patchInject(devMode, 'devMode', this.constructor.name); + this.nextTraceId(); - this.devMode = devMode; this.winstonLogger = winston.createLogger({ - level, + level: logLevel, format: winston.format.combine(customFormat, winston.format.json()), - // format: winston.format.json(), - // defaultMeta: { service: 'user-service' }, - transports: [ - // - // - Write all logs with importance level of `error` or less to `error.log` - // - Write all logs with importance level of `info` or less to `solo.log` - // - new winston.transports.File({filename: path.join(constants.SOLO_LOGS_DIR, 'solo.log')}), - // new winston.transports.File({filename: constants.TMP_DIR + "/logs/error.log", level: 'error'}), - // new winston.transports.Console({format: customFormat}) - ], + transports: [new winston.transports.File({filename: path.join(constants.SOLO_LOGS_DIR, 'solo.log')})], }); } diff --git a/src/core/package_downloader.ts b/src/core/package_downloader.ts index 5abd01b13..51289b0d6 100644 --- a/src/core/package_downloader.ts +++ b/src/core/package_downloader.ts @@ -30,12 +30,15 @@ import * as https from 'https'; import * as http from 'http'; import {Templates} from './templates.js'; import * as constants from './constants.js'; -import {type SoloLogger} from './logging.js'; +import {SoloLogger} from './logging.js'; import {StatusCodes} from 'http-status-codes'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; +@injectable() export class PackageDownloader { - constructor(public readonly logger: SoloLogger) { - if (!logger) throw new IllegalArgumentError('an instance of core/SoloLogger is required', logger); + constructor(@inject(SoloLogger) public readonly logger?: SoloLogger) { + this.logger = patchInject(logger, SoloLogger, this.constructor.name); } isValidURL(url: string) { diff --git a/src/core/platform_installer.ts b/src/core/platform_installer.ts index d8be49e00..b916c118e 100644 --- a/src/core/platform_installer.ts +++ b/src/core/platform_installer.ts @@ -19,32 +19,31 @@ import {Listr} from 'listr2'; import * as path from 'path'; import {SoloError, IllegalArgumentError, MissingArgumentError} from './errors.js'; import * as constants from './constants.js'; -import {type ConfigManager} from './config_manager.js'; -import {type K8} from './k8.js'; +import {ConfigManager} from './config_manager.js'; +import {K8} from './k8.js'; import {Templates} from './templates.js'; import {Flags as flags} from '../commands/flags.js'; import * as Base64 from 'js-base64'; import chalk from 'chalk'; -import {type SoloLogger} from './logging.js'; +import {SoloLogger} from './logging.js'; import type {NodeAlias, NodeAliases, PodName} from '../types/aliases.js'; import {Duration} from './time/duration.js'; import {sleep} from './helpers.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; /** PlatformInstaller install platform code in the root-container of a network pod */ +@injectable() export class PlatformInstaller { - private logger: SoloLogger; - private k8: K8; - private configManager: ConfigManager; - - constructor(logger: SoloLogger, k8: K8, configManager: ConfigManager) { - if (!logger) throw new MissingArgumentError('an instance of core/SoloLogger is required'); - if (!k8) throw new MissingArgumentError('an instance of core/K8 is required'); - if (!configManager) throw new MissingArgumentError('an instance of core/ConfigManager is required'); - - this.logger = logger; - this.k8 = k8; - this.configManager = configManager; + constructor( + @inject(SoloLogger) private logger?: SoloLogger, + @inject(K8) private k8?: K8, + @inject(ConfigManager) private configManager?: ConfigManager, + ) { + this.logger = patchInject(logger, SoloLogger, this.constructor.name); + this.k8 = patchInject(k8, K8, this.constructor.name); + this.configManager = patchInject(configManager, ConfigManager, this.constructor.name); } private _getNamespace(): string { diff --git a/src/core/profile_manager.ts b/src/core/profile_manager.ts index 61a07d790..3c0c81656 100644 --- a/src/core/profile_manager.ts +++ b/src/core/profile_manager.ts @@ -25,15 +25,17 @@ import {readFile, writeFile} from 'fs/promises'; import {Flags as flags} from '../commands/flags.js'; import {Templates} from './templates.js'; import * as constants from './constants.js'; -import {type ConfigManager} from './config_manager.js'; +import {ConfigManager} from './config_manager.js'; import * as helpers from './helpers.js'; import {getNodeAccountMap} from './helpers.js'; import {AccountId} from '@hashgraph/sdk'; import type {SemVer} from 'semver'; -import type {SoloLogger} from './logging.js'; +import {SoloLogger} from './logging.js'; import type {AnyObject, DirPath, NodeAlias, NodeAliases, Path} from '../types/aliases.js'; import type {GenesisNetworkDataConstructor} from './genesis_network_models/genesis_network_data_constructor.js'; import type {Optional} from '../types/index.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; const consensusSidecars = [ 'recordStreamUploader', @@ -43,6 +45,7 @@ const consensusSidecars = [ 'otelCollector', ]; +@injectable() export class ProfileManager { private readonly logger: SoloLogger; private readonly configManager: ConfigManager; @@ -51,17 +54,16 @@ export class ProfileManager { private profiles: Map; private profileFile: Optional; - constructor(logger: SoloLogger, configManager: ConfigManager, cacheDir: DirPath = constants.SOLO_VALUES_DIR) { - if (!logger) throw new MissingArgumentError('An instance of core/SoloLogger is required'); - if (!configManager) throw new MissingArgumentError('An instance of core/ConfigManager is required'); - - this.logger = logger; - this.configManager = configManager; + constructor( + @inject(SoloLogger) logger?: SoloLogger, + @inject(ConfigManager) configManager?: ConfigManager, + @inject('cacheDir') cacheDir?: DirPath, + ) { + this.logger = patchInject(logger, SoloLogger, this.constructor.name); + this.configManager = patchInject(configManager, ConfigManager, this.constructor.name); + this.cacheDir = path.resolve(patchInject(cacheDir, 'cacheDir', this.constructor.name)); this.profiles = new Map(); - - cacheDir = path.resolve(cacheDir); - this.cacheDir = cacheDir; } /** diff --git a/src/core/shell_runner.ts b/src/core/shell_runner.ts index dc19f18c0..49c9c12d4 100644 --- a/src/core/shell_runner.ts +++ b/src/core/shell_runner.ts @@ -16,11 +16,14 @@ */ import {spawn} from 'child_process'; import chalk from 'chalk'; -import {type SoloLogger} from './logging.js'; +import {SoloLogger} from './logging.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; +@injectable() export class ShellRunner { - constructor(public logger: SoloLogger) { - if (!logger) throw new Error('An instance of core/SoloLogger is required'); + constructor(@inject(SoloLogger) public logger?: SoloLogger) { + this.logger = patchInject(logger, SoloLogger, this.constructor.name); } /** Returns a promise that invokes the shell command */ diff --git a/src/core/zippy.ts b/src/core/zippy.ts index 152258df0..d1bac6aca 100644 --- a/src/core/zippy.ts +++ b/src/core/zippy.ts @@ -20,11 +20,14 @@ import AdmZip from 'adm-zip'; import * as tar from 'tar'; import chalk from 'chalk'; import path from 'path'; -import type {SoloLogger} from './logging.js'; +import {SoloLogger} from './logging.js'; +import {inject, injectable} from 'tsyringe-neo'; +import {patchInject} from './container_helper.js'; +@injectable() export class Zippy { - constructor(private readonly logger: SoloLogger) { - if (!logger) throw new Error('An instance of core/SoloLogger is required'); + constructor(@inject(SoloLogger) private readonly logger?: SoloLogger) { + this.logger = patchInject(logger, SoloLogger, this.constructor.name); } /** diff --git a/src/index.ts b/src/index.ts index 386fa8639..8e68d3e55 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,14 +19,17 @@ import yargs from 'yargs'; import {hideBin} from 'yargs/helpers'; import 'dotenv/config'; import path from 'path'; +// eslint-disable-next-line n/no-extraneous-import +import 'reflect-metadata'; +import {container} from 'tsyringe-neo'; +import './core/container_init.js'; import {ListrLogger} from 'listr2'; import {Flags as flags} from './commands/flags.js'; import * as commands from './commands/index.js'; -import {HelmDependencyManager, DependencyManager} from './core/dependency_managers/index.js'; +import {DependencyManager} from './core/dependency_managers/index.js'; import * as constants from './core/constants.js'; import {PackageDownloader} from './core/package_downloader.js'; -import {Zippy} from './core/zippy.js'; import {Helm} from './core/helm.js'; import {ChartManager} from './core/chart_manager.js'; import {ConfigManager} from './core/config_manager.js'; @@ -38,16 +41,17 @@ import {LeaseManager} from './core/lease/lease_manager.js'; import {CertificateManager} from './core/certificate_manager.js'; import {LocalConfig} from './core/config/local_config.js'; import {RemoteConfigManager} from './core/config/remote/remote_config_manager.js'; -import * as logging from './core/logging.js'; import * as helpers from './core/helpers.js'; import {K8} from './core/k8.js'; import {CustomProcessOutput} from './core/process_output.js'; import {type Opts} from './types/command_types.js'; -import {IntervalLeaseRenewalService} from './core/lease/interval_lease_renewal.js'; -import {type LeaseRenewalService} from './core/lease/lease.js'; +import {SoloLogger} from './core/logging.js'; +import {Container} from './core/container_init.js'; export function main(argv: any) { - const logger = logging.NewLogger('debug'); + Container.getInstance().init(); + + const logger = container.resolve(SoloLogger); constants.LISTR_DEFAULT_RENDERER_OPTION.logger = new ListrLogger({processOutput: new CustomProcessOutput(logger)}); if (argv.length >= 3 && ['-version', '--version', '-v', '--v'].includes(argv[2])) { logger.showUser(chalk.cyan('\n******************************* Solo *********************************************')); @@ -58,26 +62,20 @@ export function main(argv: any) { try { // prepare dependency manger registry - const downloader = new PackageDownloader(logger); - const zippy = new Zippy(logger); - const helmDepManager = new HelmDependencyManager(downloader, zippy, logger); - const depManagerMap = new Map().set(constants.HELM, helmDepManager); - const depManager = new DependencyManager(logger, depManagerMap); - - const helm = new Helm(logger); - const chartManager = new ChartManager(helm, logger); - const configManager = new ConfigManager(logger); - const k8 = new K8(configManager, logger); - const accountManager = new AccountManager(logger, k8); - const platformInstaller = new PlatformInstaller(logger, k8, configManager); - const keyManager = new KeyManager(logger); - const profileManager = new ProfileManager(logger, configManager); - const leaseRenewalService: LeaseRenewalService = new IntervalLeaseRenewalService(); - const leaseManager = new LeaseManager(k8, configManager, logger, leaseRenewalService); - const certificateManager = new CertificateManager(k8, logger, configManager); - const localConfigPath = path.join(constants.SOLO_CACHE_DIR, constants.DEFAULT_LOCAL_CONFIG_FILE); - const localConfig = new LocalConfig(localConfigPath, logger, configManager); - const remoteConfigManager = new RemoteConfigManager(k8, logger, localConfig, configManager); + const downloader = container.resolve(PackageDownloader); + const depManager = container.resolve(DependencyManager); + const helm = container.resolve(Helm); + const chartManager = container.resolve(ChartManager); + const configManager = container.resolve(ConfigManager); + const k8 = container.resolve(K8); + const accountManager = container.resolve(AccountManager); + const platformInstaller = container.resolve(PlatformInstaller); + const keyManager = container.resolve(KeyManager); + const profileManager = container.resolve(ProfileManager); + const leaseManager = container.resolve(LeaseManager); + const certificateManager = container.resolve(CertificateManager); + const localConfig = container.resolve(LocalConfig); + const remoteConfigManager = container.resolve(RemoteConfigManager); // set cluster and namespace in the global configManager from kubernetes context // so that we don't need to prompt the user diff --git a/test/e2e/commands/account.test.ts b/test/e2e/commands/account.test.ts index 5abcad3b5..bb36d4272 100644 --- a/test/e2e/commands/account.test.ts +++ b/test/e2e/commands/account.test.ts @@ -32,17 +32,14 @@ import { } from '@hashgraph/sdk'; import * as constants from '../../../src/core/constants.js'; import * as version from '../../../version.js'; -import { - bootstrapTestVariables, - e2eTestSuite, - getDefaultArgv, - HEDERA_PLATFORM_VERSION_TAG, - TEST_CLUSTER, - testLogger, -} from '../../test_util.js'; +import {e2eTestSuite, getDefaultArgv, HEDERA_PLATFORM_VERSION_TAG, TEST_CLUSTER, testLogger} from '../../test_util.js'; import {AccountCommand} from '../../../src/commands/account.js'; import {Flags as flags} from '../../../src/commands/flags.js'; import {Duration} from '../../../src/core/time/duration.js'; +import {type K8} from '../../../src/core/k8.js'; +import {type AccountManager} from '../../../src/core/account_manager.js'; +import {type ConfigManager} from '../../../src/core/config_manager.js'; +import {type NodeCommand} from '../../../src/commands/node/index.js'; const defaultTimeout = Duration.ofSeconds(20).toMillis(); @@ -62,12 +59,20 @@ argv[flags.chartDirectory.name] = process.env.SOLO_CHARTS_DIR ?? undefined; e2eTestSuite(testName, argv, undefined, undefined, undefined, undefined, undefined, undefined, true, bootstrapResp => { describe('AccountCommand', async () => { - const accountCmd = new AccountCommand(bootstrapResp.opts, testSystemAccounts); - bootstrapResp.cmd.accountCmd = accountCmd; - const k8 = bootstrapResp.opts.k8; - const accountManager = bootstrapResp.opts.accountManager; - const configManager = bootstrapResp.opts.configManager; - const nodeCmd = bootstrapResp.cmd.nodeCmd; + let accountCmd: AccountCommand; + let k8: K8; + let accountManager: AccountManager; + let configManager: ConfigManager; + let nodeCmd: NodeCommand; + + before(() => { + accountCmd = new AccountCommand(bootstrapResp.opts, testSystemAccounts); + bootstrapResp.cmd.accountCmd = accountCmd; + k8 = bootstrapResp.opts.k8; + accountManager = bootstrapResp.opts.accountManager; + configManager = bootstrapResp.opts.configManager; + nodeCmd = bootstrapResp.cmd.nodeCmd; + }); after(async function () { this.timeout(Duration.ofMinutes(3).toMillis()); diff --git a/test/e2e/commands/node_local_hedera.test.ts b/test/e2e/commands/node_local_hedera.test.ts index b0bc101ab..556e70dcd 100644 --- a/test/e2e/commands/node_local_hedera.test.ts +++ b/test/e2e/commands/node_local_hedera.test.ts @@ -25,6 +25,9 @@ import path from 'path'; import {expect} from 'chai'; import {AccountBalanceQuery, AccountCreateTransaction, Hbar, HbarUnit, PrivateKey} from '@hashgraph/sdk'; import {Duration} from '../../../src/core/time/duration.js'; +import {type NodeCommand} from '../../../src/commands/node/index.js'; +import {type AccountCommand} from '../../../src/commands/account.js'; +import {type AccountManager} from '../../../src/core/account_manager.js'; const LOCAL_HEDERA = 'local-hedera-app'; const argv = getDefaultArgv(); @@ -52,11 +55,17 @@ e2eTestSuite( undefined, true, bootstrapResp => { - const nodeCmd = bootstrapResp.cmd.nodeCmd; - const accountCmd = bootstrapResp.cmd.accountCmd; - const accountManager = bootstrapResp.manager.accountManager; describe('Node for hedera app should have started successfully', () => { - hederaK8 = bootstrapResp.opts.k8; + let nodeCmd: NodeCommand; + let accountCmd: AccountCommand; + let accountManager: AccountManager; + + before(() => { + nodeCmd = bootstrapResp.cmd.nodeCmd; + accountCmd = bootstrapResp.cmd.accountCmd; + accountManager = bootstrapResp.manager.accountManager; + hederaK8 = bootstrapResp.opts.k8; + }); it('save the state and restart the node with saved state', async () => { // create an account so later we can verify its balance after restart diff --git a/test/e2e/commands/node_local_ptt.test.ts b/test/e2e/commands/node_local_ptt.test.ts index 20d426306..341d572fa 100644 --- a/test/e2e/commands/node_local_ptt.test.ts +++ b/test/e2e/commands/node_local_ptt.test.ts @@ -19,6 +19,7 @@ import {describe} from 'mocha'; import {Flags as flags} from '../../../src/commands/flags.js'; import {e2eTestSuite, getDefaultArgv, TEST_CLUSTER} from '../../test_util.js'; import {Duration} from '../../../src/core/time/duration.js'; +import {type K8} from '../../../src/core/k8.js'; const LOCAL_PTT = 'local-ptt-app'; const argv = getDefaultArgv(); @@ -39,7 +40,11 @@ argv[flags.namespace.name] = LOCAL_PTT; e2eTestSuite(LOCAL_PTT, argv, undefined, undefined, undefined, undefined, undefined, undefined, true, bootstrapResp => { describe('Node for platform app should start successfully', () => { - const pttK8 = bootstrapResp.opts.k8; + let pttK8: K8; + + before(() => { + pttK8 = bootstrapResp.opts.k8; + }); it('get the logs and delete the namespace', async () => { await pttK8.getNodeLogs(LOCAL_PTT); diff --git a/test/e2e/e2e_node_util.ts b/test/e2e/e2e_node_util.ts index 19d135891..fd6585eae 100644 --- a/test/e2e/e2e_node_util.ts +++ b/test/e2e/e2e_node_util.ts @@ -36,6 +36,7 @@ import {type K8} from '../../src/core/k8.js'; import {type NodeCommand} from '../../src/commands/node/index.js'; import {Duration} from '../../src/core/time/duration.js'; import {StatusCodes} from 'http-status-codes'; +import {container} from 'tsyringe-neo'; export function e2eNodeKeyRefreshTest(testName: string, mode: string, releaseTag = HEDERA_PLATFORM_VERSION_TAG) { const namespace = testName; @@ -191,7 +192,7 @@ export function e2eNodeKeyRefreshTest(testName: string, mode: string, releaseTag async function nodeRefreshTestSetup(argv: Record, testName: string, k8: K8, nodeAliases: string) { argv[flags.nodeAliasesUnparsed.name] = nodeAliases; - const configManager = new ConfigManager(testLogger); + const configManager = container.resolve(ConfigManager); configManager.update(argv); const podArray = await k8.getPodsByLabel([`app=network-${nodeAliases}`, 'solo.hedera.com/type=network-node']); diff --git a/test/e2e/integration/commands/init.test.ts b/test/e2e/integration/commands/init.test.ts index 64272c0f3..ac9605a6a 100644 --- a/test/e2e/integration/commands/init.test.ts +++ b/test/e2e/integration/commands/init.test.ts @@ -18,9 +18,7 @@ import {describe, it} from 'mocha'; import {expect} from 'chai'; import {InitCommand} from '../../../../src/commands/init.js'; -import {HelmDependencyManager, DependencyManager} from '../../../../src/core/dependency_managers/index.js'; -import {Zippy} from '../../../../src/core/zippy.js'; -import * as constants from '../../../../src/core/constants.js'; +import {DependencyManager} from '../../../../src/core/dependency_managers/index.js'; import {Helm} from '../../../../src/core/helm.js'; import {ChartManager} from '../../../../src/core/chart_manager.js'; import {ConfigManager} from '../../../../src/core/config_manager.js'; @@ -30,29 +28,23 @@ import {KeyManager} from '../../../../src/core/key_manager.js'; import {LeaseManager} from '../../../../src/core/lease/lease_manager.js'; import {RemoteConfigManager} from '../../../../src/core/config/remote/remote_config_manager.js'; import * as logging from '../../../../src/core/logging.js'; -import {PackageDownloader} from '../../../../src/core/package_downloader.js'; import sinon from 'sinon'; -import {IntervalLeaseRenewalService} from '../../../../src/core/lease/interval_lease_renewal.js'; import path from 'path'; import {BASE_TEST_DIR} from '../../../test_util.js'; import {Duration} from '../../../../src/core/time/duration.js'; +import {container} from 'tsyringe-neo'; const testLogger = logging.NewLogger('debug', true); describe('InitCommand', () => { - // prepare dependency manger registry - const downloader = new PackageDownloader(testLogger); - const zippy = new Zippy(testLogger); - const helmDepManager = new HelmDependencyManager(downloader, zippy, testLogger); - const depManagerMap = new Map().set(constants.HELM, helmDepManager); - const depManager = new DependencyManager(testLogger, depManagerMap); + const depManager = container.resolve(DependencyManager); + const helm = container.resolve(Helm); + const chartManager = container.resolve(ChartManager); - const helm = new Helm(testLogger); - const chartManager = new ChartManager(helm, testLogger); - const configManager = new ConfigManager(testLogger); + const configManager = container.resolve(ConfigManager); let k8: K8; let localConfig: LocalConfig; - const keyManager = new KeyManager(testLogger); + const keyManager = container.resolve(KeyManager); let leaseManager: LeaseManager; let remoteConfigManager: RemoteConfigManager; @@ -63,10 +55,11 @@ describe('InitCommand', () => { before(() => { sandbox = sinon.createSandbox(); sandbox.stub(K8.prototype, 'init').callsFake(() => this); - k8 = new K8(configManager, testLogger); - localConfig = new LocalConfig(path.join(BASE_TEST_DIR, 'local-config.yaml'), testLogger, configManager); - remoteConfigManager = new RemoteConfigManager(k8, testLogger, localConfig, configManager); - leaseManager = new LeaseManager(k8, configManager, testLogger, new IntervalLeaseRenewalService()); + k8 = container.resolve(K8); + localConfig = new LocalConfig(path.join(BASE_TEST_DIR, 'local-config.yaml')); + remoteConfigManager = container.resolve(RemoteConfigManager); + leaseManager = container.resolve(LeaseManager); + // @ts-ignore initCmd = new InitCommand({ logger: testLogger, diff --git a/test/e2e/integration/core/account_manager.test.ts b/test/e2e/integration/core/account_manager.test.ts index 4ab5b5582..5251ce2d6 100644 --- a/test/e2e/integration/core/account_manager.test.ts +++ b/test/e2e/integration/core/account_manager.test.ts @@ -22,6 +22,8 @@ import {e2eTestSuite, getDefaultArgv, TEST_CLUSTER} from '../../../test_util.js' import * as version from '../../../../version.js'; import type {PodName} from '../../../../src/types/aliases.js'; import {Duration} from '../../../../src/core/time/duration.js'; +import {type K8} from '../../../../src/core/k8.js'; +import {type AccountManager} from '../../../../src/core/account_manager.js'; const namespace = 'account-mngr-e2e'; const argv = getDefaultArgv(); @@ -36,8 +38,13 @@ argv[flags.chartDirectory.name] = process.env.SOLO_CHARTS_DIR ?? undefined; e2eTestSuite(namespace, argv, undefined, undefined, undefined, undefined, undefined, undefined, true, bootstrapResp => { describe('AccountManager', async () => { - const k8 = bootstrapResp.opts.k8; - const accountManager = bootstrapResp.opts.accountManager; + let k8: K8; + let accountManager: AccountManager; + + before(() => { + k8 = bootstrapResp.opts.k8; + accountManager = bootstrapResp.opts.accountManager; + }); after(async function () { this.timeout(Duration.ofMinutes(3).toMillis()); diff --git a/test/e2e/integration/core/chart_manager.test.ts b/test/e2e/integration/core/chart_manager.test.ts index 79d9d5e83..72399eed9 100644 --- a/test/e2e/integration/core/chart_manager.test.ts +++ b/test/e2e/integration/core/chart_manager.test.ts @@ -21,10 +21,10 @@ import * as constants from '../../../../src/core/constants.js'; import {Helm} from '../../../../src/core/helm.js'; import {ChartManager} from '../../../../src/core/chart_manager.js'; import {testLogger} from '../../../test_util.js'; +import {container} from 'tsyringe-neo'; describe('ChartManager', () => { - const helm = new Helm(testLogger); - const chartManager = new ChartManager(helm, testLogger); + const chartManager = container.resolve(ChartManager); it('should be able to list installed charts', async () => { const ns = constants.SOLO_SETUP_NAMESPACE; diff --git a/test/e2e/integration/core/dependency_managers/helm_dependency_manager.test.ts b/test/e2e/integration/core/dependency_managers/helm_dependency_manager.test.ts index 0a360e275..35d38e70b 100644 --- a/test/e2e/integration/core/dependency_managers/helm_dependency_manager.test.ts +++ b/test/e2e/integration/core/dependency_managers/helm_dependency_manager.test.ts @@ -40,17 +40,17 @@ describe('HelmDependencyManager', () => { }); it('should return helm version', () => { - const helmDependencyManager = new HelmDependencyManager(downloader, zippy, testLogger, tmpDir); + const helmDependencyManager = new HelmDependencyManager(undefined, undefined, tmpDir); expect(helmDependencyManager.getHelmVersion()).to.equal(version.HELM_VERSION); }); it('should be able to check when helm not installed', () => { - const helmDependencyManager = new HelmDependencyManager(downloader, zippy, testLogger, tmpDir); + const helmDependencyManager = new HelmDependencyManager(undefined, undefined, tmpDir); expect(helmDependencyManager.isInstalled()).not.to.be.ok; }); it('should be able to check when helm is installed', () => { - const helmDependencyManager = new HelmDependencyManager(downloader, zippy, testLogger, tmpDir); + const helmDependencyManager = new HelmDependencyManager(undefined, undefined, tmpDir); fs.writeFileSync(helmDependencyManager.getHelmPath(), ''); expect(helmDependencyManager.isInstalled()).to.be.ok; }); @@ -63,9 +63,8 @@ describe('HelmDependencyManager', () => { // { osRelease: 'windows', osArch: 'amd64' } ]).it('should be able to install helm base on os and architecture', async input => { const helmDependencyManager = new HelmDependencyManager( - downloader, - zippy, - testLogger, + undefined, + undefined, tmpDir, input.osPlatform, input.osArch, diff --git a/test/e2e/integration/core/k8_e2e.test.ts b/test/e2e/integration/core/k8_e2e.test.ts index f4642039a..95f513f2d 100644 --- a/test/e2e/integration/core/k8_e2e.test.ts +++ b/test/e2e/integration/core/k8_e2e.test.ts @@ -47,6 +47,7 @@ import { import crypto from 'crypto'; import type {PodName} from '../../../../src/types/aliases.js'; import {Duration} from '../../../../src/core/time/duration.js'; +import {container} from 'tsyringe-neo'; const defaultTimeout = Duration.ofMinutes(2).toMillis(); @@ -80,8 +81,8 @@ async function createPod( describe('K8', () => { const testLogger = logging.NewLogger('debug', true); - const configManager = new ConfigManager(testLogger); - const k8 = new K8(configManager, testLogger); + const configManager = container.resolve(ConfigManager); + const k8 = container.resolve(K8); const testNamespace = 'k8-e2e'; const argv = []; const podName = `test-pod-${uuid4()}` as PodName; diff --git a/test/e2e/integration/core/lease.test.ts b/test/e2e/integration/core/lease.test.ts index 27ef8cbde..773733f5a 100644 --- a/test/e2e/integration/core/lease.test.ts +++ b/test/e2e/integration/core/lease.test.ts @@ -25,14 +25,15 @@ import {sleep} from '../../../../src/core/helpers.js'; import {LeaseRelinquishmentError} from '../../../../src/core/lease/lease_errors.js'; import {NoopLeaseRenewalService} from './noop_lease_renewal_service.test.js'; import {Duration} from '../../../../src/core/time/duration.js'; +import {container} from 'tsyringe-neo'; const defaultTimeout = Duration.ofMinutes(2).toMillis(); const leaseDuration = 4; describe('Lease', async () => { const testLogger = logging.NewLogger('debug', true); - const configManager = new ConfigManager(testLogger); - const k8 = new K8(configManager, testLogger); + const configManager = container.resolve(ConfigManager); + const k8 = container.resolve(K8); const testNamespace = 'lease-e2e'; const renewalService = new NoopLeaseRenewalService(); diff --git a/test/e2e/integration/core/lease_renewal.test.ts b/test/e2e/integration/core/lease_renewal.test.ts index 27cebceff..e1e508e20 100644 --- a/test/e2e/integration/core/lease_renewal.test.ts +++ b/test/e2e/integration/core/lease_renewal.test.ts @@ -25,16 +25,17 @@ import {sleep} from '../../../../src/core/helpers.js'; import {IntervalLeaseRenewalService} from '../../../../src/core/lease/interval_lease_renewal.js'; import {type V1Lease} from '@kubernetes/client-node'; import {Duration} from '../../../../src/core/time/duration.js'; +import {container} from 'tsyringe-neo'; const defaultTimeout = Duration.ofMinutes(2).toMillis(); const leaseDuration = 4; describe('LeaseRenewalService', async () => { const testLogger = logging.NewLogger('debug', true); - const configManager = new ConfigManager(testLogger); - const k8 = new K8(configManager, testLogger); + const configManager = container.resolve(ConfigManager); + const k8 = container.resolve(K8); + const renewalService = container.resolve(IntervalLeaseRenewalService); const testNamespace = 'lease-renewal-e2e'; - const renewalService = new IntervalLeaseRenewalService(); before(async function () { this.timeout(defaultTimeout); diff --git a/test/e2e/integration/core/platform_installer_e2e.test.ts b/test/e2e/integration/core/platform_installer_e2e.test.ts index c7cf04720..721c3f591 100644 --- a/test/e2e/integration/core/platform_installer_e2e.test.ts +++ b/test/e2e/integration/core/platform_installer_e2e.test.ts @@ -24,6 +24,9 @@ import {e2eTestSuite, getDefaultArgv, getTestCacheDir, TEST_CLUSTER, testLogger} import {Flags as flags} from '../../../../src/commands/flags.js'; import * as version from '../../../../version.js'; import {Duration} from '../../../../src/core/time/duration.js'; +import {type K8} from '../../../../src/core/k8.js'; +import {type AccountManager} from '../../../../src/core/account_manager.js'; +import {type PlatformInstaller} from '../../../../src/core/platform_installer.js'; const defaultTimeout = Duration.ofSeconds(20).toMillis(); @@ -51,13 +54,19 @@ e2eTestSuite( undefined, false, bootstrapResp => { - describe('PackageInstallerE2E', async () => { - const k8 = bootstrapResp.opts.k8; - const accountManager = bootstrapResp.opts.accountManager; - const installer = bootstrapResp.opts.platformInstaller; + describe('Platform Installer E2E', async () => { + let k8: K8; + let accountManager: AccountManager; + let installer: PlatformInstaller; const podName = 'network-node1-0'; const packageVersion = 'v0.42.5'; + before(() => { + k8 = bootstrapResp.opts.k8; + accountManager = bootstrapResp.opts.accountManager; + installer = bootstrapResp.opts.platformInstaller; + }); + after(async function () { this.timeout(Duration.ofMinutes(3).toMillis()); diff --git a/test/e2e/integration/core/remote_config_manager.test.ts b/test/e2e/integration/core/remote_config_manager.test.ts index 8098bb2c1..f7206f770 100644 --- a/test/e2e/integration/core/remote_config_manager.test.ts +++ b/test/e2e/integration/core/remote_config_manager.test.ts @@ -18,17 +18,17 @@ import {it, describe, after, before} from 'mocha'; import {expect} from 'chai'; import * as fs from 'fs'; -import * as constants from '../../../../src/core/constants.js'; import {LocalConfig} from '../../../../src/core/config/local_config.js'; import {RemoteConfigManager} from '../../../../src/core/config/remote/remote_config_manager.js'; import {e2eTestSuite, getDefaultArgv, getTestCacheDir, TEST_CLUSTER} from '../../../test_util.js'; import {Flags as flags} from '../../../../src/commands/flags.js'; import * as version from '../../../../version.js'; -import path from 'path'; import {SoloError} from '../../../../src/core/errors.js'; import {RemoteConfigDataWrapper} from '../../../../src/core/config/remote/remote_config_data_wrapper.js'; import {Duration} from '../../../../src/core/time/duration.js'; +import {container} from 'tsyringe-neo'; +import {type K8} from '../../../../src/core/k8.js'; const defaultTimeout = Duration.ofSeconds(20).toMillis(); @@ -57,20 +57,13 @@ e2eTestSuite( false, bootstrapResp => { describe('RemoteConfigManager', async () => { - const k8 = bootstrapResp.opts.k8; - const logger = bootstrapResp.opts.logger; - const configManager = bootstrapResp.opts.configManager; - const filePath = path.join(constants.SOLO_CACHE_DIR, constants.DEFAULT_LOCAL_CONFIG_FILE); + let k8: K8; - const localConfig = new LocalConfig(filePath, logger, configManager); - const remoteConfigManager = new RemoteConfigManager(k8, logger, localConfig, configManager); + let localConfig: LocalConfig; + let remoteConfigManager: RemoteConfigManager; const email = 'john@gmail.com'; - localConfig.userEmailAddress = email; - localConfig.deployments = {[namespace]: {clusters: [`kind-${namespace}`]}}; - localConfig.currentDeploymentName = namespace; - after(async function () { this.timeout(Duration.ofMinutes(3).toMillis()); await k8.deleteNamespace(namespace); @@ -79,6 +72,14 @@ e2eTestSuite( before(function () { this.timeout(defaultTimeout); + k8 = bootstrapResp.opts.k8; + localConfig = container.resolve(LocalConfig); + remoteConfigManager = container.resolve(RemoteConfigManager); + + localConfig.userEmailAddress = email; + localConfig.deployments = {[namespace]: {clusters: [`kind-${namespace}`]}}; + localConfig.currentDeploymentName = namespace; + if (!fs.existsSync(testCacheDir)) { fs.mkdirSync(testCacheDir); } diff --git a/test/e2e/integration/core/remote_config_validator.test.ts b/test/e2e/integration/core/remote_config_validator.test.ts index fc3cf49b4..312a48a4d 100644 --- a/test/e2e/integration/core/remote_config_validator.test.ts +++ b/test/e2e/integration/core/remote_config_validator.test.ts @@ -21,7 +21,6 @@ import * as constants from '../../../../src/core/constants.js'; import {ConfigManager} from '../../../../src/core/config_manager.js'; import {K8} from '../../../../src/core/k8.js'; import {Templates} from '../../../../src/core/templates.js'; -import {testLogger} from '../../../test_util.js'; import {Flags as flags} from '../../../../src/commands/flags.js'; import {V1Container, V1ExecAction, V1ObjectMeta, V1Pod, V1PodSpec, V1Probe} from '@kubernetes/client-node'; import {RemoteConfigValidator} from '../../../../src/core/config/remote/remote_config_validator.js'; @@ -36,15 +35,18 @@ import {MirrorNodeExplorerComponent} from '../../../../src/core/config/remote/co import {EnvoyProxyComponent} from '../../../../src/core/config/remote/components/envoy_proxy_component.js'; import type {NodeAlias, NodeAliases} from '../../../../src/types/aliases.js'; +import {container} from 'tsyringe-neo'; describe('RemoteConfigValidator', () => { const namespace = 'remote-config-validator'; - const configManager = new ConfigManager(testLogger); - configManager.update({[flags.namespace.name]: namespace}); - const k8 = new K8(configManager, testLogger); + let configManager: ConfigManager; + let k8: K8; before(async () => { + configManager = container.resolve(ConfigManager); + configManager.update({[flags.namespace.name]: namespace}); + k8 = container.resolve(K8); await k8.createNamespace(namespace); }); diff --git a/test/setup.ts b/test/setup.ts index c4c4ddb84..8d7dd801d 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -14,9 +14,14 @@ * limitations under the License. * */ +// eslint-disable-next-line n/no-extraneous-import +import 'reflect-metadata'; import * as chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; import sinonChai from 'sinon-chai'; +import {resetTestContainer} from './test_container.js'; + +resetTestContainer(); chai.use(chaiAsPromised); chai.use(sinonChai); diff --git a/test/test_container.ts b/test/test_container.ts new file mode 100644 index 000000000..48fab19e7 --- /dev/null +++ b/test/test_container.ts @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import path from 'path'; +import {Container} from '../src/core/container_init.js'; + +const cacheDirectory = path.join('test', 'data', 'tmp'); + +export function resetTestContainer(cacheDir: string = cacheDirectory) { + Container.getInstance().reset(cacheDir, 'debug', true); +} diff --git a/test/test_util.ts b/test/test_util.ts index b6935fc95..d5d427772 100644 --- a/test/test_util.ts +++ b/test/test_util.ts @@ -27,7 +27,7 @@ import {ClusterCommand} from '../src/commands/cluster.js'; import {InitCommand} from '../src/commands/init.js'; import {NetworkCommand} from '../src/commands/network.js'; import {NodeCommand} from '../src/commands/node/index.js'; -import {DependencyManager, HelmDependencyManager} from '../src/core/dependency_managers/index.js'; +import {DependencyManager} from '../src/core/dependency_managers/index.js'; import {sleep} from '../src/core/helpers.js'; import {AccountBalanceQuery, AccountCreateTransaction, Hbar, HbarUnit, PrivateKey} from '@hashgraph/sdk'; import {NODE_LOG_FAILURE_MSG, ROOT_CONTAINER, SOLO_LOGS_DIR} from '../src/core/constants.js'; @@ -36,7 +36,8 @@ import {AccountCommand} from '../src/commands/account.js'; import {SoloError} from '../src/core/errors.js'; import {execSync} from 'child_process'; import * as NodeCommandConfigs from '../src/commands/node/configs.js'; -import type {SoloLogger} from '../src/core/logging.js'; + +import {SoloLogger} from '../src/core/logging.js'; import type {BaseCommand} from '../src/commands/base.js'; import type {NodeAlias} from '../src/types/aliases.js'; import type {NetworkNodeServices} from '../src/core/network_node_services.js'; @@ -51,22 +52,22 @@ import {RemoteConfigManager} from '../src/core/config/remote/remote_config_manag import * as constants from '../src/core/constants.js'; import {Templates} from '../src/core/templates.js'; import {ConfigManager} from '../src/core/config_manager.js'; -import * as logging from '../src/core/logging.js'; import {Helm} from '../src/core/helm.js'; import {ChartManager} from '../src/core/chart_manager.js'; import {PackageDownloader} from '../src/core/package_downloader.js'; import {KeyManager} from '../src/core/key_manager.js'; -import {Zippy} from '../src/core/zippy.js'; import {HEDERA_PLATFORM_VERSION} from '../version.js'; -import {IntervalLeaseRenewalService} from '../src/core/lease/interval_lease_renewal.js'; import {Duration} from '../src/core/time/duration.js'; +import {container} from 'tsyringe-neo'; +import {resetTestContainer} from './test_container.js'; -export const testLogger = logging.NewLogger('debug', true); export const TEST_CLUSTER = 'solo-e2e'; export const HEDERA_PLATFORM_VERSION_TAG = HEDERA_PLATFORM_VERSION; export const BASE_TEST_DIR = path.join('test', 'data', 'tmp'); +export let testLogger = container.resolve(SoloLogger); + export function getTestCacheDir(testName?: string) { const d = testName ? path.join(BASE_TEST_DIR, testName) : BASE_TEST_DIR; @@ -137,24 +138,24 @@ export function bootstrapTestVariables( ): BootstrapResponse { const namespace: string = argv[flags.namespace.name] || 'bootstrap-ns'; const cacheDir: string = argv[flags.cacheDir.name] || getTestCacheDir(testName); - const configManager = new ConfigManager(testLogger); + resetTestContainer(cacheDir); + const configManager = container.resolve(ConfigManager); configManager.update(argv); - const downloader = new PackageDownloader(testLogger); - const zippy = new Zippy(testLogger); - const helmDepManager = new HelmDependencyManager(downloader, zippy, testLogger); - const depManagerMap = new Map().set(constants.HELM, helmDepManager); - const depManager = new DependencyManager(testLogger, depManagerMap); - const keyManager = new KeyManager(testLogger); - const helm = new Helm(testLogger); - const chartManager = new ChartManager(helm, testLogger); - const k8 = k8Arg || new K8(configManager, testLogger); - const accountManager = new AccountManager(testLogger, k8); - const platformInstaller = new PlatformInstaller(testLogger, k8, configManager); - const profileManager = new ProfileManager(testLogger, configManager); - const leaseManager = new LeaseManager(k8, configManager, testLogger, new IntervalLeaseRenewalService()); - const certificateManager = new CertificateManager(k8, testLogger, configManager); - const localConfig = new LocalConfig(path.join(BASE_TEST_DIR, 'local-config.yaml'), testLogger, configManager); - const remoteConfigManager = new RemoteConfigManager(k8, testLogger, localConfig, configManager); + + const downloader = container.resolve(PackageDownloader); + const depManager = container.resolve(DependencyManager); + const helm = container.resolve(Helm); + const chartManager = container.resolve(ChartManager); + const keyManager = container.resolve(KeyManager); + const k8 = k8Arg || container.resolve(K8); + const accountManager = container.resolve(AccountManager); + const platformInstaller = container.resolve(PlatformInstaller); + const profileManager = container.resolve(ProfileManager); + const leaseManager = container.resolve(LeaseManager); + const certificateManager = container.resolve(CertificateManager); + const localConfig = container.resolve(LocalConfig); + const remoteConfigManager = container.resolve(RemoteConfigManager); + testLogger = container.resolve(SoloLogger); const opts: TestOpts = { logger: testLogger, @@ -434,7 +435,7 @@ async function addKeyHashToMap( export function getK8Instance(configManager: ConfigManager) { try { - return new K8(configManager, testLogger); + return container.resolve(K8); // TODO: return a mock without running the init within constructor after we convert to Mocha, Jest ESModule mocks are broke. } catch (e) { if (!(e instanceof SoloError)) { @@ -448,7 +449,7 @@ export function getK8Instance(configManager: ConfigManager) { // Create cluster execSync(`kind create cluster --name "${process.env.SOLO_CLUSTER_NAME}"`, {stdio: 'inherit'}); - return new K8(configManager, testLogger); + return container.resolve(K8); } } diff --git a/test/unit/commands/base.test.ts b/test/unit/commands/base.test.ts index 2df6be98e..c941eab5b 100644 --- a/test/unit/commands/base.test.ts +++ b/test/unit/commands/base.test.ts @@ -16,48 +16,46 @@ */ import {expect} from 'chai'; -import {HelmDependencyManager, DependencyManager} from '../../../src/core/dependency_managers/index.js'; +import {DependencyManager} from '../../../src/core/dependency_managers/index.js'; import {Helm} from '../../../src/core/helm.js'; import {ChartManager} from '../../../src/core/chart_manager.js'; import {ConfigManager} from '../../../src/core/config_manager.js'; -import {PackageDownloader} from '../../../src/core/package_downloader.js'; -import {Zippy} from '../../../src/core/zippy.js'; -import * as constants from '../../../src/core/constants.js'; import {LocalConfig} from '../../../src/core/config/local_config.js'; import {RemoteConfigManager} from '../../../src/core/config/remote/remote_config_manager.js'; import {K8} from '../../../src/core/k8.js'; -import * as logging from '../../../src/core/logging.js'; import {BaseCommand} from '../../../src/commands/base.js'; import {Flags as flags} from '../../../src/commands/flags.js'; import sinon from 'sinon'; -import path from 'path'; -import {BASE_TEST_DIR} from '../../test_util.js'; - -const testLogger = logging.NewLogger('debug', true); +import {container} from 'tsyringe-neo'; +import {SoloLogger} from '../../../src/core/logging.js'; +import {resetTestContainer} from '../../test_container.js'; describe('BaseCommand', () => { - const helm = new Helm(testLogger); - const chartManager = new ChartManager(helm, testLogger); - const configManager = new ConfigManager(testLogger); - - // prepare dependency manger registry - const downloader = new PackageDownloader(testLogger); - const zippy = new Zippy(testLogger); - const helmDepManager = new HelmDependencyManager(downloader, zippy, testLogger); - const depManagerMap = new Map().set(constants.HELM, helmDepManager); - const depManager = new DependencyManager(testLogger, depManagerMap); - const localConfig = new LocalConfig(path.join(BASE_TEST_DIR, 'local-config.yaml'), testLogger, configManager); - const remoteConfigManager = new RemoteConfigManager({} as any, testLogger, localConfig, configManager); - + let helm: Helm; + let chartManager: ChartManager; + let configManager: ConfigManager; + let depManager: DependencyManager; + let localConfig: LocalConfig; + let remoteConfigManager: RemoteConfigManager; let sandbox = sinon.createSandbox(); + let testLogger: SoloLogger; let baseCmd: BaseCommand; describe('runShell', () => { before(() => { + resetTestContainer(); + testLogger = container.resolve(SoloLogger); + helm = container.resolve(Helm); + chartManager = container.resolve(ChartManager); + configManager = container.resolve(ConfigManager); + depManager = container.resolve(DependencyManager); + localConfig = container.resolve(LocalConfig); + remoteConfigManager = container.resolve(RemoteConfigManager); + sandbox = sinon.createSandbox(); sandbox.stub(K8.prototype, 'init').callsFake(() => this); - const k8 = new K8(configManager, testLogger); + const k8 = container.resolve(K8); // @ts-ignore baseCmd = new BaseCommand({ diff --git a/test/unit/commands/cluster.test.ts b/test/unit/commands/cluster.test.ts index 5eb726bbc..6ff3afd2d 100644 --- a/test/unit/commands/cluster.test.ts +++ b/test/unit/commands/cluster.test.ts @@ -29,6 +29,9 @@ import {ChartManager} from '../../../src/core/chart_manager.js'; import {Helm} from '../../../src/core/helm.js'; import {ROOT_DIR} from '../../../src/core/constants.js'; import path from 'path'; +import {container} from 'tsyringe-neo'; +import {resetTestContainer} from '../../test_container.js'; +import * as test from 'node:test'; const getBaseCommandOpts = () => ({ logger: sinon.stub(), @@ -55,19 +58,24 @@ argv[flags.force.name] = true; argv[flags.clusterSetupNamespace.name] = constants.SOLO_SETUP_NAMESPACE; describe('ClusterCommand unit tests', () => { + before(() => { + resetTestContainer(); + }); + describe('Chart Install Function is called correctly', () => { let opts: any; beforeEach(() => { opts = getBaseCommandOpts(); - opts.logger = new SoloLogger(); - opts.helm = new Helm(opts.logger); + opts.logger = container.resolve(SoloLogger); + opts.helm = container.resolve(Helm); + opts.chartManager = container.resolve(ChartManager); opts.helm.dependency = sinon.stub(); - opts.chartManager = new ChartManager(opts.helm, opts.logger); + opts.chartManager.isChartInstalled = sinon.stub().returns(false); opts.chartManager.install = sinon.stub().returns(true); - opts.configManager = new ConfigManager(opts.logger); + opts.configManager = container.resolve(ConfigManager); opts.remoteConfigManager = sinon.stub(); }); diff --git a/test/unit/commands/context.test.ts b/test/unit/commands/context.test.ts index b8381c2eb..ceea764dc 100644 --- a/test/unit/commands/context.test.ts +++ b/test/unit/commands/context.test.ts @@ -100,7 +100,7 @@ describe('ContextCommandTasks unit tests', () => { chartManager: sandbox.createStubInstance(ChartManager), configManager, depManager: sandbox.createStubInstance(DependencyManager), - localConfig: new LocalConfig(filePath, loggerStub, configManager), + localConfig: new LocalConfig(filePath), downloader: sandbox.createStubInstance(PackageDownloader), keyManager: sandbox.createStubInstance(KeyManager), accountManager: sandbox.createStubInstance(AccountManager), @@ -156,7 +156,7 @@ describe('ContextCommandTasks unit tests', () => { }; const opts = getBaseCommandOpts(sandbox, remoteConfig, []); command = await runUpdateLocalConfigTask(opts); // @ts-ignore - localConfig = new LocalConfig(filePath, loggerStub, command.configManager); + localConfig = new LocalConfig(filePath); expect(localConfig.currentDeploymentName).to.equal('deployment'); expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2']); @@ -174,7 +174,7 @@ describe('ContextCommandTasks unit tests', () => { }; const opts = getBaseCommandOpts(sandbox, remoteConfig, [[flags.context, 'provided-context']]); command = await runUpdateLocalConfigTask(opts); // @ts-ignore - localConfig = new LocalConfig(filePath, loggerStub, command.configManager); + localConfig = new LocalConfig(filePath); expect(localConfig.currentDeploymentName).to.equal('deployment'); expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2']); @@ -196,7 +196,7 @@ describe('ContextCommandTasks unit tests', () => { [flags.context, 'provided-context-2,provided-context-3,provided-context-4'], ]); command = await runUpdateLocalConfigTask(opts); // @ts-ignore - localConfig = new LocalConfig(filePath, loggerStub, command.configManager); + localConfig = new LocalConfig(filePath); expect(localConfig.currentDeploymentName).to.equal('deployment'); expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'cluster-3', 'cluster-4']); @@ -217,7 +217,7 @@ describe('ContextCommandTasks unit tests', () => { }; const opts = getBaseCommandOpts(sandbox, remoteConfig, [[flags.quiet, true]]); command = await runUpdateLocalConfigTask(opts); // @ts-ignore - localConfig = new LocalConfig(filePath, loggerStub, command.configManager); + localConfig = new LocalConfig(filePath); expect(localConfig.currentDeploymentName).to.equal('deployment'); expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'cluster-3']); @@ -238,7 +238,7 @@ describe('ContextCommandTasks unit tests', () => { const opts = getBaseCommandOpts(sandbox, remoteConfig, []); command = await runUpdateLocalConfigTask(opts); // @ts-ignore - localConfig = new LocalConfig(filePath, loggerStub, command.configManager); + localConfig = new LocalConfig(filePath); expect(localConfig.currentDeploymentName).to.equal('deployment'); expect(localConfig.getCurrentDeployment().clusters).to.deep.equal(['cluster-2', 'new-cluster']); diff --git a/test/unit/commands/network.test.ts b/test/unit/commands/network.test.ts index 57a0988c7..cf11457cc 100644 --- a/test/unit/commands/network.test.ts +++ b/test/unit/commands/network.test.ts @@ -18,13 +18,7 @@ import sinon from 'sinon'; import {describe, it, beforeEach} from 'mocha'; import {expect} from 'chai'; -import { - bootstrapTestVariables, - getDefaultArgv, - HEDERA_PLATFORM_VERSION_TAG, - TEST_CLUSTER, - testLogger, -} from '../../test_util.js'; +import {getDefaultArgv, HEDERA_PLATFORM_VERSION_TAG, TEST_CLUSTER} from '../../test_util.js'; import {Flags as flags} from '../../../src/commands/flags.js'; import * as version from '../../../version.js'; import * as constants from '../../../src/core/constants.js'; @@ -34,23 +28,20 @@ import {Helm} from '../../../src/core/helm.js'; import path from 'path'; import {NetworkCommand} from '../../../src/commands/network.js'; import {LeaseManager} from '../../../src/core/lease/lease_manager.js'; -import {IntervalLeaseRenewalService} from '../../../src/core/lease/interval_lease_renewal.js'; import {RemoteConfigManager} from '../../../src/core/config/remote/remote_config_manager.js'; import {ProfileManager} from '../../../src/core/profile_manager.js'; import {KeyManager} from '../../../src/core/key_manager.js'; import {ROOT_DIR} from '../../../src/core/constants.js'; import {ListrLease} from '../../../src/core/lease/listr_lease.js'; import {GenesisNetworkDataConstructor} from '../../../src/core/genesis_network_models/genesis_network_data_constructor.js'; - -const getBaseCommandOpts = () => ({ - logger: sinon.stub(), - helm: sinon.stub(), - k8: sinon.stub(), - chartManager: sinon.stub(), - configManager: sinon.stub(), - depManager: sinon.stub(), - localConfig: sinon.stub(), -}); +import {container} from 'tsyringe-neo'; +import {SoloLogger} from '../../../src/core/logging.js'; +import {K8} from '../../../src/core/k8.js'; +import {PlatformInstaller} from '../../../src/core/platform_installer.js'; +import {CertificateManager} from '../../../src/core/certificate_manager.js'; +import {DependencyManager} from '../../../src/core/dependency_managers/index.js'; +import {LocalConfig} from '../../../src/core/config/local_config.js'; +import {resetTestContainer} from '../../test_container.js'; const testName = 'network-cmd-unit'; const namespace = testName; @@ -71,47 +62,61 @@ describe('NetworkCommand unit tests', () => { describe('Chart Install Function is called correctly', () => { let opts: any; - const bootstrapResp = bootstrapTestVariables(testName, argv); - beforeEach(() => { - opts = getBaseCommandOpts(); - opts.logger = testLogger; - opts.helm = new Helm(opts.logger); - opts.helm.dependency = sinon.stub(); + resetTestContainer(); + opts = {}; - opts.configManager = new ConfigManager(testLogger); + opts.logger = container.resolve(SoloLogger); + + opts.configManager = container.resolve(ConfigManager); opts.configManager.update(argv); - opts.k8 = sinon.stub(); + + opts.k8 = sinon.stub() as unknown as K8; opts.k8.hasNamespace = sinon.stub().returns(true); opts.k8.getNamespacedConfigMap = sinon.stub().returns(null); opts.k8.waitForPodReady = sinon.stub(); opts.k8.waitForPods = sinon.stub(); opts.k8.readNamespacedLease = sinon.stub(); opts.k8.logger = opts.logger; + container.registerInstance(K8, opts.k8); + + opts.depManager = sinon.stub() as unknown as DependencyManager; + container.registerInstance(DependencyManager, opts.depManager); + + opts.localConfig = sinon.stub() as unknown as LocalConfig; + container.registerInstance(LocalConfig, opts.localConfig); + + opts.helm = container.resolve(Helm); + opts.helm.dependency = sinon.stub(); ListrLease.newAcquireLeaseTask = sinon.stub().returns({ run: sinon.stub().returns({}), }); - opts.keyManager = new KeyManager(testLogger); + opts.keyManager = container.resolve(KeyManager); opts.keyManager.copyGossipKeysToStaging = sinon.stub(); opts.keyManager.copyNodeKeysToStaging = sinon.stub(); + opts.platformInstaller = sinon.stub(); opts.platformInstaller.copyNodeKeys = sinon.stub(); + container.registerInstance(PlatformInstaller, opts.platformInstaller); - opts.profileManager = new ProfileManager(testLogger, opts.configManager); + opts.profileManager = container.resolve(ProfileManager); opts.profileManager.prepareValuesForSoloChart = sinon.stub(); + opts.certificateManager = sinon.stub(); + container.registerInstance(CertificateManager, opts.certificateManager); - opts.chartManager = new ChartManager(opts.helm, opts.logger); + opts.chartManager = container.resolve(ChartManager); opts.chartManager.isChartInstalled = sinon.stub().returns(true); opts.chartManager.isChartInstalled.onSecondCall().returns(false); - opts.chartManager.install = sinon.stub().returns(true); - opts.remoteConfigManager = new RemoteConfigManager(opts.k8, opts.logger, opts.localConfig, opts.configManager); + opts.chartManager.uninstall = sinon.stub().returns(true); + + opts.remoteConfigManager = container.resolve(RemoteConfigManager); + opts.remoteConfigManager.getConfigMap = sinon.stub().returns(null); - opts.configManager = new ConfigManager(opts.logger); - opts.leaseManager = new LeaseManager(opts.k8, opts.configManager, opts.logger, new IntervalLeaseRenewalService()); + opts.leaseManager = container.resolve(LeaseManager); opts.leaseManager.currentNamespace = sinon.stub().returns(testName); GenesisNetworkDataConstructor.initialize = sinon.stub().returns(null); diff --git a/test/unit/core/certificate_manager.test.ts b/test/unit/core/certificate_manager.test.ts index 60814eed4..6e7cb5e28 100644 --- a/test/unit/core/certificate_manager.test.ts +++ b/test/unit/core/certificate_manager.test.ts @@ -22,23 +22,23 @@ import {ConfigManager} from '../../../src/core/config_manager.js'; import {K8} from '../../../src/core/k8.js'; import {CertificateManager} from '../../../src/core/certificate_manager.js'; import {Flags as flags} from '../../../src/commands/flags.js'; -import {testLogger} from '../../test_util.js'; import {SoloError} from '../../../src/core/errors.js'; +import {container} from 'tsyringe-neo'; +import {resetTestContainer} from '../../test_container.js'; describe('Certificate Manager', () => { const argv = {}; // @ts-ignore const k8InitSpy = jest.spyOn(K8.prototype, 'init').mockImplementation(() => {}); const k8CreateSecret = jest.spyOn(K8.prototype, 'createSecret').mockResolvedValue(true); - let k8: K8; let certificateManager: CertificateManager; before(() => { + resetTestContainer(); argv[flags.namespace.name] = 'namespace'; - const configManager = new ConfigManager(testLogger); + const configManager = container.resolve(ConfigManager); configManager.update(argv); - k8 = new K8(configManager, testLogger); - certificateManager = new CertificateManager(k8, testLogger, configManager); + certificateManager = container.resolve(CertificateManager); }); after(() => { diff --git a/test/unit/core/config_manager.test.ts b/test/unit/core/config_manager.test.ts index 96aa9512f..0fc94b04d 100644 --- a/test/unit/core/config_manager.test.ts +++ b/test/unit/core/config_manager.test.ts @@ -19,12 +19,23 @@ import {describe, it} from 'mocha'; import {ConfigManager} from '../../../src/core/config_manager.js'; import {Flags as flags} from '../../../src/commands/flags.js'; +import {container} from 'tsyringe-neo'; +import {SoloLogger} from '../../../src/core/logging.js'; import {testLogger} from '../../test_util.js'; describe('ConfigManager', () => { describe('update values using argv', () => { + beforeEach(() => { + container.clearInstances(); + container.register('logLevel', {useValue: 'debug'}); + container.register('devMode', {useValue: true}); + container.register(SoloLogger, {useValue: new SoloLogger()}); + container.registerInstance(SoloLogger, testLogger); + container.register(ConfigManager, {useClass: ConfigManager}); + }); + it('should update string flag value', () => { - const cm = new ConfigManager(testLogger); + const cm = container.resolve(ConfigManager); const argv = {}; argv[flags.releaseTag.name] = 'v0.42.5'; @@ -40,7 +51,7 @@ describe('ConfigManager', () => { }); it('should update number flag value', () => { - const cm = new ConfigManager(testLogger); + const cm = container.resolve(ConfigManager); const argv = {}; argv[flags.replicaCount.name] = 1; @@ -56,7 +67,7 @@ describe('ConfigManager', () => { }); it('should update boolean flag value', () => { - const cm = new ConfigManager(testLogger); + const cm = container.resolve(ConfigManager); // boolean values should work const argv = {}; @@ -87,7 +98,7 @@ describe('ConfigManager', () => { it('should take user input as the first preference', () => { // Given: config has value, argv has a different value // Expected: argv should retain the value - const cm = new ConfigManager(testLogger); + const cm = container.resolve(ConfigManager); cm.setFlag(flags.devMode, false); expect(cm.getFlag(flags.devMode)).not.to.be.ok; @@ -102,7 +113,7 @@ describe('ConfigManager', () => { it('should take default as the last preference', () => { // Given: neither config nor argv has the flag value set // Expected: argv should inherit the default flag value - const cm = new ConfigManager(testLogger); + const cm = container.resolve(ConfigManager); expect(cm.hasFlag(flags.devMode)).not.to.be.ok; // shouldn't have set const argv = {}; // devMode flag is not set in argv and cached config doesn't have it either diff --git a/test/unit/core/dependency_managers/dependency_manager.test.ts b/test/unit/core/dependency_managers/dependency_manager.test.ts index 5e4274f79..986441263 100644 --- a/test/unit/core/dependency_managers/dependency_manager.test.ts +++ b/test/unit/core/dependency_managers/dependency_manager.test.ts @@ -17,20 +17,17 @@ import {expect} from 'chai'; import {describe, it} from 'mocha'; -import {DependencyManager, HelmDependencyManager} from '../../../../src/core/dependency_managers/index.js'; -import {PackageDownloader} from '../../../../src/core/package_downloader.js'; -import {Zippy} from '../../../../src/core/zippy.js'; -import * as constants from '../../../../src/core/constants.js'; -import * as logging from '../../../../src/core/logging.js'; +import {DependencyManager} from '../../../../src/core/dependency_managers/index.js'; +import {container} from 'tsyringe-neo'; +import {resetTestContainer} from '../../../test_container.js'; -const testLogger = logging.NewLogger('debug', true); describe('DependencyManager', () => { - // prepare dependency manger registry - const downloader = new PackageDownloader(testLogger); - const zippy = new Zippy(testLogger); - const helmDepManager = new HelmDependencyManager(downloader, zippy, testLogger); - const depManagerMap: Map = new Map().set(constants.HELM, helmDepManager); - const depManager = new DependencyManager(testLogger, depManagerMap); + let depManager; + + before(() => { + resetTestContainer(); + depManager = container.resolve(DependencyManager); + }); describe('checkDependency', () => { it('should fail during invalid dependency check', async () => { diff --git a/test/unit/core/helm.test.ts b/test/unit/core/helm.test.ts index 2abfb2db1..4d29af6bc 100644 --- a/test/unit/core/helm.test.ts +++ b/test/unit/core/helm.test.ts @@ -20,19 +20,18 @@ import {afterEach, beforeEach, describe, it} from 'mocha'; import each from 'mocha-each'; import * as constants from '../../../src/core/constants.js'; -import * as logging from '../../../src/core/logging.js'; import {Helm} from '../../../src/core/helm.js'; import {Templates} from '../../../src/core/templates.js'; import {ShellRunner} from '../../../src/core/shell_runner.js'; describe('Helm platform specific tests', () => { each(['linux', 'windows', 'darwin']).describe('Helm on %s platform', osPlatform => { - const logger = logging.NewLogger('debug', true); - const helm = new Helm(logger, osPlatform); + let helm: Helm, shellStub: sinon.SinonStub<[cmd: string, verbose?: boolean], Promise>, helmPath: string; - let shellStub: sinon.SinonStub<[cmd: string, verbose?: boolean], Promise>; - - const helmPath = Templates.installationPath(constants.HELM, osPlatform); + before(() => { + helm = new Helm(osPlatform); + helmPath = Templates.installationPath(constants.HELM, osPlatform); + }); // Stub the ShellRunner.prototype.run method for all tests beforeEach(() => { diff --git a/test/unit/core/k8.test.ts b/test/unit/core/k8.test.ts index bef75fb7f..b9f74923d 100644 --- a/test/unit/core/k8.test.ts +++ b/test/unit/core/k8.test.ts @@ -17,13 +17,13 @@ import {expect} from 'chai'; import {describe, it, after, before} from 'mocha'; import jest from 'jest-mock'; - import * as constants from '../../../src/core/constants.js'; import {K8} from '../../../src/core/k8.js'; import {ConfigManager} from '../../../src/core/config_manager.js'; -import {testLogger} from '../../test_util.js'; import {Flags as flags} from '../../../src/commands/flags.js'; import {Duration} from '../../../src/core/time/duration.js'; +import {container} from 'tsyringe-neo'; +import {resetTestContainer} from '../../test_container.js'; function listNamespacedPodMockSetup(k8: K8, numOfFailures: number, result: any) { for (let i = 0; i < numOfFailures - 1; i++) { @@ -71,10 +71,11 @@ describe('K8 Unit Tests', function () { let k8: K8; before(() => { + resetTestContainer(); argv[flags.namespace.name] = 'namespace'; - const configManager = new ConfigManager(testLogger); + const configManager = container.resolve(ConfigManager); configManager.update(argv); - k8 = new K8(configManager, testLogger); + k8 = container.resolve(K8); k8.kubeClient = { // @ts-ignore listNamespacedPod: jest.fn(), diff --git a/test/unit/core/key_manager.test.ts b/test/unit/core/key_manager.test.ts index 9662f43f3..f15c72f67 100644 --- a/test/unit/core/key_manager.test.ts +++ b/test/unit/core/key_manager.test.ts @@ -22,13 +22,12 @@ import os from 'os'; import path from 'path'; import {KeyManager} from '../../../src/core/key_manager.js'; import * as constants from '../../../src/core/constants.js'; -import * as logging from '../../../src/core/logging.js'; import type {NodeAlias} from '../../../src/types/aliases.js'; import {Duration} from '../../../src/core/time/duration.js'; +import {container} from 'tsyringe-neo'; describe('KeyManager', () => { - const logger = logging.NewLogger('debug', true); - const keyManager = new KeyManager(logger); + const keyManager = container.resolve(KeyManager); it('should generate signing key', async () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'keys-')); diff --git a/test/unit/core/local_config.test.ts b/test/unit/core/local_config.test.ts index 4dc78a699..99de98df3 100644 --- a/test/unit/core/local_config.test.ts +++ b/test/unit/core/local_config.test.ts @@ -33,7 +33,7 @@ describe('LocalConfig', () => { const expectFailedValidation = expectedMessage => { try { - new LocalConfig(filePath, testLogger, configManager); + new LocalConfig(filePath); expect.fail('Expected an error to be thrown'); } catch (error) { expect(error).to.be.instanceOf(SoloError); @@ -43,7 +43,7 @@ describe('LocalConfig', () => { beforeEach(async () => { await fs.promises.writeFile(filePath, stringify(config)); - localConfig = new LocalConfig(filePath, testLogger, configManager); + localConfig = new LocalConfig(filePath); }); afterEach(async () => { @@ -65,7 +65,7 @@ describe('LocalConfig', () => { await localConfig.write(); // reinitialize with updated config file - const newConfig = new LocalConfig(filePath, testLogger, configManager); + const newConfig = new LocalConfig(filePath); expect(newConfig.userEmailAddress).to.eq(newEmailAddress); }); @@ -92,7 +92,7 @@ describe('LocalConfig', () => { expect(localConfig.deployments).to.deep.eq(newDeployments); await localConfig.write(); - const newConfig = new LocalConfig(filePath, testLogger, configManager); + const newConfig = new LocalConfig(filePath); expect(newConfig.deployments).to.deep.eq(newDeployments); }); @@ -160,7 +160,7 @@ describe('LocalConfig', () => { expect(localConfig.currentDeploymentName).to.eq(newCurrentDeployment); await localConfig.write(); - const newConfig = new LocalConfig(filePath, testLogger, configManager); + const newConfig = new LocalConfig(filePath); expect(newConfig.currentDeploymentName).to.eq(newCurrentDeployment); }); @@ -184,7 +184,7 @@ describe('LocalConfig', () => { it('should throw an error if file path is not set', async () => { try { - new LocalConfig('', testLogger, configManager); + new LocalConfig(''); expect.fail('Expected an error to be thrown'); } catch (error) { expect(error).to.be.instanceOf(MissingArgumentError); diff --git a/test/unit/core/platform_installer.test.ts b/test/unit/core/platform_installer.test.ts index 0506ea20b..06d15f55c 100644 --- a/test/unit/core/platform_installer.test.ts +++ b/test/unit/core/platform_installer.test.ts @@ -21,20 +21,17 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import * as constants from '../../../src/core/constants.js'; -import {ConfigManager} from '../../../src/core/config_manager.js'; import {PlatformInstaller} from '../../../src/core/platform_installer.js'; -import * as logging from '../../../src/core/logging.js'; import {IllegalArgumentError, MissingArgumentError} from '../../../src/core/errors.js'; -import {getK8Instance} from '../../test_util.js'; import type {PodName} from '../../../src/types/aliases.js'; +import {container} from 'tsyringe-neo'; describe('PackageInstaller', () => { - const testLogger = logging.NewLogger('debug', true); - const configManager = new ConfigManager(testLogger); + let installer: PlatformInstaller; - const k8 = getK8Instance(configManager); - - const installer = new PlatformInstaller(testLogger, k8, configManager); + before(() => { + installer = container.resolve(PlatformInstaller); + }); describe('validatePlatformReleaseDir', () => { it('should fail for missing path', () => { @@ -64,7 +61,7 @@ describe('PackageInstaller', () => { fs.mkdirSync(`${tmpDir}/${constants.HEDERA_DATA_APPS_DIR}`, {recursive: true}); fs.mkdirSync(`${tmpDir}/${constants.HEDERA_DATA_LIB_DIR}`, {recursive: true}); fs.writeFileSync(`${tmpDir}/${constants.HEDERA_DATA_LIB_DIR}/test.jar`, ''); - // @ts-ignore + // @ts-expect-error - TS2554: Expected 1 arguments, but got 0 expect(() => installer.validatePlatformReleaseDir()).to.throw(MissingArgumentError); fs.rmSync(tmpDir, {recursive: true}); }); @@ -74,7 +71,7 @@ describe('PackageInstaller', () => { fs.mkdirSync(`${tmpDir}/${constants.HEDERA_DATA_APPS_DIR}`, {recursive: true}); fs.writeFileSync(`${tmpDir}/${constants.HEDERA_DATA_APPS_DIR}/app.jar`, ''); fs.mkdirSync(`${tmpDir}/${constants.HEDERA_DATA_LIB_DIR}`, {recursive: true}); - // @ts-ignore + // @ts-expect-error - TS2554: Expected 1 arguments, but got 0 expect(() => installer.validatePlatformReleaseDir()).to.throw(MissingArgumentError); fs.rmSync(tmpDir, {recursive: true}); }); @@ -85,7 +82,7 @@ describe('PackageInstaller', () => { fs.writeFileSync(`${tmpDir}/${constants.HEDERA_DATA_APPS_DIR}/app.jar`, ''); fs.mkdirSync(`${tmpDir}/${constants.HEDERA_DATA_LIB_DIR}`, {recursive: true}); fs.writeFileSync(`${tmpDir}/${constants.HEDERA_DATA_LIB_DIR}/lib-1.jar`, ''); - // @ts-ignore + // @ts-expect-error - TS2554: Expected 1 arguments, but got 0 expect(() => installer.validatePlatformReleaseDir()).to.throw(MissingArgumentError); fs.rmSync(tmpDir, {recursive: true}); }); @@ -102,12 +99,12 @@ describe('PackageInstaller', () => { describe('copyGossipKeys', () => { it('should fail for missing podName', async () => { - // @ts-ignore + // @ts-expect-error - TS2554: Expected 3 arguments, but got 2 await expect(installer.copyGossipKeys('', os.tmpdir())).to.be.rejectedWith(MissingArgumentError); }); it('should fail for missing stagingDir path', async () => { - // @ts-ignore + // @ts-expect-error - TS2554: Expected 3 arguments, but got 2 await expect(installer.copyGossipKeys('node1', '')).to.be.rejectedWith(MissingArgumentError); }); }); diff --git a/test/unit/core/profile_manager.test.ts b/test/unit/core/profile_manager.test.ts index a4b01a417..180b6ea0a 100644 --- a/test/unit/core/profile_manager.test.ts +++ b/test/unit/core/profile_manager.test.ts @@ -24,25 +24,33 @@ import {Flags as flags} from '../../../src/commands/flags.js'; import * as constants from '../../../src/core/constants.js'; import {ConfigManager} from '../../../src/core/config_manager.js'; import {ProfileManager} from '../../../src/core/profile_manager.js'; -import {getTestCacheDir, getTmpDir, testLogger} from '../../test_util.js'; +import {getTestCacheDir, getTmpDir} from '../../test_util.js'; import * as version from '../../../version.js'; import type {NodeAlias} from '../../../src/types/aliases.js'; - -const tmpDir = getTmpDir(); -const configManager = new ConfigManager(testLogger); -const profileManager = new ProfileManager(testLogger, configManager, tmpDir); -configManager.setFlag(flags.nodeAliasesUnparsed, 'node1,node2,node4'); -const testProfileFile = path.join('test', 'data', 'test-profiles.yaml'); -configManager.setFlag(flags.cacheDir, getTestCacheDir('ProfileManager')); -configManager.setFlag(flags.releaseTag, version.HEDERA_PLATFORM_VERSION); -const cacheDir = configManager.getFlag(flags.cacheDir) as string; -configManager.setFlag(flags.apiPermissionProperties, path.join(cacheDir, 'templates', 'api-permission.properties')); -configManager.setFlag(flags.applicationProperties, path.join(cacheDir, 'templates', 'application.properties')); -configManager.setFlag(flags.bootstrapProperties, path.join(cacheDir, 'templates', 'bootstrap.properties')); -configManager.setFlag(flags.log4j2Xml, path.join(cacheDir, 'templates', 'log4j2.xml')); -configManager.setFlag(flags.settingTxt, path.join(cacheDir, 'templates', 'settings.txt')); +import {container} from 'tsyringe-neo'; +import {resetTestContainer} from '../../test_container.js'; describe('ProfileManager', () => { + let tmpDir: string, configManager: ConfigManager, profileManager: ProfileManager, cacheDir: string; + + const testProfileFile = path.join('test', 'data', 'test-profiles.yaml'); + + before(() => { + resetTestContainer(); + tmpDir = getTmpDir(); + configManager = container.resolve(ConfigManager); + profileManager = new ProfileManager(undefined, undefined, tmpDir); + configManager.setFlag(flags.nodeAliasesUnparsed, 'node1,node2,node4'); + configManager.setFlag(flags.cacheDir, getTestCacheDir('ProfileManager')); + configManager.setFlag(flags.releaseTag, version.HEDERA_PLATFORM_VERSION); + cacheDir = configManager.getFlag(flags.cacheDir) as string; + configManager.setFlag(flags.apiPermissionProperties, path.join(cacheDir, 'templates', 'api-permission.properties')); + configManager.setFlag(flags.applicationProperties, path.join(cacheDir, 'templates', 'application.properties')); + configManager.setFlag(flags.bootstrapProperties, path.join(cacheDir, 'templates', 'bootstrap.properties')); + configManager.setFlag(flags.log4j2Xml, path.join(cacheDir, 'templates', 'log4j2.xml')); + configManager.setFlag(flags.settingTxt, path.join(cacheDir, 'templates', 'settings.txt')); + }); + after(() => { fs.rmSync(tmpDir, {recursive: true}); }); diff --git a/test/unit/core/shell_runner.test.ts b/test/unit/core/shell_runner.test.ts index 886fef943..98cc4a784 100644 --- a/test/unit/core/shell_runner.test.ts +++ b/test/unit/core/shell_runner.test.ts @@ -21,21 +21,16 @@ import sinon from 'sinon'; import {expect} from 'chai'; import {describe, it, beforeEach, afterEach} from 'mocha'; import {ShellRunner} from '../../../src/core/shell_runner.js'; -import {NewLogger, SoloLogger} from '../../../src/core/logging.js'; +import {SoloLogger} from '../../../src/core/logging.js'; import {ChildProcess} from 'child_process'; import {Readable} from 'stream'; import {Duration} from '../../../src/core/time/duration.js'; describe('ShellRunner', () => { - let logger: SoloLogger, - shellRunner: ShellRunner, - loggerStub: SinonStub, - childProcessSpy: SinonSpy, - readableSpy: SinonSpy; + let shellRunner: ShellRunner, loggerStub: SinonStub, childProcessSpy: SinonSpy, readableSpy: SinonSpy; beforeEach(() => { - logger = NewLogger('debug'); - shellRunner = new ShellRunner(logger); + shellRunner = new ShellRunner(); // Spy on methods loggerStub = sinon.stub(SoloLogger.prototype, 'debug');