From a920f2c057a53e38e6627faf273b2a67d80e749e Mon Sep 17 00:00:00 2001
From: Jari Kolehmainen
Date: Tue, 21 Mar 2023 17:07:27 +0200
Subject: [PATCH 1/9] Electron 22.3.3 (#7389)
* electron 22.3.3.
Signed-off-by: Jari Kolehmainen
* fix typo
Signed-off-by: Jari Kolehmainen
* fix crash on quit
Signed-off-by: Jari Kolehmainen
* fix sessionData app path
Signed-off-by: Jari Kolehmainen
* Fix errors after merging new feature
Signed-off-by: Sebastian Malton
---------
Signed-off-by: Jari Kolehmainen
Signed-off-by: Sebastian Malton
Co-authored-by: Sebastian Malton
---
package-lock.json | 368 ++++--------------
packages/core/package.json | 2 +-
.../src/common/app-paths/app-path-names.ts | 2 +-
.../src/common/app-paths/app-paths.test.ts | 6 +-
.../initialize-sentry-reporting.injectable.ts | 2 +-
.../app-paths/setup-app-paths.injectable.ts | 1 +
.../resolve-system-proxy-window.injectable.ts | 6 +-
packages/open-lens/package.json | 4 +-
.../application/electron-main/package.json | 2 +-
.../messaging/electron/main/package.json | 2 +-
.../messaging/electron/renderer/package.json | 2 +-
11 files changed, 89 insertions(+), 308 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index fc13b55540..d00efc567c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2099,92 +2099,25 @@
}
},
"node_modules/@electron/get": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz",
- "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz",
+ "integrity": "sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g==",
"dependencies": {
"debug": "^4.1.1",
"env-paths": "^2.2.0",
"fs-extra": "^8.1.0",
- "got": "^9.6.0",
+ "got": "^11.8.5",
"progress": "^2.0.3",
"semver": "^6.2.0",
"sumchecker": "^3.0.1"
},
"engines": {
- "node": ">=8.6"
+ "node": ">=12"
},
"optionalDependencies": {
- "global-agent": "^3.0.0",
- "global-tunnel-ng": "^2.7.1"
+ "global-agent": "^3.0.0"
}
},
- "node_modules/@electron/get/node_modules/@sindresorhus/is": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
- "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@electron/get/node_modules/@szmarczak/http-timer": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
- "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
- "dependencies": {
- "defer-to-connect": "^1.0.1"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@electron/get/node_modules/cacheable-request": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
- "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
- "dependencies": {
- "clone-response": "^1.0.2",
- "get-stream": "^5.1.0",
- "http-cache-semantics": "^4.0.0",
- "keyv": "^3.0.0",
- "lowercase-keys": "^2.0.0",
- "normalize-url": "^4.1.0",
- "responselike": "^1.0.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@electron/get/node_modules/cacheable-request/node_modules/get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@electron/get/node_modules/decompress-response": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
- "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==",
- "dependencies": {
- "mimic-response": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@electron/get/node_modules/defer-to-connect": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
- "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
- },
"node_modules/@electron/get/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@@ -2198,51 +2131,6 @@
"node": ">=6 <7 || >=8"
}
},
- "node_modules/@electron/get/node_modules/get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@electron/get/node_modules/got": {
- "version": "9.6.0",
- "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
- "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
- "dependencies": {
- "@sindresorhus/is": "^0.14.0",
- "@szmarczak/http-timer": "^1.1.2",
- "cacheable-request": "^6.0.0",
- "decompress-response": "^3.3.0",
- "duplexer3": "^0.1.4",
- "get-stream": "^4.1.0",
- "lowercase-keys": "^1.0.1",
- "mimic-response": "^1.0.1",
- "p-cancelable": "^1.0.0",
- "to-readable-stream": "^1.0.0",
- "url-parse-lax": "^3.0.0"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/@electron/get/node_modules/got/node_modules/lowercase-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
- "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/@electron/get/node_modules/json-buffer": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
- "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ=="
- },
"node_modules/@electron/get/node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
@@ -2251,55 +2139,6 @@
"graceful-fs": "^4.1.6"
}
},
- "node_modules/@electron/get/node_modules/keyv": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
- "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
- "dependencies": {
- "json-buffer": "3.0.0"
- }
- },
- "node_modules/@electron/get/node_modules/normalize-url": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
- "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@electron/get/node_modules/p-cancelable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
- "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@electron/get/node_modules/pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "node_modules/@electron/get/node_modules/responselike": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
- "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==",
- "dependencies": {
- "lowercase-keys": "^1.0.0"
- }
- },
- "node_modules/@electron/get/node_modules/responselike/node_modules/lowercase-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
- "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/@electron/get/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -7977,6 +7816,16 @@
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
"integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA=="
},
+ "node_modules/@types/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz",
@@ -11054,6 +10903,7 @@
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
"engines": [
"node >= 0.8"
],
@@ -11067,12 +10917,14 @@
"node_modules/concat-stream/node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
},
"node_modules/concat-stream/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@@ -11087,6 +10939,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
"dependencies": {
"safe-buffer": "~5.1.0"
}
@@ -11178,7 +11031,7 @@
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
"integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"ini": "^1.3.4",
"proto-list": "~1.2.1"
@@ -12657,11 +12510,6 @@
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
"dev": true
},
- "node_modules/duplexer3": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
- "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA=="
- },
"node_modules/duplexify": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
@@ -12736,20 +12584,20 @@
}
},
"node_modules/electron": {
- "version": "19.1.9",
- "resolved": "https://registry.npmjs.org/electron/-/electron-19.1.9.tgz",
- "integrity": "sha512-XT5LkTzIHB+ZtD3dTmNnKjVBWrDWReCKt9G1uAFLz6uJMEVcIUiYO+fph5pLXETiBw/QZBx8egduMEfIccLx+g==",
+ "version": "22.3.3",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.3.tgz",
+ "integrity": "sha512-+ZJDVfyhw7J2A46/kGKscktIhzOisTeJKrUBJLXa7PTB+U+cwyoxCBIaIOnDsdicBCX4nAc1mo6YMQjQQdAmgw==",
"hasInstallScript": true,
"dependencies": {
- "@electron/get": "^1.14.1",
+ "@electron/get": "^2.0.0",
"@types/node": "^16.11.26",
- "extract-zip": "^1.0.3"
+ "extract-zip": "^2.0.1"
},
"bin": {
"electron": "cli.js"
},
"engines": {
- "node": ">= 8.6"
+ "node": ">= 12.20.55"
}
},
"node_modules/electron-builder": {
@@ -13054,7 +12902,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
- "devOptional": true,
+ "dev": true,
"engines": {
"node": ">= 0.8"
}
@@ -15013,42 +14861,46 @@
}
},
"node_modules/extract-zip": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
- "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"dependencies": {
- "concat-stream": "^1.6.2",
- "debug": "^2.6.9",
- "mkdirp": "^0.5.4",
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"bin": {
"extract-zip": "cli.js"
- }
- },
- "node_modules/extract-zip/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/extract-zip/node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "dependencies": {
- "minimist": "^1.2.6"
},
- "bin": {
- "mkdirp": "bin/cmd.js"
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
}
},
- "node_modules/extract-zip/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "node_modules/extract-zip/node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/extract-zip/node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
},
"node_modules/extsprintf": {
"version": "1.3.0",
@@ -16106,22 +15958,6 @@
"node": ">=10.0"
}
},
- "node_modules/global-tunnel-ng": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz",
- "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==",
- "optional": true,
- "peer": true,
- "dependencies": {
- "encodeurl": "^1.0.2",
- "lodash": "^4.17.10",
- "npm-conf": "^1.1.3",
- "tunnel": "^0.0.6"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -24084,30 +23920,6 @@
"npm-normalize-package-bin": "^1.0.1"
}
},
- "node_modules/npm-conf": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz",
- "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==",
- "optional": true,
- "peer": true,
- "dependencies": {
- "config-chain": "^1.1.11",
- "pify": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/npm-conf/node_modules/pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/npm-install-checks": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz",
@@ -28525,14 +28337,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/prepend-http": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
- "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/prettier": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
@@ -28727,7 +28531,7 @@
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
"integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
- "devOptional": true
+ "dev": true
},
"node_modules/protocols": {
"version": "2.0.1",
@@ -30131,9 +29935,9 @@
}
},
"node_modules/rimraf/node_modules/glob": {
- "version": "9.2.1",
- "resolved": "https://registry.npmjs.org/glob/-/glob-9.2.1.tgz",
- "integrity": "sha512-Pxxgq3W0HyA3XUvSXcFhRSs+43Jsx0ddxcFrbjxNGkL2Ak5BAUBxLqI5G6ADDeCHLfzzXFhe0b1yYcctGmytMA==",
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.0.tgz",
+ "integrity": "sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@@ -32431,14 +32235,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/to-readable-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
- "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -32751,16 +32547,6 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
- "node_modules/tunnel": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
- "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
- }
- },
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -32846,7 +32632,8 @@
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+ "dev": true
},
"node_modules/typedoc": {
"version": "0.23.25",
@@ -33170,17 +32957,6 @@
"requires-port": "^1.0.0"
}
},
- "node_modules/url-parse-lax": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
- "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==",
- "dependencies": {
- "prepend-http": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/use-isomorphic-layout-effect": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
@@ -34511,7 +34287,7 @@
"css-loader": "^6.7.3",
"deepdash": "^5.3.9",
"dompurify": "^2.4.4",
- "electron": "^19.1.9",
+ "electron": "^22.3.3",
"electron-builder": "^23.6.0",
"esbuild": "^0.17.8",
"esbuild-loader": "^2.21.0",
@@ -36698,7 +36474,7 @@
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.2",
- "electron": "^19.1.9",
+ "electron": "^22.3.3",
"electron-builder": "^23.6.0",
"electron-notarize": "^0.3.0",
"esbuild-loader": "^2.20.0",
@@ -37192,7 +36968,7 @@
"@k8slens/feature-core": "^6.5.0-alpha.0",
"@ogre-tools/injectable": "^15.1.2",
"@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2",
- "electron": "^19.1.9"
+ "electron": "^22.3.3"
}
},
"packages/technical-features/application/legacy-extensions": {
@@ -37269,7 +37045,7 @@
"@k8slens/messaging": "^1.0.0-alpha.1",
"@ogre-tools/injectable": "^15.1.2",
"@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2",
- "electron": "^19.1.8",
+ "electron": "^22.3.3",
"lodash": "^4.17.21"
}
},
@@ -37287,7 +37063,7 @@
"@k8slens/startable-stoppable": "^1.0.0-alpha.1",
"@ogre-tools/injectable": "^15.1.2",
"@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2",
- "electron": "^19.1.8",
+ "electron": "^22.3.3",
"lodash": "^4.17.21"
}
},
diff --git a/packages/core/package.json b/packages/core/package.json
index 15c79d774d..42c39d6181 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -257,7 +257,7 @@
"css-loader": "^6.7.3",
"deepdash": "^5.3.9",
"dompurify": "^2.4.4",
- "electron": "^19.1.9",
+ "electron": "^22.3.3",
"electron-builder": "^23.6.0",
"esbuild": "^0.17.8",
"esbuild-loader": "^2.21.0",
diff --git a/packages/core/src/common/app-paths/app-path-names.ts b/packages/core/src/common/app-paths/app-path-names.ts
index 8e3d2c440e..b7b829a5e3 100644
--- a/packages/core/src/common/app-paths/app-path-names.ts
+++ b/packages/core/src/common/app-paths/app-path-names.ts
@@ -11,7 +11,7 @@ export const pathNames: PathName[] = [
"home",
"appData",
"userData",
- "cache",
+ "sessionData",
"temp",
"exe",
"module",
diff --git a/packages/core/src/common/app-paths/app-paths.test.ts b/packages/core/src/common/app-paths/app-paths.test.ts
index f847346dad..851ece4390 100644
--- a/packages/core/src/common/app-paths/app-paths.test.ts
+++ b/packages/core/src/common/app-paths/app-paths.test.ts
@@ -21,7 +21,6 @@ describe("app-paths", () => {
const defaultAppPathsStub: AppPaths = {
currentApp: "/some-current-app",
appData: "/some-app-data",
- cache: "/some-cache",
crashDumps: "/some-crash-dumps",
desktop: "/some-desktop",
documents: "/some-documents",
@@ -36,6 +35,7 @@ describe("app-paths", () => {
temp: "/some-temp",
videos: "/some-videos",
userData: "/some-irrelevant-user-data",
+ sessionData: "/some-irrelevant-user-data", // By default this points to userData
};
builder.beforeApplicationStart(({ mainDi }) => {
@@ -73,7 +73,6 @@ describe("app-paths", () => {
expect(actual).toEqual({
currentApp: "/some-current-app",
appData: "/some-app-data",
- cache: "/some-cache",
crashDumps: "/some-crash-dumps",
desktop: "/some-desktop",
documents: "/some-documents",
@@ -88,6 +87,7 @@ describe("app-paths", () => {
temp: "/some-temp",
videos: "/some-videos",
userData: "/some-app-data/some-product-name",
+ sessionData: "/some-app-data/some-product-name",
});
});
@@ -97,7 +97,6 @@ describe("app-paths", () => {
expect(actual).toEqual({
currentApp: "/some-current-app",
appData: "/some-app-data",
- cache: "/some-cache",
crashDumps: "/some-crash-dumps",
desktop: "/some-desktop",
documents: "/some-documents",
@@ -112,6 +111,7 @@ describe("app-paths", () => {
temp: "/some-temp",
videos: "/some-videos",
userData: "/some-app-data/some-product-name",
+ sessionData: "/some-app-data/some-product-name",
});
});
});
diff --git a/packages/core/src/common/error-reporting/initialize-sentry-reporting.injectable.ts b/packages/core/src/common/error-reporting/initialize-sentry-reporting.injectable.ts
index 778f959739..677c18a586 100644
--- a/packages/core/src/common/error-reporting/initialize-sentry-reporting.injectable.ts
+++ b/packages/core/src/common/error-reporting/initialize-sentry-reporting.injectable.ts
@@ -13,7 +13,7 @@ import userStoreInjectable from "../user-store/user-store.injectable";
export type InitializeSentryReportingWith = (initSentry: (opts: BrowserOptions | ElectronMainOptions) => void) => void;
-const mapProcessName = (type: "browser" | "renderer" | "worker") => type === "browser" ? "main" : type;
+const mapProcessName = (type: "browser" | "renderer" | "worker" | "utility") => type === "browser" ? "main" : type;
const initializeSentryReportingWithInjectable = getInjectable({
id: "initialize-sentry-reporting-with",
diff --git a/packages/core/src/main/app-paths/setup-app-paths.injectable.ts b/packages/core/src/main/app-paths/setup-app-paths.injectable.ts
index 34178547c3..255beffae0 100644
--- a/packages/core/src/main/app-paths/setup-app-paths.injectable.ts
+++ b/packages/core/src/main/app-paths/setup-app-paths.injectable.ts
@@ -34,6 +34,7 @@ const setupAppPathsInjectable = getInjectable({
const appDataPath = getElectronAppPath("appData");
setElectronAppPath("userData", joinPaths(appDataPath, appName));
+ setElectronAppPath("sessionData", getElectronAppPath("userData"));
const appPaths = pipeline(
pathNames,
diff --git a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.injectable.ts b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.injectable.ts
index 88e4319fa0..baa0da6c39 100644
--- a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.injectable.ts
+++ b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.injectable.ts
@@ -8,7 +8,11 @@ import { BrowserWindow } from "electron";
const resolveSystemProxyWindowInjectable = getInjectable({
id: "resolve-system-proxy-window",
instantiate: () => {
- return new BrowserWindow({ show: false, paintWhenInitiallyHidden: false });
+ const window = new BrowserWindow({ show: false });
+
+ window.hide();
+
+ return window;
},
causesSideEffects: true,
});
diff --git a/packages/open-lens/package.json b/packages/open-lens/package.json
index f59563de3f..577c1a2a6b 100644
--- a/packages/open-lens/package.json
+++ b/packages/open-lens/package.json
@@ -96,7 +96,7 @@
},
"build": {
"npmRebuild": false,
- "electronVersion": "19.1.9",
+ "electronVersion": "22.3.3",
"generateUpdatesFilesForAllChannels": true,
"files": [
"static/**/*",
@@ -251,7 +251,7 @@
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.2",
- "electron": "^19.1.9",
+ "electron": "^22.3.3",
"electron-builder": "^23.6.0",
"electron-notarize": "^0.3.0",
"esbuild-loader": "^2.20.0",
diff --git a/packages/technical-features/application/electron-main/package.json b/packages/technical-features/application/electron-main/package.json
index f32de6f301..96d649a362 100644
--- a/packages/technical-features/application/electron-main/package.json
+++ b/packages/technical-features/application/electron-main/package.json
@@ -35,7 +35,7 @@
"@k8slens/feature-core": "^6.5.0-alpha.0",
"@ogre-tools/injectable": "^15.1.2",
"@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2",
- "electron": "^19.1.9"
+ "electron": "^22.3.3"
},
"devDependencies": {
"@async-fn/jest": "^1.6.4",
diff --git a/packages/technical-features/messaging/electron/main/package.json b/packages/technical-features/messaging/electron/main/package.json
index 63d66ea6d0..ff1a76a7d3 100644
--- a/packages/technical-features/messaging/electron/main/package.json
+++ b/packages/technical-features/messaging/electron/main/package.json
@@ -38,7 +38,7 @@
"@k8slens/messaging": "^1.0.0-alpha.1",
"@ogre-tools/injectable": "^15.1.2",
"@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2",
- "electron": "^19.1.8",
+ "electron": "^22.3.3",
"lodash": "^4.17.21"
},
"devDependencies": {
diff --git a/packages/technical-features/messaging/electron/renderer/package.json b/packages/technical-features/messaging/electron/renderer/package.json
index dcb9a7a185..75fcb8e38f 100644
--- a/packages/technical-features/messaging/electron/renderer/package.json
+++ b/packages/technical-features/messaging/electron/renderer/package.json
@@ -39,7 +39,7 @@
"@k8slens/startable-stoppable": "^1.0.0-alpha.1",
"@ogre-tools/injectable": "^15.1.2",
"@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2",
- "electron": "^19.1.8",
+ "electron": "^22.3.3",
"lodash": "^4.17.21"
},
"devDependencies": {
From 517e2fe17d07a5e91109ea448db6defb3f65737c Mon Sep 17 00:00:00 2001
From: Sebastian Malton
Date: Tue, 21 Mar 2023 11:12:29 -0400
Subject: [PATCH 2/9] Fix type error in new @k8slens/messaging (#7392)
* Fix type error in new @k8slens/messaging
Signed-off-by: Sebastian Malton
* Better fix to conform to tests
Signed-off-by: Sebastian Malton
---------
Signed-off-by: Sebastian Malton
---
.../actual/message/message-channel-listener-injection-token.ts | 2 +-
.../enlist-message-channel-listener.injectable.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/message/message-channel-listener-injection-token.ts b/packages/technical-features/messaging/agnostic/src/features/actual/message/message-channel-listener-injection-token.ts
index 386c9f9e5e..0558bf6598 100644
--- a/packages/technical-features/messaging/agnostic/src/features/actual/message/message-channel-listener-injection-token.ts
+++ b/packages/technical-features/messaging/agnostic/src/features/actual/message/message-channel-listener-injection-token.ts
@@ -9,7 +9,7 @@ export interface MessageChannel {
export type ExtraData = { processId: number; frameId: number };
export type MessageChannelHandler = Channel extends MessageChannel
- ? (message: Message, data: ExtraData) => void
+ ? (message: Message, data?: ExtraData) => void
: never;
export interface MessageChannelListener {
diff --git a/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.injectable.ts b/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.injectable.ts
index 64e9b1f873..6948e51073 100644
--- a/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.injectable.ts
+++ b/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.injectable.ts
@@ -10,7 +10,7 @@ const enlistMessageChannelListenerInjectable = getInjectable({
const ipcRenderer = di.inject(ipcRendererInjectable);
return ({ channel, handler }) => {
- const nativeCallback = (_: IpcRendererEvent, message: unknown) => {
+ const nativeCallback = (event: IpcRendererEvent, message: unknown) => {
handler(message);
};
From 48db54ec9e42b712d97a67b8a8b85f8096e1422d Mon Sep 17 00:00:00 2001
From: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
Date: Tue, 21 Mar 2023 21:04:22 +0200
Subject: [PATCH 3/9] Renderer file logging through IPC (#7300)
* Renderer file logging through IPC
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Remove pagehide event listener as it may cause UI to freeze
Pagehide was needed in cluster frame to better handle main frame close/reload situation. But even empty pagehide listener in cluster frame seems to freeze the UI at least on some situations (multiple clusters open).
Beforeunload is not always executed in cluster frame when main frame is reloaded/closed, leaving log files open. To fix that, `stopIpcLoggingInjectable` is introduced to close all log files.
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Remove unnecessary formatting changes
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Lint fix
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Winston logger override
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Remove usage of doGeneralOverrides as it has been removed
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Update imports to match the new base
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Remove unnecessary id
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Review improvements
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Extract beforeunload listener to injectable
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
* Typo fix
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
---------
Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com>
---
packages/core/src/common/logger.injectable.ts | 11 +-
.../common/logger/ipc-file-logger-channel.ts | 24 +++
...n-logger.global-override-for-injectable.ts | 23 +++
.../src/common/winston-logger.injectable.ts | 18 ++
.../close-ipc-logging-listener.injectable.ts | 21 +++
...ransport.global-override-for-injectable.ts | 18 ++
.../create-ipc-file-transport.injectable.ts | 28 +++
.../main/logger/file-transport.injectable.ts | 6 +-
.../main/logger/ipc-file-logger.injectable.ts | 55 ++++++
.../src/main/logger/ipc-file-logger.test.ts | 160 ++++++++++++++++++
.../logger/ipc-logging-listener.injectable.ts | 39 +++++
.../main/logger/ipc-logging-listener.test.ts | 31 ++++
.../logger/stop-ipc-logging.injectable.ts | 27 +++
.../runnables/listen-unload.injectable.ts | 46 +++++
packages/core/src/renderer/bootstrap.tsx | 4 +-
.../init-cluster-frame/init-cluster-frame.ts | 95 +++++------
.../root-frame/init-root-frame.injectable.ts | 10 +-
.../logger/close-renderer-log-file-id.test.ts | 56 ++++++
.../close-renderer-log-file.injectable.ts | 28 +++
.../logger/ipc-transport.injectable.ts | 60 +++++++
.../src/renderer/logger/ipc-transport.test.ts | 48 ++++++
.../core/src/renderer/logger/ipc-transport.ts | 41 +++++
.../logger/renderer-log-file-id.injectable.ts | 29 ++++
.../logger/renderer-log-file-id.test.ts | 34 ++++
24 files changed, 836 insertions(+), 76 deletions(-)
create mode 100644 packages/core/src/common/logger/ipc-file-logger-channel.ts
create mode 100644 packages/core/src/common/winston-logger.global-override-for-injectable.ts
create mode 100644 packages/core/src/common/winston-logger.injectable.ts
create mode 100644 packages/core/src/main/logger/close-ipc-logging-listener.injectable.ts
create mode 100644 packages/core/src/main/logger/create-ipc-file-transport.global-override-for-injectable.ts
create mode 100644 packages/core/src/main/logger/create-ipc-file-transport.injectable.ts
create mode 100644 packages/core/src/main/logger/ipc-file-logger.injectable.ts
create mode 100644 packages/core/src/main/logger/ipc-file-logger.test.ts
create mode 100644 packages/core/src/main/logger/ipc-logging-listener.injectable.ts
create mode 100644 packages/core/src/main/logger/ipc-logging-listener.test.ts
create mode 100644 packages/core/src/main/logger/stop-ipc-logging.injectable.ts
create mode 100644 packages/core/src/renderer/before-frame-starts/runnables/listen-unload.injectable.ts
create mode 100644 packages/core/src/renderer/logger/close-renderer-log-file-id.test.ts
create mode 100644 packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts
create mode 100644 packages/core/src/renderer/logger/ipc-transport.injectable.ts
create mode 100644 packages/core/src/renderer/logger/ipc-transport.test.ts
create mode 100644 packages/core/src/renderer/logger/ipc-transport.ts
create mode 100644 packages/core/src/renderer/logger/renderer-log-file-id.injectable.ts
create mode 100644 packages/core/src/renderer/logger/renderer-log-file-id.test.ts
diff --git a/packages/core/src/common/logger.injectable.ts b/packages/core/src/common/logger.injectable.ts
index bc1c5de71b..e64978e44b 100644
--- a/packages/core/src/common/logger.injectable.ts
+++ b/packages/core/src/common/logger.injectable.ts
@@ -3,20 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import { createLogger, format } from "winston";
import type { Logger } from "./logger";
-import { loggerTransportInjectionToken } from "./logger/transports";
+import winstonLoggerInjectable from "./winston-logger.injectable";
const loggerInjectable = getInjectable({
id: "logger",
instantiate: (di): Logger => {
- const baseLogger = createLogger({
- format: format.combine(
- format.splat(),
- format.simple(),
- ),
- transports: di.injectMany(loggerTransportInjectionToken),
- });
+ const baseLogger = di.inject(winstonLoggerInjectable);
return {
debug: (message, ...data) => baseLogger.debug(message, ...data),
diff --git a/packages/core/src/common/logger/ipc-file-logger-channel.ts b/packages/core/src/common/logger/ipc-file-logger-channel.ts
new file mode 100644
index 0000000000..7550f4f314
--- /dev/null
+++ b/packages/core/src/common/logger/ipc-file-logger-channel.ts
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token";
+
+export interface IpcFileLogObject {
+ fileId: string;
+ entry: {
+ level: string;
+ message: string;
+ internalMessage: string;
+ };
+}
+
+export type IpcFileLoggerChannel = MessageChannel;
+
+export const ipcFileLoggerChannel: IpcFileLoggerChannel = {
+ id: "ipc-file-logger-channel",
+};
+
+export const closeIpcFileLoggerChannel: MessageChannel = {
+ id: "close-ipc-file-logger-channel",
+};
diff --git a/packages/core/src/common/winston-logger.global-override-for-injectable.ts b/packages/core/src/common/winston-logger.global-override-for-injectable.ts
new file mode 100644
index 0000000000..3d55f914dd
--- /dev/null
+++ b/packages/core/src/common/winston-logger.global-override-for-injectable.ts
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type winston from "winston";
+import { getGlobalOverride } from "@k8slens/test-utils";
+import { noop } from "@k8slens/utilities";
+import winstonLoggerInjectable from "./winston-logger.injectable";
+
+export default getGlobalOverride(winstonLoggerInjectable, () => ({
+ log: noop,
+ add: noop,
+ remove: noop,
+ clear: noop,
+ close: noop,
+
+ warn: noop,
+ debug: noop,
+ error: noop,
+ info: noop,
+ silly: noop,
+}) as winston.Logger);
diff --git a/packages/core/src/common/winston-logger.injectable.ts b/packages/core/src/common/winston-logger.injectable.ts
new file mode 100644
index 0000000000..481d520fac
--- /dev/null
+++ b/packages/core/src/common/winston-logger.injectable.ts
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { createLogger, format } from "winston";
+import { loggerTransportInjectionToken } from "./logger/transports";
+
+const winstonLoggerInjectable = getInjectable({
+ id: "winston-logger",
+ instantiate: (di) =>
+ createLogger({
+ format: format.combine(format.splat(), format.simple()),
+ transports: di.injectMany(loggerTransportInjectionToken),
+ }),
+});
+
+export default winstonLoggerInjectable;
diff --git a/packages/core/src/main/logger/close-ipc-logging-listener.injectable.ts b/packages/core/src/main/logger/close-ipc-logging-listener.injectable.ts
new file mode 100644
index 0000000000..6870a29c61
--- /dev/null
+++ b/packages/core/src/main/logger/close-ipc-logging-listener.injectable.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
+import { getMessageChannelListenerInjectable } from "../../common/utils/channel/message-channel-listener-injection-token";
+import {
+ closeIpcFileLoggerChannel,
+} from "../../common/logger/ipc-file-logger-channel";
+
+const closeIpcFileLoggingListenerInjectable = getMessageChannelListenerInjectable({
+ id: "close-ipc-file-logging",
+ channel: closeIpcFileLoggerChannel,
+ handler: (di) => {
+ const ipcFileLogger = di.inject(ipcFileLoggerInjectable);
+
+ return (fileId) => ipcFileLogger.close(fileId);
+ },
+});
+
+export default closeIpcFileLoggingListenerInjectable;
diff --git a/packages/core/src/main/logger/create-ipc-file-transport.global-override-for-injectable.ts b/packages/core/src/main/logger/create-ipc-file-transport.global-override-for-injectable.ts
new file mode 100644
index 0000000000..98fc62da49
--- /dev/null
+++ b/packages/core/src/main/logger/create-ipc-file-transport.global-override-for-injectable.ts
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { transports } from "winston";
+import { getGlobalOverride } from "@k8slens/test-utils";
+import { noop } from "@k8slens/utilities";
+import createIpcFileLoggerTransportInjectable from "./create-ipc-file-transport.injectable";
+
+export default getGlobalOverride(
+ createIpcFileLoggerTransportInjectable,
+ () => () =>
+ ({
+ log: noop,
+ close: noop,
+ } as typeof transports.File),
+);
diff --git a/packages/core/src/main/logger/create-ipc-file-transport.injectable.ts b/packages/core/src/main/logger/create-ipc-file-transport.injectable.ts
new file mode 100644
index 0000000000..f29e02fc90
--- /dev/null
+++ b/packages/core/src/main/logger/create-ipc-file-transport.injectable.ts
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { transports } from "winston";
+import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable";
+
+const createIpcFileLoggerTransportInjectable = getInjectable({
+ id: "create-ipc-file-logger-transport",
+ instantiate: (di) => {
+ const options = {
+ dirname: di.inject(directoryForLogsInjectable),
+ maxsize: 1024 * 1024,
+ maxFiles: 2,
+ tailable: true,
+ };
+
+ return (fileId: string) =>
+ new transports.File({
+ ...options,
+ filename: `lens-${fileId}.log`,
+ });
+ },
+ causesSideEffects: true,
+});
+
+export default createIpcFileLoggerTransportInjectable;
diff --git a/packages/core/src/main/logger/file-transport.injectable.ts b/packages/core/src/main/logger/file-transport.injectable.ts
index fcf855eec4..c71b44a2a0 100644
--- a/packages/core/src/main/logger/file-transport.injectable.ts
+++ b/packages/core/src/main/logger/file-transport.injectable.ts
@@ -7,8 +7,8 @@ import { transports } from "winston";
import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable";
import { loggerTransportInjectionToken } from "../../common/logger/transports";
-const fileLoggerTranportInjectable = getInjectable({
- id: "file-logger-tranport",
+const fileLoggerTransportInjectable = getInjectable({
+ id: "file-logger-transport",
instantiate: (di) => new transports.File({
handleExceptions: false,
level: "debug",
@@ -26,4 +26,4 @@ const fileLoggerTranportInjectable = getInjectable({
decorable: false,
});
-export default fileLoggerTranportInjectable;
+export default fileLoggerTransportInjectable;
diff --git a/packages/core/src/main/logger/ipc-file-logger.injectable.ts b/packages/core/src/main/logger/ipc-file-logger.injectable.ts
new file mode 100644
index 0000000000..df82ef7c6b
--- /dev/null
+++ b/packages/core/src/main/logger/ipc-file-logger.injectable.ts
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { getOrInsertWith } from "@k8slens/utilities";
+import type { LogEntry, transports } from "winston";
+import createIpcFileLoggerTransportInjectable from "./create-ipc-file-transport.injectable";
+
+export interface IpcFileLogger {
+ log: (fileLog: { fileId: string; entry: LogEntry }) => void;
+ close: (fileId: string) => void;
+ closeAll: () => void;
+}
+
+const ipcFileLoggerInjectable = getInjectable({
+ id: "ipc-file-logger",
+ instantiate: (di): IpcFileLogger => {
+ const createIpcFileTransport = di.inject(createIpcFileLoggerTransportInjectable);
+ const fileTransports = new Map();
+
+ function log({ fileId, entry }: { fileId: string; entry: LogEntry }) {
+ const transport = getOrInsertWith(
+ fileTransports,
+ fileId,
+ () => createIpcFileTransport(fileId),
+ );
+
+ transport?.log?.(entry, () => {});
+ }
+
+ function close(fileId: string) {
+ const transport = fileTransports.get(fileId);
+
+ if (transport) {
+ transport.close?.();
+ fileTransports.delete(fileId);
+ }
+ }
+
+ function closeAll() {
+ for (const fileId of fileTransports.keys()) {
+ close(fileId);
+ }
+ }
+
+ return {
+ log,
+ close,
+ closeAll,
+ };
+ },
+});
+
+export default ipcFileLoggerInjectable;
diff --git a/packages/core/src/main/logger/ipc-file-logger.test.ts b/packages/core/src/main/logger/ipc-file-logger.test.ts
new file mode 100644
index 0000000000..1ff727e200
--- /dev/null
+++ b/packages/core/src/main/logger/ipc-file-logger.test.ts
@@ -0,0 +1,160 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getDiForUnitTesting } from "../getDiForUnitTesting";
+import createIpcFileLoggerTransportInjectable from "./create-ipc-file-transport.injectable";
+import type { IpcFileLogger } from "./ipc-file-logger.injectable";
+import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
+
+describe("ipc file logger in main", () => {
+ let logMock: jest.Mock;
+ let closeMock: jest.Mock;
+ let createFileTransportMock: jest.Mock;
+ let logger: IpcFileLogger;
+
+ beforeEach(() => {
+ logMock = jest.fn();
+ closeMock = jest.fn();
+ createFileTransportMock = jest.fn(() => ({
+ log: logMock,
+ close: closeMock,
+ }));
+
+ const di = getDiForUnitTesting();
+
+ di.override(createIpcFileLoggerTransportInjectable, () => createFileTransportMock);
+ logger = di.inject(ipcFileLoggerInjectable);
+ });
+
+ it("creates a transport for new log file", () => {
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ expect(createFileTransportMock).toHaveBeenCalledWith("some-log-file");
+ });
+
+ it("uses existing transport for log file", () => {
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ expect(createFileTransportMock).toHaveBeenCalledTimes(1);
+
+ expect(createFileTransportMock).toHaveBeenCalledWith("some-log-file");
+ });
+
+ it("creates separate transport for each log file", () => {
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ logger.log({
+ fileId: "some-other-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ logger.log({
+ fileId: "some-yet-another-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ expect(createFileTransportMock).toHaveBeenCalledTimes(3);
+
+ expect(createFileTransportMock).toHaveBeenCalledWith("some-log-file");
+
+ expect(createFileTransportMock).toHaveBeenCalledWith("some-other-log-file");
+
+ expect(createFileTransportMock).toHaveBeenCalledWith("some-yet-another-log-file");
+ });
+
+ it("logs using file transport", () => {
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "some-log-message" },
+ });
+ expect(logMock.mock.calls[0][0]).toEqual({
+ level: "irrelevant",
+ message: "some-log-message",
+ });
+ });
+
+ it("logs to correct files", () => {
+ const someLogMock = jest.fn();
+ const someOthertLogMock = jest.fn();
+
+ createFileTransportMock.mockImplementation((fileId: string) => {
+ if (fileId === "some-log-file") {
+ return { log: someLogMock };
+ }
+
+ if (fileId === "some-other-log-file") {
+ return { log: someOthertLogMock };
+ }
+
+ return null;
+ });
+
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "some-log-message" },
+ });
+ logger.log({
+ fileId: "some-other-log-file",
+ entry: { level: "irrelevant", message: "some-other-log-message" },
+ });
+
+ expect(someLogMock).toHaveBeenCalledTimes(1);
+ expect(someLogMock.mock.calls[0][0]).toEqual({
+ level: "irrelevant",
+ message: "some-log-message",
+ });
+ expect(someOthertLogMock).toHaveBeenCalledTimes(1);
+ expect(someOthertLogMock.mock.calls[0][0]).toEqual({
+ level: "irrelevant",
+ message: "some-other-log-message",
+ });
+ });
+
+ it("closes transport (to ensure no file handles are left open)", () => {
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ logger.close("some-log-file");
+
+ expect(closeMock).toHaveBeenCalled();
+ });
+
+ it("creates a new transport once needed after closing previous", () => {
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ logger.close("some-log-file");
+
+ logger.log({
+ fileId: "some-log-file",
+ entry: { level: "irrelevant", message: "irrelevant" },
+ });
+
+ expect(createFileTransportMock).toHaveBeenCalledTimes(2);
+ expect(logMock).toHaveBeenCalledTimes(2);
+ });
+});
diff --git a/packages/core/src/main/logger/ipc-logging-listener.injectable.ts b/packages/core/src/main/logger/ipc-logging-listener.injectable.ts
new file mode 100644
index 0000000000..3a3748846b
--- /dev/null
+++ b/packages/core/src/main/logger/ipc-logging-listener.injectable.ts
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
+import { getMessageChannelListenerInjectable } from "../../common/utils/channel/message-channel-listener-injection-token";
+import type { IpcFileLogObject } from "../../common/logger/ipc-file-logger-channel";
+import { ipcFileLoggerChannel } from "../../common/logger/ipc-file-logger-channel";
+import { MESSAGE } from "triple-beam";
+
+/**
+ * Winston uses symbol property for the actual message.
+ *
+ * For that to get through IPC, use the internalMessage property instead
+ */
+export function deserializeLogFromIpc(ipcFileLogObject: IpcFileLogObject) {
+ const { internalMessage, ...standardEntry } = ipcFileLogObject.entry;
+
+ return {
+ ...ipcFileLogObject,
+ entry: {
+ ...standardEntry,
+ [MESSAGE]: internalMessage,
+ },
+ };
+}
+
+const ipcFileLoggingListenerInjectable = getMessageChannelListenerInjectable({
+ id: "ipc-file-logging",
+ channel: ipcFileLoggerChannel,
+ handler: (di) => {
+ const logger = di.inject(ipcFileLoggerInjectable);
+
+ return (ipcFileLogObject) =>
+ logger.log(deserializeLogFromIpc(ipcFileLogObject));
+ },
+});
+
+export default ipcFileLoggingListenerInjectable;
diff --git a/packages/core/src/main/logger/ipc-logging-listener.test.ts b/packages/core/src/main/logger/ipc-logging-listener.test.ts
new file mode 100644
index 0000000000..55bb64f4c4
--- /dev/null
+++ b/packages/core/src/main/logger/ipc-logging-listener.test.ts
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import { MESSAGE } from "triple-beam";
+import { deserializeLogFromIpc } from "./ipc-logging-listener.injectable";
+
+describe("Ipc log deserialization", () => {
+ it("fills in the unique symbol message property Winston transports use internally", () => {
+ const logObject = {
+ fileId: "irrelevant",
+ entry: {
+ level: "irrelevant",
+ message: "some public message",
+ internalMessage: "some internal message",
+ someProperty: "irrelevant",
+ },
+ };
+
+ expect(deserializeLogFromIpc(logObject)).toEqual({
+ entry: {
+ level: "irrelevant",
+ message: "some public message",
+ [MESSAGE]: "some internal message",
+ someProperty: "irrelevant",
+ },
+ fileId: "irrelevant",
+ });
+ });
+});
diff --git a/packages/core/src/main/logger/stop-ipc-logging.injectable.ts b/packages/core/src/main/logger/stop-ipc-logging.injectable.ts
new file mode 100644
index 0000000000..bdb94a412e
--- /dev/null
+++ b/packages/core/src/main/logger/stop-ipc-logging.injectable.ts
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { beforeQuitOfFrontEndInjectionToken } from "../start-main-application/runnable-tokens/phases";
+import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
+
+const stopIpcLoggingInjectable = getInjectable({
+ id: "stop-ipc-logging",
+
+ instantiate: (di) => {
+ const ipcFileLogger = di.inject(ipcFileLoggerInjectable);
+
+ return {
+ run: () => {
+ ipcFileLogger.closeAll();
+
+ return undefined;
+ },
+ };
+ },
+
+ injectionToken: beforeQuitOfFrontEndInjectionToken,
+});
+
+export default stopIpcLoggingInjectable;
diff --git a/packages/core/src/renderer/before-frame-starts/runnables/listen-unload.injectable.ts b/packages/core/src/renderer/before-frame-starts/runnables/listen-unload.injectable.ts
new file mode 100644
index 0000000000..6b6e0e751c
--- /dev/null
+++ b/packages/core/src/renderer/before-frame-starts/runnables/listen-unload.injectable.ts
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import currentlyInClusterFrameInjectable from "../../routes/currently-in-cluster-frame.injectable";
+import { beforeFrameStartsSecondInjectionToken } from "../tokens";
+import loggerInjectable from "../../../common/logger.injectable";
+import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
+import frameRoutingIdInjectable from "../../frames/cluster-frame/init-cluster-frame/frame-routing-id/frame-routing-id.injectable";
+import closeRendererLogFileInjectable from "../../logger/close-renderer-log-file.injectable";
+import { unmountComponentAtNode } from "react-dom";
+
+const listenUnloadInjectable = getInjectable({
+ id: "listen-unload",
+ instantiate: (di) => ({
+ run: () => {
+ const closeRendererLogFile = di.inject(closeRendererLogFileInjectable);
+ const isClusterFrame = di.inject(currentlyInClusterFrameInjectable);
+ const logger = di.inject(loggerInjectable);
+
+ window.addEventListener("beforeunload", () => {
+ if (isClusterFrame) {
+ const hostedCluster = di.inject(hostedClusterInjectable);
+ const frameRoutingId = di.inject(frameRoutingIdInjectable);
+
+ logger.info(
+ `[CLUSTER-FRAME] Unload dashboard, clusterId=${hostedCluster?.id}, frameId=${frameRoutingId}`,
+ );
+ } else {
+ logger.info("[ROOT-FRAME]: Unload app");
+ }
+
+ closeRendererLogFile();
+ const rootElem = document.getElementById("app");
+
+ if (rootElem) {
+ unmountComponentAtNode(rootElem);
+ }
+ });
+ },
+ }),
+ injectionToken: beforeFrameStartsSecondInjectionToken,
+});
+
+export default listenUnloadInjectable;
diff --git a/packages/core/src/renderer/bootstrap.tsx b/packages/core/src/renderer/bootstrap.tsx
index a811f07d5c..75439fce13 100644
--- a/packages/core/src/renderer/bootstrap.tsx
+++ b/packages/core/src/renderer/bootstrap.tsx
@@ -6,7 +6,7 @@
import "./components/app.scss";
import React from "react";
-import { render, unmountComponentAtNode } from "react-dom";
+import { render } from "react-dom";
import { DefaultProps } from "./mui-base-theme";
import { DiContextProvider } from "@ogre-tools/injectable-react";
import type {
@@ -43,7 +43,7 @@ export async function bootstrap(di: DiContainerForInjection) {
}
try {
- await initializeApp(() => unmountComponentAtNode(rootElem));
+ await initializeApp();
} catch (error) {
console.error(`[BOOTSTRAP]: view initialization error: ${error}`, {
origin: location.href,
diff --git a/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts b/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts
index 9bd0a26a3c..9e901a8060 100644
--- a/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts
+++ b/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts
@@ -22,62 +22,51 @@ interface Dependencies {
const logPrefix = "[CLUSTER-FRAME]:";
-export const initClusterFrame = ({
- hostedCluster,
- loadExtensions,
- catalogEntityRegistry,
- frameRoutingId,
- emitAppEvent,
- logger,
- showErrorNotification,
-}: Dependencies) =>
- async (unmountRoot: () => void) => {
+export const initClusterFrame =
+ ({
+ hostedCluster,
+ loadExtensions,
+ catalogEntityRegistry,
+ frameRoutingId,
+ emitAppEvent,
+ logger,
+ showErrorNotification,
+ }: Dependencies) =>
+ async () => {
// TODO: Make catalogEntityRegistry already initialized when passed as dependency
- catalogEntityRegistry.init();
+ catalogEntityRegistry.init();
- logger.info(
- `${logPrefix} Init dashboard, clusterId=${hostedCluster.id}, frameId=${frameRoutingId}`,
- );
-
- await requestSetClusterFrameId(hostedCluster.id);
- await when(() => hostedCluster.ready.get()); // cluster.activate() is done at this point
-
- catalogEntityRegistry.activeEntity = hostedCluster.id;
-
- // Only load the extensions once the catalog has been populated.
- // Note that the Catalog might still have unprocessed entities until the extensions are fully loaded.
- when(
- () => catalogEntityRegistry.items.get().length > 0,
- () =>
- loadExtensions(),
- {
- timeout: 15_000,
- onError: (error) => {
- logger.warn(
- "[CLUSTER-FRAME]: error from activeEntity when()",
- error,
- );
-
- showErrorNotification("Failed to get KubernetesCluster for this view. Extensions will not be loaded.");
- },
- },
- );
-
- setTimeout(() => {
- emitAppEvent({
- name: "cluster",
- action: "open",
- params: {
- clusterId: hostedCluster.id,
- },
- });
- });
-
- window.onbeforeunload = () => {
logger.info(
- `${logPrefix} Unload dashboard, clusterId=${(hostedCluster.id)}, frameId=${frameRoutingId}`,
+ `${logPrefix} Init dashboard, clusterId=${hostedCluster.id}, frameId=${frameRoutingId}`,
);
- unmountRoot();
+ await requestSetClusterFrameId(hostedCluster.id);
+ await when(() => hostedCluster.ready.get()); // cluster.activate() is done at this point
+
+ catalogEntityRegistry.activeEntity = hostedCluster.id;
+
+ // Only load the extensions once the catalog has been populated.
+ // Note that the Catalog might still have unprocessed entities until the extensions are fully loaded.
+ when(
+ () => catalogEntityRegistry.items.get().length > 0,
+ () => loadExtensions(),
+ {
+ timeout: 15_000,
+ onError: (error) => {
+ logger.warn("[CLUSTER-FRAME]: error from activeEntity when()", error);
+
+ showErrorNotification("Failed to get KubernetesCluster for this view. Extensions will not be loaded.");
+ },
+ },
+ );
+
+ setTimeout(() => {
+ emitAppEvent({
+ name: "cluster",
+ action: "open",
+ params: {
+ clusterId: hostedCluster.id,
+ },
+ });
+ });
};
- };
diff --git a/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts b/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts
index f1e3024d80..e1bedb0c88 100644
--- a/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts
+++ b/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts
@@ -9,7 +9,6 @@ import lensProtocolRouterRendererInjectable from "../../protocol-handler/lens-pr
import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable";
import registerIpcListenersInjectable from "../../ipc/register-ipc-listeners.injectable";
import loadExtensionsInjectable from "../load-extensions.injectable";
-import loggerInjectable from "../../../common/logger.injectable";
import { delay } from "@k8slens/utilities";
import { broadcastMessage } from "../../../common/ipc";
import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling";
@@ -23,9 +22,8 @@ const initRootFrameInjectable = getInjectable({
const bindProtocolAddRouteHandlers = di.inject(bindProtocolAddRouteHandlersInjectable);
const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable);
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
- const logger = di.inject(loggerInjectable);
- return async (unmountRoot: () => void) => {
+ return async () => {
catalogEntityRegistry.init();
try {
@@ -56,12 +54,6 @@ const initRootFrameInjectable = getInjectable({
window.addEventListener("online", () => broadcastMessage("network:online"));
registerIpcListeners();
-
- window.addEventListener("beforeunload", () => {
- logger.info("[ROOT-FRAME]: Unload app");
-
- unmountRoot();
- });
};
},
});
diff --git a/packages/core/src/renderer/logger/close-renderer-log-file-id.test.ts b/packages/core/src/renderer/logger/close-renderer-log-file-id.test.ts
new file mode 100644
index 0000000000..1520844c30
--- /dev/null
+++ b/packages/core/src/renderer/logger/close-renderer-log-file-id.test.ts
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import winstonLoggerInjectable from "../../common/winston-logger.injectable";
+import { getDiForUnitTesting } from "../getDiForUnitTesting";
+import closeRendererLogFileInjectable from "./close-renderer-log-file.injectable";
+import type { DiContainer } from "@ogre-tools/injectable";
+import type winston from "winston";
+import type { SendMessageToChannel } from "../../common/utils/channel/message-to-channel-injection-token";
+import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
+import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
+import ipcLogTransportInjectable from "./ipc-transport.injectable";
+import type IpcLogTransport from "./ipc-transport";
+
+describe("close renderer file logging", () => {
+ let di: DiContainer;
+ let sendIpcMock: SendMessageToChannel;
+ let winstonMock: winston.Logger;
+ let ipcTransportMock: IpcLogTransport;
+
+ beforeEach(() => {
+ di = getDiForUnitTesting();
+ sendIpcMock = jest.fn();
+ winstonMock = {
+ remove: jest.fn(),
+ } as any as winston.Logger;
+ ipcTransportMock = { name: "ipc-renderer-transport" } as IpcLogTransport;
+
+ di.override(winstonLoggerInjectable, () => winstonMock);
+ di.override(sendMessageToChannelInjectionToken, () => sendIpcMock);
+ di.override(rendererLogFileIdInjectable, () => "some-log-id");
+ di.override(ipcLogTransportInjectable, () => ipcTransportMock);
+ });
+
+ it("sends the ipc close message with correct log id", () => {
+ const closeLog = di.inject(closeRendererLogFileInjectable);
+
+ closeLog();
+
+ expect(sendIpcMock).toHaveBeenCalledWith(
+ { id: "close-ipc-file-logger-channel" },
+ "some-log-id",
+ );
+ });
+
+ it("removes the transport to prevent further logging to closed file", () => {
+ const closeLog = di.inject(closeRendererLogFileInjectable);
+
+ closeLog();
+
+ expect(winstonMock.remove).toHaveBeenCalledWith({
+ name: "ipc-renderer-transport",
+ });
+ });
+});
diff --git a/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts b/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts
new file mode 100644
index 0000000000..8015708d84
--- /dev/null
+++ b/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import winstonLoggerInjectable from "../../common/winston-logger.injectable";
+import { closeIpcFileLoggerChannel } from "../../common/logger/ipc-file-logger-channel";
+import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
+import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
+import ipcLogTransportInjectable from "./ipc-transport.injectable";
+
+const closeRendererLogFileInjectable = getInjectable({
+ id: "close-renderer-log-file",
+ instantiate: (di) => {
+ const winstonLogger = di.inject(winstonLoggerInjectable);
+ const ipcLogTransport = di.inject(ipcLogTransportInjectable);
+ const messageToChannel = di.inject(sendMessageToChannelInjectionToken);
+ const fileId = di.inject(rendererLogFileIdInjectable);
+
+
+ return () => {
+ messageToChannel(closeIpcFileLoggerChannel, fileId);
+ winstonLogger.remove(ipcLogTransport);
+ };
+ },
+});
+
+export default closeRendererLogFileInjectable;
diff --git a/packages/core/src/renderer/logger/ipc-transport.injectable.ts b/packages/core/src/renderer/logger/ipc-transport.injectable.ts
new file mode 100644
index 0000000000..45139cd917
--- /dev/null
+++ b/packages/core/src/renderer/logger/ipc-transport.injectable.ts
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { loggerTransportInjectionToken } from "../../common/logger/transports";
+import type winston from "winston";
+import { MESSAGE } from "triple-beam";
+
+import IpcLogTransport from "./ipc-transport";
+import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
+import type {
+ IpcFileLogObject } from "../../common/logger/ipc-file-logger-channel";
+import {
+ closeIpcFileLoggerChannel,
+ ipcFileLoggerChannel,
+} from "../../common/logger/ipc-file-logger-channel";
+import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
+
+/**
+ * Winston uses symbol property for the actual message.
+ *
+ * For that to get through IPC, use the internalMessage property instead
+ */
+function serializeLogForIpc(
+ fileId: string,
+ entry: winston.LogEntry,
+): IpcFileLogObject {
+ return {
+ fileId,
+ entry: {
+ level: entry.level,
+ message: entry.message,
+ internalMessage: Object.getOwnPropertyDescriptor(entry, MESSAGE)?.value,
+ },
+ };
+}
+
+const ipcLogTransportInjectable = getInjectable({
+ id: "renderer-file-logger-transport",
+ instantiate: (di) => {
+ const messageToChannel = di.inject(sendMessageToChannelInjectionToken);
+ const fileId = di.inject(rendererLogFileIdInjectable);
+
+ return new IpcLogTransport({
+ sendIpcLogMessage: (entry) =>
+ messageToChannel(
+ ipcFileLoggerChannel,
+ serializeLogForIpc(fileId, entry),
+ ),
+ closeIpcLogging: () =>
+ messageToChannel(closeIpcFileLoggerChannel, fileId),
+ handleExceptions: false,
+ level: "info",
+ });
+ },
+ injectionToken: loggerTransportInjectionToken,
+});
+
+export default ipcLogTransportInjectable;
diff --git a/packages/core/src/renderer/logger/ipc-transport.test.ts b/packages/core/src/renderer/logger/ipc-transport.test.ts
new file mode 100644
index 0000000000..931df377c7
--- /dev/null
+++ b/packages/core/src/renderer/logger/ipc-transport.test.ts
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import type { DiContainer } from "@ogre-tools/injectable";
+import type { SendMessageToChannel } from "../../common/utils/channel/message-to-channel-injection-token";
+import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
+import { getDiForUnitTesting } from "../getDiForUnitTesting";
+import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
+import ipcLogTransportInjectable from "./ipc-transport.injectable";
+import { MESSAGE } from "triple-beam";
+
+describe("renderer log transport through ipc", () => {
+ let di: DiContainer;
+ let sendIpcMock: SendMessageToChannel;
+
+ beforeEach(() => {
+ sendIpcMock = jest.fn();
+ di = getDiForUnitTesting();
+ di.override(sendMessageToChannelInjectionToken, () => sendIpcMock);
+ di.override(rendererLogFileIdInjectable, () => "some-log-id");
+ });
+
+ it("send serialized ipc messages on log", () => {
+ const logTransport = di.inject(ipcLogTransportInjectable);
+
+ logTransport.log(
+ {
+ level: "info",
+ message: "some log text",
+ [MESSAGE]: "actual winston log text",
+ },
+ () => {},
+ );
+
+ expect(sendIpcMock).toHaveBeenCalledWith(
+ { id: "ipc-file-logger-channel" },
+ {
+ entry: {
+ level: "info",
+ message: "some log text",
+ internalMessage: "actual winston log text",
+ },
+ fileId: "some-log-id",
+ },
+ );
+ });
+});
diff --git a/packages/core/src/renderer/logger/ipc-transport.ts b/packages/core/src/renderer/logger/ipc-transport.ts
new file mode 100644
index 0000000000..a1f6fa819e
--- /dev/null
+++ b/packages/core/src/renderer/logger/ipc-transport.ts
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import type { LogEntry } from "winston";
+import type { TransportStreamOptions } from "winston-transport";
+import TransportStream from "winston-transport";
+
+interface IpcLogTransportOptions extends TransportStreamOptions {
+ sendIpcLogMessage: (entry: LogEntry) => void;
+ closeIpcLogging: () => void;
+}
+
+class IpcLogTransport extends TransportStream {
+ sendIpcLogMessage: (entry: LogEntry) => void;
+ closeIpcLogging: () => void;
+ name = "ipc-renderer-transport";
+
+ constructor(options: IpcLogTransportOptions) {
+ const { sendIpcLogMessage, closeIpcLogging, ...winstonOptions } = options;
+
+ super(winstonOptions);
+
+ this.sendIpcLogMessage = sendIpcLogMessage;
+ this.closeIpcLogging = closeIpcLogging;
+ }
+
+ log(logEntry: LogEntry, next: () => void) {
+ setImmediate(() => {
+ this.emit("logged", logEntry);
+ });
+ this.sendIpcLogMessage(logEntry);
+ next();
+ }
+
+ close() {
+ this.closeIpcLogging();
+ }
+}
+
+export default IpcLogTransport;
diff --git a/packages/core/src/renderer/logger/renderer-log-file-id.injectable.ts b/packages/core/src/renderer/logger/renderer-log-file-id.injectable.ts
new file mode 100644
index 0000000000..81f4196b08
--- /dev/null
+++ b/packages/core/src/renderer/logger/renderer-log-file-id.injectable.ts
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import windowLocationInjectable from "../../common/k8s-api/window-location.injectable";
+import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable";
+import { getClusterIdFromHost } from "../../common/utils";
+
+const rendererLogFileIdInjectable = getInjectable({
+ id: "renderer-log-file-id",
+ instantiate: (di) => {
+ let frameId: string;
+ const currentlyInClusterFrame = di.inject(currentlyInClusterFrameInjectable);
+
+ if (currentlyInClusterFrame) {
+ const { host } = di.inject(windowLocationInjectable);
+ const clusterId = getClusterIdFromHost(host);
+
+ frameId = `cluster-${clusterId}`;
+ } else {
+ frameId = "main";
+ }
+
+ return `renderer-${frameId}`;
+ },
+});
+
+export default rendererLogFileIdInjectable;
diff --git a/packages/core/src/renderer/logger/renderer-log-file-id.test.ts b/packages/core/src/renderer/logger/renderer-log-file-id.test.ts
new file mode 100644
index 0000000000..bd2abf7a63
--- /dev/null
+++ b/packages/core/src/renderer/logger/renderer-log-file-id.test.ts
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import windowLocationInjectable from "../../common/k8s-api/window-location.injectable";
+import { getDiForUnitTesting } from "../getDiForUnitTesting";
+import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable";
+import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
+
+describe("renderer log file id", () => {
+
+ it("clearly names log for renderer main frame", () => {
+ const di = getDiForUnitTesting();
+
+ di.override(currentlyInClusterFrameInjectable, () => false);
+
+ const mainFileId = di.inject(rendererLogFileIdInjectable);
+
+ expect(mainFileId).toBe("renderer-main");
+ });
+
+ it("includes cluster id in renderer log file names", () => {
+ const di = getDiForUnitTesting();
+
+ di.override(currentlyInClusterFrameInjectable, () => true);
+ di.override(windowLocationInjectable, () => ({
+ host: "some-cluster.lens.app",
+ port: "irrelevant",
+ }));
+ const clusterFileId = di.inject(rendererLogFileIdInjectable);
+
+ expect(clusterFileId).toBe("renderer-cluster-some-cluster");
+ });
+});
From 54093242367717292312df01905d052b66017953 Mon Sep 17 00:00:00 2001
From: Sebastian Malton
Date: Tue, 21 Mar 2023 16:13:27 -0400
Subject: [PATCH 4/9] Revert "Renderer file logging through IPC" (#7393)
This reverts commit 48db54ec9e42b712d97a67b8a8b85f8096e1422d.
---
packages/core/src/common/logger.injectable.ts | 11 +-
.../common/logger/ipc-file-logger-channel.ts | 24 ---
...n-logger.global-override-for-injectable.ts | 23 ---
.../src/common/winston-logger.injectable.ts | 18 --
.../close-ipc-logging-listener.injectable.ts | 21 ---
...ransport.global-override-for-injectable.ts | 18 --
.../create-ipc-file-transport.injectable.ts | 28 ---
.../main/logger/file-transport.injectable.ts | 6 +-
.../main/logger/ipc-file-logger.injectable.ts | 55 ------
.../src/main/logger/ipc-file-logger.test.ts | 160 ------------------
.../logger/ipc-logging-listener.injectable.ts | 39 -----
.../main/logger/ipc-logging-listener.test.ts | 31 ----
.../logger/stop-ipc-logging.injectable.ts | 27 ---
.../runnables/listen-unload.injectable.ts | 46 -----
packages/core/src/renderer/bootstrap.tsx | 4 +-
.../init-cluster-frame/init-cluster-frame.ts | 87 +++++-----
.../root-frame/init-root-frame.injectable.ts | 10 +-
.../logger/close-renderer-log-file-id.test.ts | 56 ------
.../close-renderer-log-file.injectable.ts | 28 ---
.../logger/ipc-transport.injectable.ts | 60 -------
.../src/renderer/logger/ipc-transport.test.ts | 48 ------
.../core/src/renderer/logger/ipc-transport.ts | 41 -----
.../logger/renderer-log-file-id.injectable.ts | 29 ----
.../logger/renderer-log-file-id.test.ts | 34 ----
24 files changed, 72 insertions(+), 832 deletions(-)
delete mode 100644 packages/core/src/common/logger/ipc-file-logger-channel.ts
delete mode 100644 packages/core/src/common/winston-logger.global-override-for-injectable.ts
delete mode 100644 packages/core/src/common/winston-logger.injectable.ts
delete mode 100644 packages/core/src/main/logger/close-ipc-logging-listener.injectable.ts
delete mode 100644 packages/core/src/main/logger/create-ipc-file-transport.global-override-for-injectable.ts
delete mode 100644 packages/core/src/main/logger/create-ipc-file-transport.injectable.ts
delete mode 100644 packages/core/src/main/logger/ipc-file-logger.injectable.ts
delete mode 100644 packages/core/src/main/logger/ipc-file-logger.test.ts
delete mode 100644 packages/core/src/main/logger/ipc-logging-listener.injectable.ts
delete mode 100644 packages/core/src/main/logger/ipc-logging-listener.test.ts
delete mode 100644 packages/core/src/main/logger/stop-ipc-logging.injectable.ts
delete mode 100644 packages/core/src/renderer/before-frame-starts/runnables/listen-unload.injectable.ts
delete mode 100644 packages/core/src/renderer/logger/close-renderer-log-file-id.test.ts
delete mode 100644 packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts
delete mode 100644 packages/core/src/renderer/logger/ipc-transport.injectable.ts
delete mode 100644 packages/core/src/renderer/logger/ipc-transport.test.ts
delete mode 100644 packages/core/src/renderer/logger/ipc-transport.ts
delete mode 100644 packages/core/src/renderer/logger/renderer-log-file-id.injectable.ts
delete mode 100644 packages/core/src/renderer/logger/renderer-log-file-id.test.ts
diff --git a/packages/core/src/common/logger.injectable.ts b/packages/core/src/common/logger.injectable.ts
index e64978e44b..bc1c5de71b 100644
--- a/packages/core/src/common/logger.injectable.ts
+++ b/packages/core/src/common/logger.injectable.ts
@@ -3,13 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
+import { createLogger, format } from "winston";
import type { Logger } from "./logger";
-import winstonLoggerInjectable from "./winston-logger.injectable";
+import { loggerTransportInjectionToken } from "./logger/transports";
const loggerInjectable = getInjectable({
id: "logger",
instantiate: (di): Logger => {
- const baseLogger = di.inject(winstonLoggerInjectable);
+ const baseLogger = createLogger({
+ format: format.combine(
+ format.splat(),
+ format.simple(),
+ ),
+ transports: di.injectMany(loggerTransportInjectionToken),
+ });
return {
debug: (message, ...data) => baseLogger.debug(message, ...data),
diff --git a/packages/core/src/common/logger/ipc-file-logger-channel.ts b/packages/core/src/common/logger/ipc-file-logger-channel.ts
deleted file mode 100644
index 7550f4f314..0000000000
--- a/packages/core/src/common/logger/ipc-file-logger-channel.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token";
-
-export interface IpcFileLogObject {
- fileId: string;
- entry: {
- level: string;
- message: string;
- internalMessage: string;
- };
-}
-
-export type IpcFileLoggerChannel = MessageChannel;
-
-export const ipcFileLoggerChannel: IpcFileLoggerChannel = {
- id: "ipc-file-logger-channel",
-};
-
-export const closeIpcFileLoggerChannel: MessageChannel = {
- id: "close-ipc-file-logger-channel",
-};
diff --git a/packages/core/src/common/winston-logger.global-override-for-injectable.ts b/packages/core/src/common/winston-logger.global-override-for-injectable.ts
deleted file mode 100644
index 3d55f914dd..0000000000
--- a/packages/core/src/common/winston-logger.global-override-for-injectable.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type winston from "winston";
-import { getGlobalOverride } from "@k8slens/test-utils";
-import { noop } from "@k8slens/utilities";
-import winstonLoggerInjectable from "./winston-logger.injectable";
-
-export default getGlobalOverride(winstonLoggerInjectable, () => ({
- log: noop,
- add: noop,
- remove: noop,
- clear: noop,
- close: noop,
-
- warn: noop,
- debug: noop,
- error: noop,
- info: noop,
- silly: noop,
-}) as winston.Logger);
diff --git a/packages/core/src/common/winston-logger.injectable.ts b/packages/core/src/common/winston-logger.injectable.ts
deleted file mode 100644
index 481d520fac..0000000000
--- a/packages/core/src/common/winston-logger.injectable.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import { createLogger, format } from "winston";
-import { loggerTransportInjectionToken } from "./logger/transports";
-
-const winstonLoggerInjectable = getInjectable({
- id: "winston-logger",
- instantiate: (di) =>
- createLogger({
- format: format.combine(format.splat(), format.simple()),
- transports: di.injectMany(loggerTransportInjectionToken),
- }),
-});
-
-export default winstonLoggerInjectable;
diff --git a/packages/core/src/main/logger/close-ipc-logging-listener.injectable.ts b/packages/core/src/main/logger/close-ipc-logging-listener.injectable.ts
deleted file mode 100644
index 6870a29c61..0000000000
--- a/packages/core/src/main/logger/close-ipc-logging-listener.injectable.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
-import { getMessageChannelListenerInjectable } from "../../common/utils/channel/message-channel-listener-injection-token";
-import {
- closeIpcFileLoggerChannel,
-} from "../../common/logger/ipc-file-logger-channel";
-
-const closeIpcFileLoggingListenerInjectable = getMessageChannelListenerInjectable({
- id: "close-ipc-file-logging",
- channel: closeIpcFileLoggerChannel,
- handler: (di) => {
- const ipcFileLogger = di.inject(ipcFileLoggerInjectable);
-
- return (fileId) => ipcFileLogger.close(fileId);
- },
-});
-
-export default closeIpcFileLoggingListenerInjectable;
diff --git a/packages/core/src/main/logger/create-ipc-file-transport.global-override-for-injectable.ts b/packages/core/src/main/logger/create-ipc-file-transport.global-override-for-injectable.ts
deleted file mode 100644
index 98fc62da49..0000000000
--- a/packages/core/src/main/logger/create-ipc-file-transport.global-override-for-injectable.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type { transports } from "winston";
-import { getGlobalOverride } from "@k8slens/test-utils";
-import { noop } from "@k8slens/utilities";
-import createIpcFileLoggerTransportInjectable from "./create-ipc-file-transport.injectable";
-
-export default getGlobalOverride(
- createIpcFileLoggerTransportInjectable,
- () => () =>
- ({
- log: noop,
- close: noop,
- } as typeof transports.File),
-);
diff --git a/packages/core/src/main/logger/create-ipc-file-transport.injectable.ts b/packages/core/src/main/logger/create-ipc-file-transport.injectable.ts
deleted file mode 100644
index f29e02fc90..0000000000
--- a/packages/core/src/main/logger/create-ipc-file-transport.injectable.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import { transports } from "winston";
-import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable";
-
-const createIpcFileLoggerTransportInjectable = getInjectable({
- id: "create-ipc-file-logger-transport",
- instantiate: (di) => {
- const options = {
- dirname: di.inject(directoryForLogsInjectable),
- maxsize: 1024 * 1024,
- maxFiles: 2,
- tailable: true,
- };
-
- return (fileId: string) =>
- new transports.File({
- ...options,
- filename: `lens-${fileId}.log`,
- });
- },
- causesSideEffects: true,
-});
-
-export default createIpcFileLoggerTransportInjectable;
diff --git a/packages/core/src/main/logger/file-transport.injectable.ts b/packages/core/src/main/logger/file-transport.injectable.ts
index c71b44a2a0..fcf855eec4 100644
--- a/packages/core/src/main/logger/file-transport.injectable.ts
+++ b/packages/core/src/main/logger/file-transport.injectable.ts
@@ -7,8 +7,8 @@ import { transports } from "winston";
import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable";
import { loggerTransportInjectionToken } from "../../common/logger/transports";
-const fileLoggerTransportInjectable = getInjectable({
- id: "file-logger-transport",
+const fileLoggerTranportInjectable = getInjectable({
+ id: "file-logger-tranport",
instantiate: (di) => new transports.File({
handleExceptions: false,
level: "debug",
@@ -26,4 +26,4 @@ const fileLoggerTransportInjectable = getInjectable({
decorable: false,
});
-export default fileLoggerTransportInjectable;
+export default fileLoggerTranportInjectable;
diff --git a/packages/core/src/main/logger/ipc-file-logger.injectable.ts b/packages/core/src/main/logger/ipc-file-logger.injectable.ts
deleted file mode 100644
index df82ef7c6b..0000000000
--- a/packages/core/src/main/logger/ipc-file-logger.injectable.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import { getOrInsertWith } from "@k8slens/utilities";
-import type { LogEntry, transports } from "winston";
-import createIpcFileLoggerTransportInjectable from "./create-ipc-file-transport.injectable";
-
-export interface IpcFileLogger {
- log: (fileLog: { fileId: string; entry: LogEntry }) => void;
- close: (fileId: string) => void;
- closeAll: () => void;
-}
-
-const ipcFileLoggerInjectable = getInjectable({
- id: "ipc-file-logger",
- instantiate: (di): IpcFileLogger => {
- const createIpcFileTransport = di.inject(createIpcFileLoggerTransportInjectable);
- const fileTransports = new Map();
-
- function log({ fileId, entry }: { fileId: string; entry: LogEntry }) {
- const transport = getOrInsertWith(
- fileTransports,
- fileId,
- () => createIpcFileTransport(fileId),
- );
-
- transport?.log?.(entry, () => {});
- }
-
- function close(fileId: string) {
- const transport = fileTransports.get(fileId);
-
- if (transport) {
- transport.close?.();
- fileTransports.delete(fileId);
- }
- }
-
- function closeAll() {
- for (const fileId of fileTransports.keys()) {
- close(fileId);
- }
- }
-
- return {
- log,
- close,
- closeAll,
- };
- },
-});
-
-export default ipcFileLoggerInjectable;
diff --git a/packages/core/src/main/logger/ipc-file-logger.test.ts b/packages/core/src/main/logger/ipc-file-logger.test.ts
deleted file mode 100644
index 1ff727e200..0000000000
--- a/packages/core/src/main/logger/ipc-file-logger.test.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getDiForUnitTesting } from "../getDiForUnitTesting";
-import createIpcFileLoggerTransportInjectable from "./create-ipc-file-transport.injectable";
-import type { IpcFileLogger } from "./ipc-file-logger.injectable";
-import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
-
-describe("ipc file logger in main", () => {
- let logMock: jest.Mock;
- let closeMock: jest.Mock;
- let createFileTransportMock: jest.Mock;
- let logger: IpcFileLogger;
-
- beforeEach(() => {
- logMock = jest.fn();
- closeMock = jest.fn();
- createFileTransportMock = jest.fn(() => ({
- log: logMock,
- close: closeMock,
- }));
-
- const di = getDiForUnitTesting();
-
- di.override(createIpcFileLoggerTransportInjectable, () => createFileTransportMock);
- logger = di.inject(ipcFileLoggerInjectable);
- });
-
- it("creates a transport for new log file", () => {
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- expect(createFileTransportMock).toHaveBeenCalledWith("some-log-file");
- });
-
- it("uses existing transport for log file", () => {
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- expect(createFileTransportMock).toHaveBeenCalledTimes(1);
-
- expect(createFileTransportMock).toHaveBeenCalledWith("some-log-file");
- });
-
- it("creates separate transport for each log file", () => {
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- logger.log({
- fileId: "some-other-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- logger.log({
- fileId: "some-yet-another-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- expect(createFileTransportMock).toHaveBeenCalledTimes(3);
-
- expect(createFileTransportMock).toHaveBeenCalledWith("some-log-file");
-
- expect(createFileTransportMock).toHaveBeenCalledWith("some-other-log-file");
-
- expect(createFileTransportMock).toHaveBeenCalledWith("some-yet-another-log-file");
- });
-
- it("logs using file transport", () => {
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "some-log-message" },
- });
- expect(logMock.mock.calls[0][0]).toEqual({
- level: "irrelevant",
- message: "some-log-message",
- });
- });
-
- it("logs to correct files", () => {
- const someLogMock = jest.fn();
- const someOthertLogMock = jest.fn();
-
- createFileTransportMock.mockImplementation((fileId: string) => {
- if (fileId === "some-log-file") {
- return { log: someLogMock };
- }
-
- if (fileId === "some-other-log-file") {
- return { log: someOthertLogMock };
- }
-
- return null;
- });
-
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "some-log-message" },
- });
- logger.log({
- fileId: "some-other-log-file",
- entry: { level: "irrelevant", message: "some-other-log-message" },
- });
-
- expect(someLogMock).toHaveBeenCalledTimes(1);
- expect(someLogMock.mock.calls[0][0]).toEqual({
- level: "irrelevant",
- message: "some-log-message",
- });
- expect(someOthertLogMock).toHaveBeenCalledTimes(1);
- expect(someOthertLogMock.mock.calls[0][0]).toEqual({
- level: "irrelevant",
- message: "some-other-log-message",
- });
- });
-
- it("closes transport (to ensure no file handles are left open)", () => {
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- logger.close("some-log-file");
-
- expect(closeMock).toHaveBeenCalled();
- });
-
- it("creates a new transport once needed after closing previous", () => {
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- logger.close("some-log-file");
-
- logger.log({
- fileId: "some-log-file",
- entry: { level: "irrelevant", message: "irrelevant" },
- });
-
- expect(createFileTransportMock).toHaveBeenCalledTimes(2);
- expect(logMock).toHaveBeenCalledTimes(2);
- });
-});
diff --git a/packages/core/src/main/logger/ipc-logging-listener.injectable.ts b/packages/core/src/main/logger/ipc-logging-listener.injectable.ts
deleted file mode 100644
index 3a3748846b..0000000000
--- a/packages/core/src/main/logger/ipc-logging-listener.injectable.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
-import { getMessageChannelListenerInjectable } from "../../common/utils/channel/message-channel-listener-injection-token";
-import type { IpcFileLogObject } from "../../common/logger/ipc-file-logger-channel";
-import { ipcFileLoggerChannel } from "../../common/logger/ipc-file-logger-channel";
-import { MESSAGE } from "triple-beam";
-
-/**
- * Winston uses symbol property for the actual message.
- *
- * For that to get through IPC, use the internalMessage property instead
- */
-export function deserializeLogFromIpc(ipcFileLogObject: IpcFileLogObject) {
- const { internalMessage, ...standardEntry } = ipcFileLogObject.entry;
-
- return {
- ...ipcFileLogObject,
- entry: {
- ...standardEntry,
- [MESSAGE]: internalMessage,
- },
- };
-}
-
-const ipcFileLoggingListenerInjectable = getMessageChannelListenerInjectable({
- id: "ipc-file-logging",
- channel: ipcFileLoggerChannel,
- handler: (di) => {
- const logger = di.inject(ipcFileLoggerInjectable);
-
- return (ipcFileLogObject) =>
- logger.log(deserializeLogFromIpc(ipcFileLogObject));
- },
-});
-
-export default ipcFileLoggingListenerInjectable;
diff --git a/packages/core/src/main/logger/ipc-logging-listener.test.ts b/packages/core/src/main/logger/ipc-logging-listener.test.ts
deleted file mode 100644
index 55bb64f4c4..0000000000
--- a/packages/core/src/main/logger/ipc-logging-listener.test.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { MESSAGE } from "triple-beam";
-import { deserializeLogFromIpc } from "./ipc-logging-listener.injectable";
-
-describe("Ipc log deserialization", () => {
- it("fills in the unique symbol message property Winston transports use internally", () => {
- const logObject = {
- fileId: "irrelevant",
- entry: {
- level: "irrelevant",
- message: "some public message",
- internalMessage: "some internal message",
- someProperty: "irrelevant",
- },
- };
-
- expect(deserializeLogFromIpc(logObject)).toEqual({
- entry: {
- level: "irrelevant",
- message: "some public message",
- [MESSAGE]: "some internal message",
- someProperty: "irrelevant",
- },
- fileId: "irrelevant",
- });
- });
-});
diff --git a/packages/core/src/main/logger/stop-ipc-logging.injectable.ts b/packages/core/src/main/logger/stop-ipc-logging.injectable.ts
deleted file mode 100644
index bdb94a412e..0000000000
--- a/packages/core/src/main/logger/stop-ipc-logging.injectable.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import { beforeQuitOfFrontEndInjectionToken } from "../start-main-application/runnable-tokens/phases";
-import ipcFileLoggerInjectable from "./ipc-file-logger.injectable";
-
-const stopIpcLoggingInjectable = getInjectable({
- id: "stop-ipc-logging",
-
- instantiate: (di) => {
- const ipcFileLogger = di.inject(ipcFileLoggerInjectable);
-
- return {
- run: () => {
- ipcFileLogger.closeAll();
-
- return undefined;
- },
- };
- },
-
- injectionToken: beforeQuitOfFrontEndInjectionToken,
-});
-
-export default stopIpcLoggingInjectable;
diff --git a/packages/core/src/renderer/before-frame-starts/runnables/listen-unload.injectable.ts b/packages/core/src/renderer/before-frame-starts/runnables/listen-unload.injectable.ts
deleted file mode 100644
index 6b6e0e751c..0000000000
--- a/packages/core/src/renderer/before-frame-starts/runnables/listen-unload.injectable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import currentlyInClusterFrameInjectable from "../../routes/currently-in-cluster-frame.injectable";
-import { beforeFrameStartsSecondInjectionToken } from "../tokens";
-import loggerInjectable from "../../../common/logger.injectable";
-import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
-import frameRoutingIdInjectable from "../../frames/cluster-frame/init-cluster-frame/frame-routing-id/frame-routing-id.injectable";
-import closeRendererLogFileInjectable from "../../logger/close-renderer-log-file.injectable";
-import { unmountComponentAtNode } from "react-dom";
-
-const listenUnloadInjectable = getInjectable({
- id: "listen-unload",
- instantiate: (di) => ({
- run: () => {
- const closeRendererLogFile = di.inject(closeRendererLogFileInjectable);
- const isClusterFrame = di.inject(currentlyInClusterFrameInjectable);
- const logger = di.inject(loggerInjectable);
-
- window.addEventListener("beforeunload", () => {
- if (isClusterFrame) {
- const hostedCluster = di.inject(hostedClusterInjectable);
- const frameRoutingId = di.inject(frameRoutingIdInjectable);
-
- logger.info(
- `[CLUSTER-FRAME] Unload dashboard, clusterId=${hostedCluster?.id}, frameId=${frameRoutingId}`,
- );
- } else {
- logger.info("[ROOT-FRAME]: Unload app");
- }
-
- closeRendererLogFile();
- const rootElem = document.getElementById("app");
-
- if (rootElem) {
- unmountComponentAtNode(rootElem);
- }
- });
- },
- }),
- injectionToken: beforeFrameStartsSecondInjectionToken,
-});
-
-export default listenUnloadInjectable;
diff --git a/packages/core/src/renderer/bootstrap.tsx b/packages/core/src/renderer/bootstrap.tsx
index 75439fce13..a811f07d5c 100644
--- a/packages/core/src/renderer/bootstrap.tsx
+++ b/packages/core/src/renderer/bootstrap.tsx
@@ -6,7 +6,7 @@
import "./components/app.scss";
import React from "react";
-import { render } from "react-dom";
+import { render, unmountComponentAtNode } from "react-dom";
import { DefaultProps } from "./mui-base-theme";
import { DiContextProvider } from "@ogre-tools/injectable-react";
import type {
@@ -43,7 +43,7 @@ export async function bootstrap(di: DiContainerForInjection) {
}
try {
- await initializeApp();
+ await initializeApp(() => unmountComponentAtNode(rootElem));
} catch (error) {
console.error(`[BOOTSTRAP]: view initialization error: ${error}`, {
origin: location.href,
diff --git a/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts b/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts
index 9e901a8060..9bd0a26a3c 100644
--- a/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts
+++ b/packages/core/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts
@@ -22,51 +22,62 @@ interface Dependencies {
const logPrefix = "[CLUSTER-FRAME]:";
-export const initClusterFrame =
- ({
- hostedCluster,
- loadExtensions,
- catalogEntityRegistry,
- frameRoutingId,
- emitAppEvent,
- logger,
- showErrorNotification,
- }: Dependencies) =>
- async () => {
+export const initClusterFrame = ({
+ hostedCluster,
+ loadExtensions,
+ catalogEntityRegistry,
+ frameRoutingId,
+ emitAppEvent,
+ logger,
+ showErrorNotification,
+}: Dependencies) =>
+ async (unmountRoot: () => void) => {
// TODO: Make catalogEntityRegistry already initialized when passed as dependency
- catalogEntityRegistry.init();
+ catalogEntityRegistry.init();
- logger.info(
- `${logPrefix} Init dashboard, clusterId=${hostedCluster.id}, frameId=${frameRoutingId}`,
- );
+ logger.info(
+ `${logPrefix} Init dashboard, clusterId=${hostedCluster.id}, frameId=${frameRoutingId}`,
+ );
- await requestSetClusterFrameId(hostedCluster.id);
- await when(() => hostedCluster.ready.get()); // cluster.activate() is done at this point
+ await requestSetClusterFrameId(hostedCluster.id);
+ await when(() => hostedCluster.ready.get()); // cluster.activate() is done at this point
- catalogEntityRegistry.activeEntity = hostedCluster.id;
+ catalogEntityRegistry.activeEntity = hostedCluster.id;
- // Only load the extensions once the catalog has been populated.
- // Note that the Catalog might still have unprocessed entities until the extensions are fully loaded.
- when(
- () => catalogEntityRegistry.items.get().length > 0,
- () => loadExtensions(),
- {
- timeout: 15_000,
- onError: (error) => {
- logger.warn("[CLUSTER-FRAME]: error from activeEntity when()", error);
+ // Only load the extensions once the catalog has been populated.
+ // Note that the Catalog might still have unprocessed entities until the extensions are fully loaded.
+ when(
+ () => catalogEntityRegistry.items.get().length > 0,
+ () =>
+ loadExtensions(),
+ {
+ timeout: 15_000,
+ onError: (error) => {
+ logger.warn(
+ "[CLUSTER-FRAME]: error from activeEntity when()",
+ error,
+ );
- showErrorNotification("Failed to get KubernetesCluster for this view. Extensions will not be loaded.");
- },
+ showErrorNotification("Failed to get KubernetesCluster for this view. Extensions will not be loaded.");
},
+ },
+ );
+
+ setTimeout(() => {
+ emitAppEvent({
+ name: "cluster",
+ action: "open",
+ params: {
+ clusterId: hostedCluster.id,
+ },
+ });
+ });
+
+ window.onbeforeunload = () => {
+ logger.info(
+ `${logPrefix} Unload dashboard, clusterId=${(hostedCluster.id)}, frameId=${frameRoutingId}`,
);
- setTimeout(() => {
- emitAppEvent({
- name: "cluster",
- action: "open",
- params: {
- clusterId: hostedCluster.id,
- },
- });
- });
+ unmountRoot();
};
+ };
diff --git a/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts b/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts
index e1bedb0c88..f1e3024d80 100644
--- a/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts
+++ b/packages/core/src/renderer/frames/root-frame/init-root-frame.injectable.ts
@@ -9,6 +9,7 @@ import lensProtocolRouterRendererInjectable from "../../protocol-handler/lens-pr
import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable";
import registerIpcListenersInjectable from "../../ipc/register-ipc-listeners.injectable";
import loadExtensionsInjectable from "../load-extensions.injectable";
+import loggerInjectable from "../../../common/logger.injectable";
import { delay } from "@k8slens/utilities";
import { broadcastMessage } from "../../../common/ipc";
import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling";
@@ -22,8 +23,9 @@ const initRootFrameInjectable = getInjectable({
const bindProtocolAddRouteHandlers = di.inject(bindProtocolAddRouteHandlersInjectable);
const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable);
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
+ const logger = di.inject(loggerInjectable);
- return async () => {
+ return async (unmountRoot: () => void) => {
catalogEntityRegistry.init();
try {
@@ -54,6 +56,12 @@ const initRootFrameInjectable = getInjectable({
window.addEventListener("online", () => broadcastMessage("network:online"));
registerIpcListeners();
+
+ window.addEventListener("beforeunload", () => {
+ logger.info("[ROOT-FRAME]: Unload app");
+
+ unmountRoot();
+ });
};
},
});
diff --git a/packages/core/src/renderer/logger/close-renderer-log-file-id.test.ts b/packages/core/src/renderer/logger/close-renderer-log-file-id.test.ts
deleted file mode 100644
index 1520844c30..0000000000
--- a/packages/core/src/renderer/logger/close-renderer-log-file-id.test.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import winstonLoggerInjectable from "../../common/winston-logger.injectable";
-import { getDiForUnitTesting } from "../getDiForUnitTesting";
-import closeRendererLogFileInjectable from "./close-renderer-log-file.injectable";
-import type { DiContainer } from "@ogre-tools/injectable";
-import type winston from "winston";
-import type { SendMessageToChannel } from "../../common/utils/channel/message-to-channel-injection-token";
-import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
-import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
-import ipcLogTransportInjectable from "./ipc-transport.injectable";
-import type IpcLogTransport from "./ipc-transport";
-
-describe("close renderer file logging", () => {
- let di: DiContainer;
- let sendIpcMock: SendMessageToChannel;
- let winstonMock: winston.Logger;
- let ipcTransportMock: IpcLogTransport;
-
- beforeEach(() => {
- di = getDiForUnitTesting();
- sendIpcMock = jest.fn();
- winstonMock = {
- remove: jest.fn(),
- } as any as winston.Logger;
- ipcTransportMock = { name: "ipc-renderer-transport" } as IpcLogTransport;
-
- di.override(winstonLoggerInjectable, () => winstonMock);
- di.override(sendMessageToChannelInjectionToken, () => sendIpcMock);
- di.override(rendererLogFileIdInjectable, () => "some-log-id");
- di.override(ipcLogTransportInjectable, () => ipcTransportMock);
- });
-
- it("sends the ipc close message with correct log id", () => {
- const closeLog = di.inject(closeRendererLogFileInjectable);
-
- closeLog();
-
- expect(sendIpcMock).toHaveBeenCalledWith(
- { id: "close-ipc-file-logger-channel" },
- "some-log-id",
- );
- });
-
- it("removes the transport to prevent further logging to closed file", () => {
- const closeLog = di.inject(closeRendererLogFileInjectable);
-
- closeLog();
-
- expect(winstonMock.remove).toHaveBeenCalledWith({
- name: "ipc-renderer-transport",
- });
- });
-});
diff --git a/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts b/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts
deleted file mode 100644
index 8015708d84..0000000000
--- a/packages/core/src/renderer/logger/close-renderer-log-file.injectable.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import winstonLoggerInjectable from "../../common/winston-logger.injectable";
-import { closeIpcFileLoggerChannel } from "../../common/logger/ipc-file-logger-channel";
-import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
-import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
-import ipcLogTransportInjectable from "./ipc-transport.injectable";
-
-const closeRendererLogFileInjectable = getInjectable({
- id: "close-renderer-log-file",
- instantiate: (di) => {
- const winstonLogger = di.inject(winstonLoggerInjectable);
- const ipcLogTransport = di.inject(ipcLogTransportInjectable);
- const messageToChannel = di.inject(sendMessageToChannelInjectionToken);
- const fileId = di.inject(rendererLogFileIdInjectable);
-
-
- return () => {
- messageToChannel(closeIpcFileLoggerChannel, fileId);
- winstonLogger.remove(ipcLogTransport);
- };
- },
-});
-
-export default closeRendererLogFileInjectable;
diff --git a/packages/core/src/renderer/logger/ipc-transport.injectable.ts b/packages/core/src/renderer/logger/ipc-transport.injectable.ts
deleted file mode 100644
index 45139cd917..0000000000
--- a/packages/core/src/renderer/logger/ipc-transport.injectable.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import { loggerTransportInjectionToken } from "../../common/logger/transports";
-import type winston from "winston";
-import { MESSAGE } from "triple-beam";
-
-import IpcLogTransport from "./ipc-transport";
-import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
-import type {
- IpcFileLogObject } from "../../common/logger/ipc-file-logger-channel";
-import {
- closeIpcFileLoggerChannel,
- ipcFileLoggerChannel,
-} from "../../common/logger/ipc-file-logger-channel";
-import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
-
-/**
- * Winston uses symbol property for the actual message.
- *
- * For that to get through IPC, use the internalMessage property instead
- */
-function serializeLogForIpc(
- fileId: string,
- entry: winston.LogEntry,
-): IpcFileLogObject {
- return {
- fileId,
- entry: {
- level: entry.level,
- message: entry.message,
- internalMessage: Object.getOwnPropertyDescriptor(entry, MESSAGE)?.value,
- },
- };
-}
-
-const ipcLogTransportInjectable = getInjectable({
- id: "renderer-file-logger-transport",
- instantiate: (di) => {
- const messageToChannel = di.inject(sendMessageToChannelInjectionToken);
- const fileId = di.inject(rendererLogFileIdInjectable);
-
- return new IpcLogTransport({
- sendIpcLogMessage: (entry) =>
- messageToChannel(
- ipcFileLoggerChannel,
- serializeLogForIpc(fileId, entry),
- ),
- closeIpcLogging: () =>
- messageToChannel(closeIpcFileLoggerChannel, fileId),
- handleExceptions: false,
- level: "info",
- });
- },
- injectionToken: loggerTransportInjectionToken,
-});
-
-export default ipcLogTransportInjectable;
diff --git a/packages/core/src/renderer/logger/ipc-transport.test.ts b/packages/core/src/renderer/logger/ipc-transport.test.ts
deleted file mode 100644
index 931df377c7..0000000000
--- a/packages/core/src/renderer/logger/ipc-transport.test.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import type { DiContainer } from "@ogre-tools/injectable";
-import type { SendMessageToChannel } from "../../common/utils/channel/message-to-channel-injection-token";
-import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token";
-import { getDiForUnitTesting } from "../getDiForUnitTesting";
-import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
-import ipcLogTransportInjectable from "./ipc-transport.injectable";
-import { MESSAGE } from "triple-beam";
-
-describe("renderer log transport through ipc", () => {
- let di: DiContainer;
- let sendIpcMock: SendMessageToChannel;
-
- beforeEach(() => {
- sendIpcMock = jest.fn();
- di = getDiForUnitTesting();
- di.override(sendMessageToChannelInjectionToken, () => sendIpcMock);
- di.override(rendererLogFileIdInjectable, () => "some-log-id");
- });
-
- it("send serialized ipc messages on log", () => {
- const logTransport = di.inject(ipcLogTransportInjectable);
-
- logTransport.log(
- {
- level: "info",
- message: "some log text",
- [MESSAGE]: "actual winston log text",
- },
- () => {},
- );
-
- expect(sendIpcMock).toHaveBeenCalledWith(
- { id: "ipc-file-logger-channel" },
- {
- entry: {
- level: "info",
- message: "some log text",
- internalMessage: "actual winston log text",
- },
- fileId: "some-log-id",
- },
- );
- });
-});
diff --git a/packages/core/src/renderer/logger/ipc-transport.ts b/packages/core/src/renderer/logger/ipc-transport.ts
deleted file mode 100644
index a1f6fa819e..0000000000
--- a/packages/core/src/renderer/logger/ipc-transport.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import type { LogEntry } from "winston";
-import type { TransportStreamOptions } from "winston-transport";
-import TransportStream from "winston-transport";
-
-interface IpcLogTransportOptions extends TransportStreamOptions {
- sendIpcLogMessage: (entry: LogEntry) => void;
- closeIpcLogging: () => void;
-}
-
-class IpcLogTransport extends TransportStream {
- sendIpcLogMessage: (entry: LogEntry) => void;
- closeIpcLogging: () => void;
- name = "ipc-renderer-transport";
-
- constructor(options: IpcLogTransportOptions) {
- const { sendIpcLogMessage, closeIpcLogging, ...winstonOptions } = options;
-
- super(winstonOptions);
-
- this.sendIpcLogMessage = sendIpcLogMessage;
- this.closeIpcLogging = closeIpcLogging;
- }
-
- log(logEntry: LogEntry, next: () => void) {
- setImmediate(() => {
- this.emit("logged", logEntry);
- });
- this.sendIpcLogMessage(logEntry);
- next();
- }
-
- close() {
- this.closeIpcLogging();
- }
-}
-
-export default IpcLogTransport;
diff --git a/packages/core/src/renderer/logger/renderer-log-file-id.injectable.ts b/packages/core/src/renderer/logger/renderer-log-file-id.injectable.ts
deleted file mode 100644
index 81f4196b08..0000000000
--- a/packages/core/src/renderer/logger/renderer-log-file-id.injectable.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import { getInjectable } from "@ogre-tools/injectable";
-import windowLocationInjectable from "../../common/k8s-api/window-location.injectable";
-import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable";
-import { getClusterIdFromHost } from "../../common/utils";
-
-const rendererLogFileIdInjectable = getInjectable({
- id: "renderer-log-file-id",
- instantiate: (di) => {
- let frameId: string;
- const currentlyInClusterFrame = di.inject(currentlyInClusterFrameInjectable);
-
- if (currentlyInClusterFrame) {
- const { host } = di.inject(windowLocationInjectable);
- const clusterId = getClusterIdFromHost(host);
-
- frameId = `cluster-${clusterId}`;
- } else {
- frameId = "main";
- }
-
- return `renderer-${frameId}`;
- },
-});
-
-export default rendererLogFileIdInjectable;
diff --git a/packages/core/src/renderer/logger/renderer-log-file-id.test.ts b/packages/core/src/renderer/logger/renderer-log-file-id.test.ts
deleted file mode 100644
index bd2abf7a63..0000000000
--- a/packages/core/src/renderer/logger/renderer-log-file-id.test.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-import windowLocationInjectable from "../../common/k8s-api/window-location.injectable";
-import { getDiForUnitTesting } from "../getDiForUnitTesting";
-import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable";
-import rendererLogFileIdInjectable from "./renderer-log-file-id.injectable";
-
-describe("renderer log file id", () => {
-
- it("clearly names log for renderer main frame", () => {
- const di = getDiForUnitTesting();
-
- di.override(currentlyInClusterFrameInjectable, () => false);
-
- const mainFileId = di.inject(rendererLogFileIdInjectable);
-
- expect(mainFileId).toBe("renderer-main");
- });
-
- it("includes cluster id in renderer log file names", () => {
- const di = getDiForUnitTesting();
-
- di.override(currentlyInClusterFrameInjectable, () => true);
- di.override(windowLocationInjectable, () => ({
- host: "some-cluster.lens.app",
- port: "irrelevant",
- }));
- const clusterFileId = di.inject(rendererLogFileIdInjectable);
-
- expect(clusterFileId).toBe("renderer-cluster-some-cluster");
- });
-});
From 8a80607d8516736128dd22bde83d8e3351cb00db Mon Sep 17 00:00:00 2001
From: Sebastian Malton
Date: Tue, 21 Mar 2023 22:00:12 -0400
Subject: [PATCH 5/9] Add behavioural tests for Cluster Menu K8s Resources in
Sidebar menu not being shown (#7280)
* Add behavioural tests to cover bug fix
Signed-off-by: Sebastian Malton
* Remove previous fix to fix last test
Signed-off-by: Sebastian Malton
* More consistent impl of flushPromises
Signed-off-by: Sebastian Malton
* Fixup tests
Signed-off-by: Sebastian Malton
* Remove ContextHandler test (dead code)
Signed-off-by: Sebastian Malton
* Fix PrometheusHandler describe text
Signed-off-by: Sebastian Malton
* Fix type errors
Signed-off-by: Sebastian Malton
* Add useful case test-utils helper
Signed-off-by: Sebastian Malton
* Rename file to match token
Signed-off-by: Sebastian Malton
* Cleanup tests to fix type errors and use tables
Signed-off-by: Sebastian Malton
---------
Signed-off-by: Sebastian Malton
---
.../authorization-review.injectable.ts | 50 --
.../create-authorization-api.injectable.ts | 16 +
.../common/cluster/create-can-i.injectable.ts | 42 ++
.../cluster/create-core-api.injectable.ts | 16 +
...t-namespace-list-permissions.injectable.ts | 57 ++
.../cluster/list-namespaces.injectable.ts | 20 +-
...t-namespace-list-permissions.injectable.ts | 72 --
...request-namespace-list-permissions.test.ts | 501 ++++++--------
.../fs/copy.global-override-for-injectable.ts | 11 -
.../lstat.global-override-for-injectable.ts | 11 -
...irectory.global-override-for-injectable.ts | 11 -
.../remove.global-override-for-injectable.ts | 11 -
...ite-file.global-override-for-injectable.ts | 11 -
.../refresh-accessibility-technical.test.ts | 637 ++++++++++++++++++
.../core/src/main/__test__/cluster.test.ts | 12 +-
.../src/main/__test__/context-handler.test.ts | 203 ------
.../src/main/__test__/kube-auth-proxy.test.ts | 4 +-
.../main/__test__/prometheus-handler.test.ts | 2 +-
...-versions.ts => api-versions-requester.ts} | 7 +-
.../cluster/cluster-connection.injectable.ts | 34 +-
.../kube-auth-proxy-server.injectable.ts | 2 +-
.../request-api-resources.injectable.ts | 8 +-
.../request-core-api-versions.injectable.ts | 39 +-
...quest-kube-api-resources-for.injectable.ts | 2 +-
...equest-non-core-api-versions.injectable.ts | 45 +-
.../request-non-core-api-versions.test.ts | 8 +-
.../create-kube-auth-proxy.injectable.ts | 11 +-
.../main/kube-auth-proxy/kube-auth-proxy.ts | 3 +-
.../kubeconfig-manager/kubeconfig-manager.ts | 2 +-
packages/core/src/test-utils/cast.ts | 6 +
.../core/src/test-utils/mock-interface.ts | 17 +
.../test-utils/src/flush-promises.ts | 7 +-
32 files changed, 1104 insertions(+), 774 deletions(-)
delete mode 100644 packages/core/src/common/cluster/authorization-review.injectable.ts
create mode 100644 packages/core/src/common/cluster/create-authorization-api.injectable.ts
create mode 100644 packages/core/src/common/cluster/create-can-i.injectable.ts
create mode 100644 packages/core/src/common/cluster/create-core-api.injectable.ts
create mode 100644 packages/core/src/common/cluster/create-request-namespace-list-permissions.injectable.ts
delete mode 100644 packages/core/src/common/cluster/request-namespace-list-permissions.injectable.ts
delete mode 100644 packages/core/src/common/fs/copy.global-override-for-injectable.ts
delete mode 100644 packages/core/src/common/fs/lstat.global-override-for-injectable.ts
delete mode 100644 packages/core/src/common/fs/read-directory.global-override-for-injectable.ts
delete mode 100644 packages/core/src/common/fs/remove.global-override-for-injectable.ts
delete mode 100644 packages/core/src/common/fs/write-file.global-override-for-injectable.ts
create mode 100644 packages/core/src/features/cluster/refresh-accessibility-technical.test.ts
delete mode 100644 packages/core/src/main/__test__/context-handler.test.ts
rename packages/core/src/main/cluster/{request-api-versions.ts => api-versions-requester.ts} (64%)
create mode 100644 packages/core/src/test-utils/cast.ts
create mode 100644 packages/core/src/test-utils/mock-interface.ts
diff --git a/packages/core/src/common/cluster/authorization-review.injectable.ts b/packages/core/src/common/cluster/authorization-review.injectable.ts
deleted file mode 100644
index 3352423377..0000000000
--- a/packages/core/src/common/cluster/authorization-review.injectable.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type { KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node";
-import { AuthorizationV1Api } from "@kubernetes/client-node";
-import { getInjectable } from "@ogre-tools/injectable";
-import loggerInjectable from "../logger.injectable";
-
-/**
- * Requests the permissions for actions on the kube cluster
- * @param resourceAttributes The descriptor of the action that is desired to be known if it is allowed
- * @returns `true` if the actions described are allowed
- */
-export type CanI = (resourceAttributes: V1ResourceAttributes) => Promise;
-
-/**
- * @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
- */
-export type CreateAuthorizationReview = (proxyConfig: KubeConfig) => CanI;
-
-const createAuthorizationReviewInjectable = getInjectable({
- id: "authorization-review",
- instantiate: (di): CreateAuthorizationReview => {
- const logger = di.inject(loggerInjectable);
-
- return (proxyConfig) => {
- const api = proxyConfig.makeApiClient(AuthorizationV1Api);
-
- return async (resourceAttributes: V1ResourceAttributes): Promise => {
- try {
- const { body } = await api.createSelfSubjectAccessReview({
- apiVersion: "authorization.k8s.io/v1",
- kind: "SelfSubjectAccessReview",
- spec: { resourceAttributes },
- });
-
- return body.status?.allowed ?? false;
- } catch (error) {
- logger.error(`[AUTHORIZATION-REVIEW]: failed to create access review: ${error}`, { resourceAttributes });
-
- return false;
- }
- };
- };
- },
-});
-
-export default createAuthorizationReviewInjectable;
diff --git a/packages/core/src/common/cluster/create-authorization-api.injectable.ts b/packages/core/src/common/cluster/create-authorization-api.injectable.ts
new file mode 100644
index 0000000000..c0658a4a34
--- /dev/null
+++ b/packages/core/src/common/cluster/create-authorization-api.injectable.ts
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { AuthorizationV1Api } from "@kubernetes/client-node";
+import type { KubeConfig } from "@kubernetes/client-node";
+import { getInjectable } from "@ogre-tools/injectable";
+
+export type CreateAuthorizationApi = (config: KubeConfig) => AuthorizationV1Api;
+
+const createAuthorizationApiInjectable = getInjectable({
+ id: "create-authorization-api",
+ instantiate: (): CreateAuthorizationApi => (config) => config.makeApiClient(AuthorizationV1Api),
+});
+
+export default createAuthorizationApiInjectable;
diff --git a/packages/core/src/common/cluster/create-can-i.injectable.ts b/packages/core/src/common/cluster/create-can-i.injectable.ts
new file mode 100644
index 0000000000..59d30dbe77
--- /dev/null
+++ b/packages/core/src/common/cluster/create-can-i.injectable.ts
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { AuthorizationV1Api, V1ResourceAttributes } from "@kubernetes/client-node";
+import { getInjectable } from "@ogre-tools/injectable";
+import loggerInjectable from "../logger.injectable";
+
+/**
+ * Requests the permissions for actions on the kube cluster
+ * @param resourceAttributes The descriptor of the action that is desired to be known if it is allowed
+ * @returns `true` if the actions described are allowed
+ */
+export type CanI = (resourceAttributes: V1ResourceAttributes) => Promise;
+
+export type CreateCanI = (api: AuthorizationV1Api) => CanI;
+
+const createCanIInjectable = getInjectable({
+ id: "create-can-i",
+ instantiate: (di): CreateCanI => {
+ const logger = di.inject(loggerInjectable);
+
+ return (api) => async (resourceAttributes: V1ResourceAttributes): Promise => {
+ try {
+ const { body } = await api.createSelfSubjectAccessReview({
+ apiVersion: "authorization.k8s.io/v1",
+ kind: "SelfSubjectAccessReview",
+ spec: { resourceAttributes },
+ });
+
+ return body.status?.allowed ?? false;
+ } catch (error) {
+ logger.error(`[AUTHORIZATION-REVIEW]: failed to create access review: ${error}`, { resourceAttributes });
+
+ return false;
+ }
+ };
+ },
+});
+
+export default createCanIInjectable;
diff --git a/packages/core/src/common/cluster/create-core-api.injectable.ts b/packages/core/src/common/cluster/create-core-api.injectable.ts
new file mode 100644
index 0000000000..2389b12478
--- /dev/null
+++ b/packages/core/src/common/cluster/create-core-api.injectable.ts
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import type { KubeConfig } from "@kubernetes/client-node";
+import { CoreV1Api } from "@kubernetes/client-node";
+import { getInjectable } from "@ogre-tools/injectable";
+
+export type CreateCoreApi = (config: KubeConfig) => CoreV1Api;
+
+const createCoreApiInjectable = getInjectable({
+ id: "create-core-api",
+ instantiate: (): CreateCoreApi => config => config.makeApiClient(CoreV1Api),
+});
+
+export default createCoreApiInjectable;
diff --git a/packages/core/src/common/cluster/create-request-namespace-list-permissions.injectable.ts b/packages/core/src/common/cluster/create-request-namespace-list-permissions.injectable.ts
new file mode 100644
index 0000000000..b3e8875dc1
--- /dev/null
+++ b/packages/core/src/common/cluster/create-request-namespace-list-permissions.injectable.ts
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { AuthorizationV1Api } from "@kubernetes/client-node";
+import { getInjectable } from "@ogre-tools/injectable";
+import loggerInjectable from "../logger.injectable";
+import type { KubeApiResource } from "../rbac";
+
+export type CanListResource = (resource: KubeApiResource) => boolean;
+
+/**
+ * Requests the permissions for actions on the kube cluster
+ * @param namespace The namespace of the resources
+ */
+export type RequestNamespaceListPermissions = (namespace: string) => Promise;
+
+export type CreateRequestNamespaceListPermissions = (api: AuthorizationV1Api) => RequestNamespaceListPermissions;
+
+const createRequestNamespaceListPermissionsInjectable = getInjectable({
+ id: "create-request-namespace-list-permissions",
+ instantiate: (di): CreateRequestNamespaceListPermissions => {
+ const logger = di.inject(loggerInjectable);
+
+ return (api) => async (namespace) => {
+ try {
+ const { body: { status }} = await api.createSelfSubjectRulesReview({
+ apiVersion: "authorization.k8s.io/v1",
+ kind: "SelfSubjectRulesReview",
+ spec: { namespace },
+ });
+
+ if (!status || status.incomplete) {
+ logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError}`);
+
+ return () => true;
+ }
+
+ const { resourceRules } = status;
+
+ return (resource) => (
+ resourceRules
+ .filter(({ apiGroups = ["*"] }) => apiGroups.includes("*") || apiGroups.includes(resource.group))
+ .filter(({ resources = ["*"] }) => resources.includes("*") || resources.includes(resource.apiName))
+ .some(({ verbs }) => verbs.includes("*") || verbs.includes("list"))
+ );
+ } catch (error) {
+ logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review`, { namespace, error });
+
+ return () => true;
+ }
+ };
+ },
+});
+
+export default createRequestNamespaceListPermissionsInjectable;
diff --git a/packages/core/src/common/cluster/list-namespaces.injectable.ts b/packages/core/src/common/cluster/list-namespaces.injectable.ts
index 363a10abb1..04acb0671b 100644
--- a/packages/core/src/common/cluster/list-namespaces.injectable.ts
+++ b/packages/core/src/common/cluster/list-namespaces.injectable.ts
@@ -2,27 +2,21 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { KubeConfig } from "@kubernetes/client-node";
-import { CoreV1Api } from "@kubernetes/client-node";
+import type { CoreV1Api } from "@kubernetes/client-node";
import { getInjectable } from "@ogre-tools/injectable";
import { isDefined } from "@k8slens/utilities";
export type ListNamespaces = () => Promise;
-
-export type CreateListNamespaces = (config: KubeConfig) => ListNamespaces;
+export type CreateListNamespaces = (api: CoreV1Api) => ListNamespaces;
const createListNamespacesInjectable = getInjectable({
id: "create-list-namespaces",
- instantiate: (): CreateListNamespaces => (config) => {
- const coreApi = config.makeApiClient(CoreV1Api);
+ instantiate: (): CreateListNamespaces => (api) => async () => {
+ const { body: { items }} = await api.listNamespace();
- return async () => {
- const { body: { items }} = await coreApi.listNamespace();
-
- return items
- .map(ns => ns.metadata?.name)
- .filter(isDefined);
- };
+ return items
+ .map(ns => ns.metadata?.name)
+ .filter(isDefined);
},
});
diff --git a/packages/core/src/common/cluster/request-namespace-list-permissions.injectable.ts b/packages/core/src/common/cluster/request-namespace-list-permissions.injectable.ts
deleted file mode 100644
index 4b1aadeee6..0000000000
--- a/packages/core/src/common/cluster/request-namespace-list-permissions.injectable.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import type { KubeConfig } from "@kubernetes/client-node";
-import { AuthorizationV1Api } from "@kubernetes/client-node";
-import { getInjectable } from "@ogre-tools/injectable";
-import loggerInjectable from "../logger.injectable";
-import type { KubeApiResource } from "../rbac";
-
-export type CanListResource = (resource: KubeApiResource) => boolean;
-
-/**
- * Requests the permissions for actions on the kube cluster
- * @param namespace The namespace of the resources
- */
-export type RequestNamespaceListPermissions = (namespace: string) => Promise;
-
-/**
- * @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
- */
-export type RequestNamespaceListPermissionsFor = (proxyConfig: KubeConfig) => RequestNamespaceListPermissions;
-
-const requestNamespaceListPermissionsForInjectable = getInjectable({
- id: "request-namespace-list-permissions-for",
- instantiate: (di): RequestNamespaceListPermissionsFor => {
- const logger = di.inject(loggerInjectable);
-
- return (proxyConfig) => {
- const api = proxyConfig.makeApiClient(AuthorizationV1Api);
-
- return async (namespace) => {
- try {
- const { body: { status }} = await api.createSelfSubjectRulesReview({
- apiVersion: "authorization.k8s.io/v1",
- kind: "SelfSubjectRulesReview",
- spec: { namespace },
- });
-
- if (!status || status.incomplete) {
- logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError}`);
-
- return () => true;
- }
-
- const { resourceRules } = status;
-
- return (resource) => {
- const rules = resourceRules.filter(({
- apiGroups = ["*"],
- resources = ["*"],
- }) => {
- const isAboutRelevantApiGroup = apiGroups.includes("*") || apiGroups.includes(resource.group);
- const isAboutResource = resources.includes("*") || resources.includes(resource.apiName);
-
- return isAboutRelevantApiGroup && isAboutResource;
- });
-
- return rules.some(({ verbs }) => verbs.includes("*") || verbs.includes("list"));
- };
- } catch (error) {
- logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review`, { namespace, error });
-
- return () => true;
- }
- };
- };
- },
-});
-
-export default requestNamespaceListPermissionsForInjectable;
diff --git a/packages/core/src/common/cluster/request-namespace-list-permissions.test.ts b/packages/core/src/common/cluster/request-namespace-list-permissions.test.ts
index c62f69ca8e..194b5ce7e5 100644
--- a/packages/core/src/common/cluster/request-namespace-list-permissions.test.ts
+++ b/packages/core/src/common/cluster/request-namespace-list-permissions.test.ts
@@ -3,334 +3,225 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { V1SubjectRulesReviewStatus } from "@kubernetes/client-node";
+import type { AsyncFnMock } from "@async-fn/jest";
+import asyncFn from "@async-fn/jest";
+import type { AuthorizationV1Api, V1SubjectRulesReviewStatus } from "@kubernetes/client-node";
import type { DiContainer } from "@ogre-tools/injectable";
+import type { IncomingMessage } from "http";
+import { anyObject } from "jest-mock-extended";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
-import type { RequestNamespaceListPermissionsFor } from "./request-namespace-list-permissions.injectable";
-import requestNamespaceListPermissionsForInjectable from "./request-namespace-list-permissions.injectable";
+import { cast } from "../../test-utils/cast";
+import type { KubeApiResource } from "../rbac";
+import type { RequestNamespaceListPermissions } from "./create-request-namespace-list-permissions.injectable";
+import createRequestNamespaceListPermissionsInjectable from "./create-request-namespace-list-permissions.injectable";
-const createStubProxyConfig = (statusResponse: Promise<{ body: { status: V1SubjectRulesReviewStatus }}>) => ({
- makeApiClient: () => ({
- createSelfSubjectRulesReview: (): Promise<{ body: { status: V1SubjectRulesReviewStatus }}> => statusResponse,
- }),
-});
+interface TestCase {
+ description: string;
+ status: V1SubjectRulesReviewStatus;
+ expected: boolean;
+}
describe("requestNamespaceListPermissions", () => {
let di: DiContainer;
- let requestNamespaceListPermissions: RequestNamespaceListPermissionsFor;
+ let createSelfSubjectRulesReviewMock: AsyncFnMock;
+ let requestNamespaceListPermissions: RequestNamespaceListPermissions;
beforeEach(() => {
di = getDiForUnitTesting();
- requestNamespaceListPermissions = di.inject(requestNamespaceListPermissionsForInjectable);
+
+ const createRequestNamespaceListPermissions = di.inject(createRequestNamespaceListPermissionsInjectable);
+
+ createSelfSubjectRulesReviewMock = asyncFn();
+
+ requestNamespaceListPermissions = createRequestNamespaceListPermissions(cast({
+ createSelfSubjectRulesReview: createSelfSubjectRulesReviewMock,
+ }));
});
- describe("when api returns incomplete data", () => {
- it("returns truthy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: true,
- resourceRules: [],
- nonResourceRules: [],
- },
- },
- })),
- ) as any);
+ describe("when a request for list permissions in a namespace has been started", () => {
+ let request: ReturnType;
- const permissionCheck = await requestPermissions("irrelevant-namespace");
-
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeTruthy();
+ beforeEach(() => {
+ request = requestNamespaceListPermissions("irrelevant-namespace");
});
- });
- describe("when api rejects", () => {
- it("returns truthy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve, reject) => reject("unknown error")),
- ) as any);
-
- const permissionCheck = await requestPermissions("irrelevant-namespace");
-
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeTruthy();
+ it("should request the creation of a SelfSubjectRulesReview", () => {
+ expect(createSelfSubjectRulesReviewMock).toBeCalledWith(anyObject({
+ spec: {
+ namespace: "irrelevant-namespace",
+ },
+ }));
});
- });
- describe("when first resourceRule has all permissions for everything", () => {
- it("return truthy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: false,
- resourceRules: [
- {
- apiGroups: ["*"],
- verbs: ["*"],
- },
- {
- apiGroups: ["*"],
- verbs: ["get"],
- },
- ],
- nonResourceRules: [],
+ ([
+ {
+ description: "incomplete data",
+ status: {
+ incomplete: true,
+ resourceRules: [],
+ nonResourceRules: [],
+ },
+ expected: true,
+ },
+ {
+ description: "first resourceRule has all permissions for everything",
+ status: {
+ incomplete: false,
+ resourceRules: [
+ {
+ apiGroups: ["*"],
+ verbs: ["*"],
},
- },
- })),
- ) as any);
+ {
+ apiGroups: ["*"],
+ verbs: ["get"],
+ },
+ ],
+ nonResourceRules: [],
+ },
+ expected: true,
+ },
+ {
+ description: "first resourceRule has list permissions for everything",
+ status: {
+ incomplete: false,
+ resourceRules: [
+ {
+ apiGroups: ["*"],
+ verbs: ["list"],
+ },
+ {
+ apiGroups: ["*"],
+ verbs: ["get"],
+ },
+ ],
+ nonResourceRules: [],
+ },
+ expected: true,
+ },
+ {
+ description: "first resourceRule has list permissions for asked resource",
+ status: {
+ incomplete: false,
+ resourceRules: [
+ {
+ apiGroups: ["some-api-group"],
+ resources: ["some-kind"],
+ verbs: ["list"],
+ },
+ {
+ apiGroups: ["*"],
+ verbs: ["get"],
+ },
+ ],
+ nonResourceRules: [],
+ },
+ expected: true,
+ },
+ {
+ description: "last resourceRule has all permissions for everything",
+ status: {
+ incomplete: false,
+ resourceRules: [
+ {
+ apiGroups: ["*"],
+ verbs: ["get"],
+ },
+ {
+ apiGroups: ["*"],
+ verbs: ["*"],
+ },
+ ],
+ nonResourceRules: [],
+ },
+ expected: true,
+ },
+ {
+ description: "last resourceRule has list permissions for asked resource",
+ status: {
+ incomplete: false,
+ resourceRules: [
+ {
+ apiGroups: ["*"],
+ verbs: ["get"],
+ },
+ {
+ apiGroups: ["some-api-group"],
+ resources: ["some-kind"],
+ verbs: ["list"],
+ },
+ ],
+ nonResourceRules: [],
+ },
+ expected: true,
+ },
+ {
+ description: "resourceRules has matching resource without list verb",
+ status: {
+ incomplete: false,
+ resourceRules: [
+ {
+ apiGroups: ["some-api-group"],
+ resources: ["some-kind"],
+ verbs: ["get"],
+ },
+ ],
+ nonResourceRules: [],
+ },
+ expected: false,
+ },
+ {
+ description: "resourceRules has no matching resource with list verb",
+ status: {
+ incomplete: false,
+ resourceRules: [
+ {
+ apiGroups: [""],
+ resources: ["services"],
+ verbs: ["list"],
+ },
+ ],
+ nonResourceRules: [],
+ },
+ expected: false,
+ },
+ ] as TestCase[]).forEach(({ description, status, expected }) => {
+ describe(`when api returns ${description}`, () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve({
+ body: {
+ status,
+ spec: {},
+ },
+ response: null as unknown as IncomingMessage,
+ });
+ });
- const permissionCheck = await requestPermissions("irrelevant-namespace");
+ it(`allows the request to complete, and 'canListResource' will return ${expected}`, async () => {
+ const canListResource = await request;
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeTruthy();
+ expect(canListResource(someKubeResource)).toBe(expected);
+ });
+ });
});
- });
- describe("when first resourceRule has list permissions for everything", () => {
- it("return truthy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: false,
- resourceRules: [
- {
- apiGroups: ["*"],
- verbs: ["list"],
- },
- {
- apiGroups: ["*"],
- verbs: ["get"],
- },
- ],
- nonResourceRules: [],
- },
- },
- })),
- ) as any);
+ describe("when api rejects", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.reject(new Error("unknown error"));
+ });
- const permissionCheck = await requestPermissions("irrelevant-namespace");
+ it("allows the request to complete, and 'canListResource' will return true", async () => {
+ const canListResource = await request;
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeTruthy();
- });
- });
-
- describe("when first resourceRule has list permissions for asked resource", () => {
- it("return truthy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: false,
- resourceRules: [
- {
- apiGroups: [""],
- resources: ["pods"],
- verbs: ["list"],
- },
- {
- apiGroups: ["*"],
- verbs: ["get"],
- },
- ],
- nonResourceRules: [],
- },
- },
- })),
- ) as any);
-
- const permissionCheck = await requestPermissions("irrelevant-namespace");
-
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeTruthy();
- });
- });
-
- describe("when last resourceRule has all permissions for everything", () => {
- it("return truthy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: false,
- resourceRules: [
- {
- apiGroups: ["*"],
- verbs: ["get"],
- },
- {
- apiGroups: ["*"],
- verbs: ["*"],
- },
- ],
- nonResourceRules: [],
- },
- },
- })),
- ) as any);
-
- const permissionCheck = await requestPermissions("irrelevant-namespace");
-
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeTruthy();
- });
- });
-
- describe("when last resourceRule has list permissions for everything", () => {
- it("return truthy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: false,
- resourceRules: [
- {
- apiGroups: ["*"],
- verbs: ["get"],
- },
- {
- apiGroups: ["*"],
- verbs: ["list"],
- },
- ],
- nonResourceRules: [],
- },
- },
- })),
- ) as any);
-
- const permissionCheck = await requestPermissions("irrelevant-namespace");
-
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeTruthy();
- });
- });
-
- describe("when last resourceRule has list permissions for asked resource", () => {
- it("return truthy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: false,
- resourceRules: [
- {
- apiGroups: ["*"],
- verbs: ["get"],
- },
- {
- apiGroups: [""],
- resources: ["pods"],
- verbs: ["list"],
- },
- ],
- nonResourceRules: [],
- },
- },
- })),
- ) as any);
-
- const permissionCheck = await requestPermissions("irrelevant-namespace");
-
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeTruthy();
- });
- });
-
- describe("when resourceRules has matching resource without list verb", () => {
- it("return falsy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: false,
- resourceRules: [
- {
- apiGroups: [""],
- resources: ["pods"],
- verbs: ["get"],
- },
- ],
- nonResourceRules: [],
- },
- },
- })),
- ) as any);
-
- const permissionCheck = await requestPermissions("irrelevant-namespace");
-
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeFalsy();
- });
- });
-
- describe("when resourceRules has no matching resource with list verb", () => {
- it("return falsy function", async () => {
- const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
- new Promise((resolve) => resolve({
- body: {
- status: {
- incomplete: false,
- resourceRules: [
- {
- apiGroups: [""],
- resources: ["services"],
- verbs: ["list"],
- },
- ],
- nonResourceRules: [],
- },
- },
- })),
- ) as any);
-
- const permissionCheck = await requestPermissions("irrelevant-namespace");
-
- expect(permissionCheck({
- apiName: "pods",
- group: "",
- kind: "Pod",
- namespaced: true,
- })).toBeFalsy();
+ expect(canListResource(someKubeResource)).toBe(true);
+ });
});
});
});
+
+const someKubeResource: KubeApiResource = {
+ apiName: "some-kind",
+ group: "some-api-group",
+ kind: "SomeKind",
+ namespaced: true,
+};
diff --git a/packages/core/src/common/fs/copy.global-override-for-injectable.ts b/packages/core/src/common/fs/copy.global-override-for-injectable.ts
deleted file mode 100644
index 3799d3b760..0000000000
--- a/packages/core/src/common/fs/copy.global-override-for-injectable.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { getGlobalOverride } from "@k8slens/test-utils";
-import copyInjectable from "./copy.injectable";
-
-export default getGlobalOverride(copyInjectable, () => async () => {
- throw new Error("tried to copy filepaths without override");
-});
diff --git a/packages/core/src/common/fs/lstat.global-override-for-injectable.ts b/packages/core/src/common/fs/lstat.global-override-for-injectable.ts
deleted file mode 100644
index 155fac7451..0000000000
--- a/packages/core/src/common/fs/lstat.global-override-for-injectable.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { getGlobalOverride } from "@k8slens/test-utils";
-import lstatInjectable from "./lstat.injectable";
-
-export default getGlobalOverride(lstatInjectable, () => async () => {
- throw new Error("tried to lstat a filepath without override");
-});
diff --git a/packages/core/src/common/fs/read-directory.global-override-for-injectable.ts b/packages/core/src/common/fs/read-directory.global-override-for-injectable.ts
deleted file mode 100644
index 72d9b523f4..0000000000
--- a/packages/core/src/common/fs/read-directory.global-override-for-injectable.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { getGlobalOverride } from "@k8slens/test-utils";
-import readDirectoryInjectable from "./read-directory.injectable";
-
-export default getGlobalOverride(readDirectoryInjectable, () => async () => {
- throw new Error("tried to read a directory's content without override");
-});
diff --git a/packages/core/src/common/fs/remove.global-override-for-injectable.ts b/packages/core/src/common/fs/remove.global-override-for-injectable.ts
deleted file mode 100644
index 58fb0f9dce..0000000000
--- a/packages/core/src/common/fs/remove.global-override-for-injectable.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { getGlobalOverride } from "@k8slens/test-utils";
-import removePathInjectable from "./remove.injectable";
-
-export default getGlobalOverride(removePathInjectable, () => async () => {
- throw new Error("tried to remove path without override");
-});
diff --git a/packages/core/src/common/fs/write-file.global-override-for-injectable.ts b/packages/core/src/common/fs/write-file.global-override-for-injectable.ts
deleted file mode 100644
index e87f648305..0000000000
--- a/packages/core/src/common/fs/write-file.global-override-for-injectable.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { getGlobalOverride } from "@k8slens/test-utils";
-import writeFileInjectable from "./write-file.injectable";
-
-export default getGlobalOverride(writeFileInjectable, () => async () => {
- throw new Error("tried to write file without override");
-});
diff --git a/packages/core/src/features/cluster/refresh-accessibility-technical.test.ts b/packages/core/src/features/cluster/refresh-accessibility-technical.test.ts
new file mode 100644
index 0000000000..73ff6151b9
--- /dev/null
+++ b/packages/core/src/features/cluster/refresh-accessibility-technical.test.ts
@@ -0,0 +1,637 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { AsyncFnMock } from "@async-fn/jest";
+import asyncFn from "@async-fn/jest";
+import type { AuthorizationV1Api, CoreV1Api, V1APIGroupList, V1APIVersions, V1NamespaceList, V1SelfSubjectAccessReview, V1SelfSubjectRulesReview } from "@kubernetes/client-node";
+import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable";
+import type { Cluster } from "../../common/cluster/cluster";
+import createAuthorizationApiInjectable from "../../common/cluster/create-authorization-api.injectable";
+import writeJsonFileInjectable from "../../common/fs/write-json-file.injectable";
+import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
+import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
+import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
+import type { PartialDeep } from "type-fest";
+import { anyObject } from "jest-mock-extended";
+import createCoreApiInjectable from "../../common/cluster/create-core-api.injectable";
+import type { K8sRequest } from "../../main/k8s-request.injectable";
+import k8sRequestInjectable from "../../main/k8s-request.injectable";
+import type { DetectClusterMetadata } from "../../main/cluster-detectors/detect-cluster-metadata.injectable";
+import detectClusterMetadataInjectable from "../../main/cluster-detectors/detect-cluster-metadata.injectable";
+import type { ClusterConnection } from "../../main/cluster/cluster-connection.injectable";
+import clusterConnectionInjectable from "../../main/cluster/cluster-connection.injectable";
+import type { KubeAuthProxy } from "../../main/kube-auth-proxy/create-kube-auth-proxy.injectable";
+import createKubeAuthProxyInjectable from "../../main/kube-auth-proxy/create-kube-auth-proxy.injectable";
+import type { Mocked } from "../../test-utils/mock-interface";
+import { flushPromises } from "@k8slens/test-utils";
+
+describe("Refresh Cluster Accessibility Technical Tests", () => {
+ let builder: ApplicationBuilder;
+ let createSelfSubjectRulesReviewMock: AsyncFnMock;
+ let createSelfSubjectAccessReviewMock: AsyncFnMock;
+ let listNamespaceMock: AsyncFnMock;
+ let k8sRequestMock: AsyncFnMock;
+ let detectClusterMetadataMock: AsyncFnMock;
+ let kubeAuthProxyMock: Mocked;
+
+ beforeEach(async () => {
+ builder = getApplicationBuilder();
+
+ const mainDi = builder.mainDi;
+
+ mainDi.override(broadcastMessageInjectable, () => async () => {});
+
+ kubeAuthProxyMock = {
+ apiPrefix: "/some-api-prefix",
+ port: 0,
+ exit: jest.fn(),
+ run: asyncFn(),
+ };
+ mainDi.override(createKubeAuthProxyInjectable, () => () => kubeAuthProxyMock);
+
+ detectClusterMetadataMock = asyncFn();
+ mainDi.override(detectClusterMetadataInjectable, () => detectClusterMetadataMock);
+
+ k8sRequestMock = asyncFn();
+ mainDi.override(k8sRequestInjectable, () => k8sRequestMock);
+
+ createSelfSubjectRulesReviewMock = asyncFn();
+ createSelfSubjectAccessReviewMock = asyncFn();
+ mainDi.override(createAuthorizationApiInjectable, () => () => ({
+ createSelfSubjectRulesReview: createSelfSubjectRulesReviewMock,
+ createSelfSubjectAccessReview: createSelfSubjectAccessReviewMock,
+ } as any));
+
+ listNamespaceMock = asyncFn();
+ mainDi.override(createCoreApiInjectable, () => () => ({
+ listNamespace: listNamespaceMock,
+ } as any));
+
+ await builder.render();
+ });
+
+ describe("given a cluster with no configured preferences", () => {
+ let cluster: Cluster;
+ let clusterConnection: ClusterConnection;
+ let refreshPromise: Promise;
+
+ beforeEach(async () => {
+ const mainDi = builder.mainDi;
+ const clusterStore = mainDi.inject(clusterStoreInjectable);
+ const writeJsonFile = mainDi.inject(writeJsonFileInjectable);
+
+ await writeJsonFile("/some-kube-config-path", {
+ apiVersion: "v1",
+ kind: "Config",
+ clusters: [{
+ name: "some-cluster-name",
+ cluster: {
+ server: "https://localhost:8989",
+ },
+ }],
+ users: [{
+ name: "some-user-name",
+ }],
+ contexts: [{
+ name: "some-cluster-context",
+ context: {
+ user: "some-user-name",
+ cluster: "some-cluster-name",
+ },
+ }],
+ });
+
+ clusterStore.addCluster({
+ contextName: "some-cluster-context",
+ id: "some-cluster-id",
+ kubeConfigPath: "/some-kube-config-path",
+ });
+
+ cluster = clusterStore.getById("some-cluster-id") ?? (() => { throw new Error("missing cluster"); })();
+ clusterConnection = mainDi.inject(clusterConnectionInjectable, cluster);
+ refreshPromise = clusterConnection.refreshAccessibilityAndMetadata();
+ });
+
+ it("starts kubeAuthProxy", () => {
+ expect(kubeAuthProxyMock.run).toBeCalled();
+ });
+
+ describe("when kubeAuthProxy has started running and its port is found", () => {
+ beforeEach(async () => {
+ kubeAuthProxyMock.port = 1235;
+ await kubeAuthProxyMock.run.resolve();
+ await flushPromises();
+ });
+
+ it("requests if cluster has admin permissions", async () => {
+ expect(createSelfSubjectAccessReviewMock).toBeCalledWith(anyObject({
+ spec: {
+ namespace: "kube-system",
+ resource: "*",
+ verb: "create",
+ },
+ }));
+ });
+
+ describe.each([ true, false ])("when cluster admin request resolves to %p", (isAdmin) => {
+ beforeEach(async () => {
+ await createSelfSubjectAccessReviewMock.resolve({
+ body: {
+ status: {
+ allowed: isAdmin,
+ },
+ } as PartialDeep,
+ } as any);
+ });
+
+ it("requests if cluster has global watch permissions", () => {
+ expect(createSelfSubjectAccessReviewMock).toBeCalledWith(anyObject({
+ spec: {
+ verb: "watch",
+ resource: "*",
+ },
+ }));
+ });
+
+ describe.each([ true, false ])("when cluster global watch request resolves with %p", (globalWatch) => {
+ beforeEach(async () => {
+ await createSelfSubjectAccessReviewMock.resolve({
+ body: {
+ status: {
+ allowed: globalWatch,
+ },
+ } as PartialDeep,
+ } as any);
+ });
+
+ it("requests namespaces", () => {
+ expect(listNamespaceMock).toBeCalled();
+ });
+
+ describe("when list namespaces resolves", () => {
+ beforeEach(async () => {
+ await listNamespaceMock.resolve(listNamespaceResponse);
+ });
+
+ it("requests core api versions", () => {
+ expect(k8sRequestMock).toBeCalledWith(
+ anyObject({ id: "some-cluster-id" }),
+ "/api",
+ );
+ });
+
+ describe("when core api versions request resolves", () => {
+ beforeEach(async () => {
+ await k8sRequestMock.resolve({
+ serverAddressByClientCIDRs: [],
+ versions: [
+ "v1",
+ ],
+ } as V1APIVersions);
+ });
+
+ it("requests non-core api resource kinds", () => {
+ expect(k8sRequestMock).toBeCalledWith(
+ anyObject({ id: "some-cluster-id" }),
+ "/apis",
+ );
+ });
+
+ describe("when non-core api resource kinds request resolves", () => {
+ beforeEach(async () => {
+ await k8sRequestMock.resolve(nonCoreApiResponse);
+ });
+
+ it("requests specific resource kinds in core", () => {
+ expect(k8sRequestMock).toBeCalledWith(
+ anyObject({ id: "some-cluster-id" }),
+ "/api/v1",
+ );
+ });
+
+ describe("when core specific resource kinds request resolves", () => {
+ beforeEach(async () => {
+ await k8sRequestMock.resolve(coreApiKindsResponse);
+ });
+
+ it("requests specific resources kinds from the first non-core response", () => {
+ expect(k8sRequestMock).toBeCalledWith(
+ anyObject({ id: "some-cluster-id" }),
+ "/apis/node.k8s.io/v1",
+ );
+ });
+
+ describe("when first specific resource kinds request resolves", () => {
+ beforeEach(async () => {
+ await k8sRequestMock.resolve(nodeK8sIoKindsResponse);
+ });
+
+ it("requests specific resources kinds from the second non-core response", () => {
+ expect(k8sRequestMock).toBeCalledWith(
+ anyObject({ id: "some-cluster-id" }),
+ "/apis/discovery.k8s.io/v1",
+ );
+ });
+
+ describe("when second specific resource kinds request resolves", () => {
+ beforeEach(async () => {
+ await k8sRequestMock.resolve(discoveryK8sIoKindsResponse);
+ });
+
+ it("requests namespace list permissions for 'default' namespace", () => {
+ expect(createSelfSubjectRulesReviewMock).toBeCalledWith(anyObject({
+ spec: {
+ namespace: "default",
+ },
+ }));
+ });
+
+ describe("when the permissions are incomplete", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve(defaultIncompletePermissions);
+ });
+
+ it("requests namespace list permissions for 'my-namespace' namespace", () => {
+ expect(createSelfSubjectRulesReviewMock).toBeCalledWith(anyObject({
+ spec: {
+ namespace: "my-namespace",
+ },
+ }));
+ });
+
+ describe("when the permissions request for 'my-namespace' resolves as empty", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve(emptyPermissions);
+ });
+
+ it("requests cluster metadata", () => {
+ expect(detectClusterMetadataMock).toBeCalledWith(anyObject({ id: "some-cluster-id" }));
+ });
+
+ describe("when cluster metadata request resolves", () => {
+ beforeEach(async () => {
+ await detectClusterMetadataMock.resolve({});
+ });
+
+ it("allows the call to refreshAccessibilityAndMetadata to resolve", async () => {
+ await refreshPromise;
+ });
+
+ it("should have the cluster displaying 'pods'", () => {
+ expect(cluster.resourcesToShow.has("pods")).toBe(true);
+ });
+
+ it("should have the cluster displaying 'namespaces'", () => {
+ expect(cluster.resourcesToShow.has("namespaces")).toBe(true);
+ });
+ });
+ });
+
+ describe.skip("when the permissions are incomplete", () => {});
+ describe.skip("when the permissions resolve to a single entry with 'list' verb", () => {});
+ describe.skip("when the permissions resolve to multiple entries with the 'list' verb not on the first entry", () => {});
+ });
+
+ describe("when the permissions resolve to an empty list", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve(emptyPermissions);
+ });
+
+ it("requests namespace list permissions for 'my-namespace' namespace", () => {
+ expect(createSelfSubjectRulesReviewMock).toBeCalledWith(anyObject({
+ spec: {
+ namespace: "my-namespace",
+ },
+ }));
+ });
+
+ describe("when the permissions request for 'my-namespace' resolves as empty", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve(emptyPermissions);
+ });
+
+ it("requests cluster metadata", () => {
+ expect(detectClusterMetadataMock).toBeCalledWith(anyObject({ id: "some-cluster-id" }));
+ });
+
+ describe("when cluster metadata request resolves", () => {
+ beforeEach(async () => {
+ await detectClusterMetadataMock.resolve({});
+ });
+
+ it("allows the call to refreshAccessibilityAndMetadata to resolve", async () => {
+ await refreshPromise;
+ });
+
+ it("should have the cluster displaying 'pods'", () => {
+ expect(cluster.resourcesToShow.has("pods")).toBe(false);
+ });
+
+ it("should have the cluster not displaying 'namespaces'", () => {
+ expect(cluster.resourcesToShow.has("namespaces")).toBe(false);
+ });
+ });
+ });
+
+ describe.skip("when the permissions are incomplete", () => {});
+ describe.skip("when the permissions resolve to a single entry with 'list' verb", () => {});
+ describe.skip("when the permissions resolve to multiple entries with the 'list' verb not on the first entry", () => {});
+ });
+
+ describe("when the permissions resolve to a single entry with 'list' verb", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve(defaultSingleListPermissions);
+ });
+
+ it("requests namespace list permissions for 'my-namespace' namespace", () => {
+ expect(createSelfSubjectRulesReviewMock).toBeCalledWith(anyObject({
+ spec: {
+ namespace: "my-namespace",
+ },
+ }));
+ });
+
+ describe("when the permissions request for 'my-namespace' resolves as empty", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve(emptyPermissions);
+ });
+
+ it("requests cluster metadata", () => {
+ expect(detectClusterMetadataMock).toBeCalledWith(anyObject({ id: "some-cluster-id" }));
+ });
+
+ describe("when cluster metadata request resolves", () => {
+ beforeEach(async () => {
+ await detectClusterMetadataMock.resolve({});
+ });
+
+ it("allows the call to refreshAccessibilityAndMetadata to resolve", async () => {
+ await refreshPromise;
+ });
+
+ it("should have the cluster displaying 'pods'", () => {
+ expect(cluster.resourcesToShow.has("pods")).toBe(true);
+ });
+
+ it("should have the cluster not displaying 'namespaces'", () => {
+ expect(cluster.resourcesToShow.has("namespaces")).toBe(false);
+ });
+ });
+ });
+
+ describe.skip("when the permissions are incomplete", () => {});
+ describe.skip("when the permissions resolve to a single entry with 'list' verb", () => {});
+ describe.skip("when the permissions resolve to multiple entries with the 'list' verb not on the first entry", () => {});
+ });
+
+ describe("when the permissions resolve to multiple entries with the 'list' verb not on the first entry", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve(defaultMultipleListPermissions);
+ });
+
+ it("requests namespace list permissions for 'my-namespace' namespace", () => {
+ expect(createSelfSubjectRulesReviewMock).toBeCalledWith(anyObject({
+ spec: {
+ namespace: "my-namespace",
+ },
+ }));
+ });
+
+ describe("when the permissions request for 'my-namespace' resolves as empty", () => {
+ beforeEach(async () => {
+ await createSelfSubjectRulesReviewMock.resolve(emptyPermissions);
+ });
+
+ it("requests cluster metadata", () => {
+ expect(detectClusterMetadataMock).toBeCalledWith(anyObject({ id: "some-cluster-id" }));
+ });
+
+ describe("when cluster metadata request resolves", () => {
+ beforeEach(async () => {
+ await detectClusterMetadataMock.resolve({});
+ });
+
+ it("allows the call to refreshAccessibilityAndMetadata to resolve", async () => {
+ await refreshPromise;
+ });
+
+ it("should have the cluster displaying 'pods'", () => {
+ expect(cluster.resourcesToShow.has("pods")).toBe(true);
+ });
+
+ it("should have the cluster not displaying 'namespaces'", () => {
+ expect(cluster.resourcesToShow.has("namespaces")).toBe(false);
+ });
+ });
+ });
+
+ describe.skip("when the permissions are incomplete", () => {});
+ describe.skip("when the permissions resolve to a single entry with 'list' verb", () => {});
+ describe.skip("when the permissions resolve to multiple entries with the 'list' verb not on the first entry", () => {});
+ });
+ });
+
+ describe.skip("when second specific resource kinds rejects", () => {});
+ });
+ });
+
+ describe.skip("when first specific resource kinds rejects", () => {});
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+const nonCoreApiResponse = {
+ groups: [
+ {
+ name: "node.k8s.io",
+ versions: [
+ {
+ groupVersion: "node.k8s.io/v1",
+ version: "v1",
+ },
+ ],
+ preferredVersion: {
+ groupVersion: "node.k8s.io/v1",
+ version: "v1",
+ },
+ },
+ {
+ name: "discovery.k8s.io",
+ versions: [
+ {
+ groupVersion: "discovery.k8s.io/v1",
+ version: "v1",
+ },
+ ],
+ preferredVersion: {
+ groupVersion: "discovery.k8s.io/v1",
+ version: "v1",
+ },
+ },
+ ],
+} as V1APIGroupList;
+
+const listNamespaceResponse = {
+ body: {
+ items: [
+ {
+ metadata: {
+ name: "default",
+ },
+ },
+ {
+ metadata: {
+ name: "my-namespace",
+ },
+ },
+ ],
+ } as PartialDeep,
+} as Awaited>;
+
+const coreApiKindsResponse = {
+ kind: "APIResourceList",
+ groupVersion: "v1",
+ resources: [
+ {
+ name: "namespaces",
+ singularName: "",
+ namespaced: false,
+ kind: "Namespace",
+ verbs: ["create", "delete", "get", "list", "patch", "update", "watch"],
+ shortNames: ["ns"],
+ storageVersionHash: "Q3oi5N2YM8M=",
+ },
+ {
+ name: "pods",
+ singularName: "",
+ namespaced: true,
+ kind: "Pod",
+ verbs: [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch",
+ ],
+ shortNames: ["po"],
+ categories: ["all"],
+ storageVersionHash: "xPOwRZ+Yhw8=",
+ },
+ {
+ name: "pods/attach",
+ singularName: "",
+ namespaced: true,
+ kind: "PodAttachOptions",
+ verbs: ["create", "get"],
+ },
+ ],
+};
+
+const nodeK8sIoKindsResponse = {
+ kind: "APIResourceList",
+ apiVersion: "v1",
+ groupVersion: "node.k8s.io/v1",
+ resources: [
+ {
+ name: "runtimeclasses",
+ singularName: "",
+ namespaced: false,
+ kind: "RuntimeClass",
+ verbs: [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch",
+ ],
+ storageVersionHash: "WQTu1GL3T2Q=",
+ },
+ ],
+};
+
+const discoveryK8sIoKindsResponse = {
+ kind: "APIResourceList",
+ apiVersion: "v1",
+ groupVersion: "discovery.k8s.io/v1",
+ resources: [
+ {
+ name: "endpointslices",
+ singularName: "",
+ namespaced: true,
+ kind: "EndpointSlice",
+ verbs: [
+ "create",
+ "delete",
+ "deletecollection",
+ "get",
+ "list",
+ "patch",
+ "update",
+ "watch",
+ ],
+ storageVersionHash: "Nx3SIv6I0mE=",
+ },
+ ],
+};
+
+type CreateSelfSubjectRulesReviewRes = Awaited>;
+
+const defaultIncompletePermissions = {
+ body: {
+ status: {
+ incomplete: true,
+ },
+ } as PartialDeep,
+} as CreateSelfSubjectRulesReviewRes;
+
+const emptyPermissions = {
+ body: {
+ status: {
+ resourceRules: [],
+ },
+ } as PartialDeep,
+} as CreateSelfSubjectRulesReviewRes;
+
+const defaultSingleListPermissions = {
+ body: {
+ status: {
+ resourceRules: [{
+ apiGroups: [""],
+ resources: ["pods"],
+ verbs: ["list"],
+ }],
+ },
+ } as PartialDeep,
+} as CreateSelfSubjectRulesReviewRes;
+
+const defaultMultipleListPermissions = {
+ body: {
+ status: {
+ resourceRules: [
+ {
+ apiGroups: [""],
+ resources: ["pods"],
+ verbs: ["get"],
+ },
+ {
+ apiGroups: [""],
+ resources: ["pods"],
+ verbs: ["list"],
+ },
+ ],
+ },
+ } as PartialDeep,
+} as CreateSelfSubjectRulesReviewRes;
diff --git a/packages/core/src/main/__test__/cluster.test.ts b/packages/core/src/main/__test__/cluster.test.ts
index bb495af847..205a23d5ab 100644
--- a/packages/core/src/main/__test__/cluster.test.ts
+++ b/packages/core/src/main/__test__/cluster.test.ts
@@ -5,10 +5,6 @@
import { Cluster } from "../../common/cluster/cluster";
import { Kubectl } from "../kubectl/kubectl";
import { getDiForUnitTesting } from "../getDiForUnitTesting";
-import createAuthorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
-import requestNamespaceListPermissionsForInjectable from "../../common/cluster/request-namespace-list-permissions.injectable";
-import createListNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
-import prometheusHandlerInjectable from "../cluster/prometheus-handler/prometheus-handler.injectable";
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable";
@@ -19,6 +15,10 @@ import clusterConnectionInjectable from "../cluster/cluster-connection.injectabl
import kubeconfigManagerInjectable from "../kubeconfig-manager/kubeconfig-manager.injectable";
import type { KubeconfigManager } from "../kubeconfig-manager/kubeconfig-manager";
import broadcastConnectionUpdateInjectable from "../cluster/broadcast-connection-update.injectable";
+import createCanIInjectable from "../../common/cluster/create-can-i.injectable";
+import createRequestNamespaceListPermissionsInjectable from "../../common/cluster/create-request-namespace-list-permissions.injectable";
+import createListNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
+import prometheusHandlerInjectable from "../cluster/prometheus-handler/prometheus-handler.injectable";
describe("create clusters", () => {
let cluster: Cluster;
@@ -34,8 +34,8 @@ describe("create clusters", () => {
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
di.override(normalizedPlatformInjectable, () => "darwin");
di.override(broadcastConnectionUpdateInjectable, () => async () => {});
- di.override(createAuthorizationReviewInjectable, () => () => () => Promise.resolve(true));
- di.override(requestNamespaceListPermissionsForInjectable, () => () => async () => () => true);
+ di.override(createCanIInjectable, () => () => () => Promise.resolve(true));
+ di.override(createRequestNamespaceListPermissionsInjectable, () => () => async () => () => true);
di.override(createListNamespacesInjectable, () => () => () => Promise.resolve([ "default" ]));
di.override(prometheusHandlerInjectable, () => ({
getPrometheusDetails: jest.fn(),
diff --git a/packages/core/src/main/__test__/context-handler.test.ts b/packages/core/src/main/__test__/context-handler.test.ts
deleted file mode 100644
index 61bd12398d..0000000000
--- a/packages/core/src/main/__test__/context-handler.test.ts
+++ /dev/null
@@ -1,203 +0,0 @@
-/**
- * Copyright (c) OpenLens Authors. All rights reserved.
- * Licensed under MIT License. See LICENSE in root directory for more information.
- */
-
-import { getDiForUnitTesting } from "../getDiForUnitTesting";
-import { Cluster } from "../../common/cluster/cluster";
-import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
-import type { DiContainer } from "@ogre-tools/injectable";
-import { getInjectable } from "@ogre-tools/injectable";
-import type { PrometheusProvider } from "../prometheus/provider";
-import { prometheusProviderInjectionToken } from "../prometheus/provider";
-import { runInAction } from "mobx";
-import prometheusHandlerInjectable from "../cluster/prometheus-handler/prometheus-handler.injectable";
-import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
-import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable";
-import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
-import loadProxyKubeconfigInjectable from "../cluster/load-proxy-kubeconfig.injectable";
-import type { KubeConfig } from "@kubernetes/client-node";
-
-enum ServiceResult {
- Success,
- Failure,
-}
-
-const createTestPrometheusProvider = (kind: string, alwaysFail: ServiceResult): PrometheusProvider => ({
- kind,
- name: "TestProvider1",
- isConfigurable: false,
- getQuery: () => {
- throw new Error("getQuery is not implemented.");
- },
- getPrometheusService: async () => {
- switch (alwaysFail) {
- case ServiceResult.Success:
- return {
- kind,
- namespace: "default",
- port: 7000,
- service: "",
- };
- case ServiceResult.Failure:
- throw new Error("does fail");
- }
- },
-});
-
-describe("ContextHandler", () => {
- let di: DiContainer;
- let cluster: Cluster;
-
- beforeEach(() => {
- di = getDiForUnitTesting();
-
- di.override(loadProxyKubeconfigInjectable, () => async () => ({
- makeApiClient: () => ({} as any),
- } as Partial));
-
- di.override(createKubeAuthProxyInjectable, () => () => ({
- run: async () => {},
- } as KubeAuthProxy));
- di.override(directoryForTempInjectable, () => "/some-directory-for-tmp");
- di.inject(lensProxyPortInjectable).set(9968);
-
- cluster = new Cluster({
- contextName: "some-context-name",
- id: "some-cluster-id",
- kubeConfigPath: "/some-kubeconfig-path",
- }, {
- clusterServerUrl: "https://some-website.com",
- });
- });
-
- describe("getPrometheusService", () => {
- it.each([
- [0],
- [1],
- [2],
- [3],
- ])("should throw after %d failure(s)", async (failures) => {
- runInAction(() => {
- for (let i = 0; i < failures; i += 1) {
- di.register(getInjectable({
- id: `test-prometheus-provider-failure-${i}`,
- injectionToken: prometheusProviderInjectionToken,
- instantiate: () => createTestPrometheusProvider(`id_failure_${i}`, ServiceResult.Failure),
- }));
- }
- });
-
- expect(() => di.inject(prometheusHandlerInjectable, cluster).getPrometheusDetails()).rejects.toThrowError();
- });
-
- it.each([
- [1, 0],
- [1, 1],
- [1, 2],
- [1, 3],
- [2, 0],
- [2, 1],
- [2, 2],
- [2, 3],
- ])("should pick the first provider of %d success(es) after %d failure(s)", async (successes, failures) => {
- runInAction(() => {
- for (let i = 0; i < failures; i += 1) {
- di.register(getInjectable({
- id: `test-prometheus-provider-failure-${i}`,
- injectionToken: prometheusProviderInjectionToken,
- instantiate: () => createTestPrometheusProvider(`id_failure_${i}`, ServiceResult.Failure),
- }));
- }
-
- for (let i = 0; i < successes; i += 1) {
- di.register(getInjectable({
- id: `test-prometheus-provider-success-${i}`,
- injectionToken: prometheusProviderInjectionToken,
- instantiate: () => createTestPrometheusProvider(`id_success_${i}`, ServiceResult.Success),
- }));
- }
- });
-
- const details = await di.inject(prometheusHandlerInjectable, cluster).getPrometheusDetails();
-
- expect(details.provider.kind === `id_failure_${failures}`);
- });
-
- it.each([
- [1, 0],
- [1, 1],
- [1, 2],
- [1, 3],
- [2, 0],
- [2, 1],
- [2, 2],
- [2, 3],
- ])("should pick the first provider of %d success(es) before %d failure(s)", async (successes, failures) => {
- runInAction(() => {
- for (let i = 0; i < failures; i += 1) {
- di.register(getInjectable({
- id: `test-prometheus-provider-failure-${i}`,
- injectionToken: prometheusProviderInjectionToken,
- instantiate: () => createTestPrometheusProvider(`id_failure_${i}`, ServiceResult.Failure),
- }));
- }
-
- for (let i = 0; i < successes; i += 1) {
- di.register(getInjectable({
- id: `test-prometheus-provider-success-${i}`,
- injectionToken: prometheusProviderInjectionToken,
- instantiate: () => createTestPrometheusProvider(`id_success_${i}`, ServiceResult.Success),
- }));
- }
- });
-
- const details = await di.inject(prometheusHandlerInjectable, cluster).getPrometheusDetails();
-
- expect(details.provider.kind === "id_failure_0");
- });
-
- it.each([
- [1, 0],
- [1, 1],
- [1, 2],
- [1, 3],
- [2, 0],
- [2, 1],
- [2, 2],
- [2, 3],
- ])("should pick the first provider of %d success(es) between %d failure(s)", async (successes, failures) => {
- const beforeSuccesses = Math.floor(successes / 2);
-
- runInAction(() => {
- for (let i = 0; i < beforeSuccesses; i += 1) {
- di.register(getInjectable({
- id: `test-prometheus-provider-success-${i}`,
- injectionToken: prometheusProviderInjectionToken,
- instantiate: () => createTestPrometheusProvider(`id_success_${i}`, ServiceResult.Success),
- }));
- }
-
- for (let i = 0; i < failures; i += 1) {
- di.register(getInjectable({
- id: `test-prometheus-provider-failure-${i}`,
- injectionToken: prometheusProviderInjectionToken,
- instantiate: () => createTestPrometheusProvider(`id_failure_${i}`, ServiceResult.Failure),
- }));
- }
-
- for (let i = beforeSuccesses; i < successes; i += 1) {
- di.register(getInjectable({
- id: `test-prometheus-provider-success-${i}`,
- injectionToken: prometheusProviderInjectionToken,
- instantiate: () => createTestPrometheusProvider(`id_success_${i}`, ServiceResult.Success),
- }));
- }
- });
-
- const details = await di.inject(prometheusHandlerInjectable, cluster).getPrometheusDetails();
-
- expect(details.provider.kind === "id_success_0");
- });
- });
-});
diff --git a/packages/core/src/main/__test__/kube-auth-proxy.test.ts b/packages/core/src/main/__test__/kube-auth-proxy.test.ts
index 8fa8a175a5..dc4878f806 100644
--- a/packages/core/src/main/__test__/kube-auth-proxy.test.ts
+++ b/packages/core/src/main/__test__/kube-auth-proxy.test.ts
@@ -5,7 +5,6 @@
import waitUntilPortIsUsedInjectable from "../kube-auth-proxy/wait-until-port-is-used/wait-until-port-is-used.injectable";
import { Cluster } from "../../common/cluster/cluster";
-import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
import type { ChildProcess } from "child_process";
import { Kubectl } from "../kubectl/kubectl";
import type { DeepMockProxy } from "jest-mock-extended";
@@ -13,6 +12,7 @@ import { mockDeep, mock } from "jest-mock-extended";
import type { Readable } from "stream";
import { EventEmitter } from "stream";
import { getDiForUnitTesting } from "../getDiForUnitTesting";
+import type { CreateKubeAuthProxy, KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import spawnInjectable from "../child-process/spawn.injectable";
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
@@ -29,7 +29,7 @@ import getBasenameOfPathInjectable from "../../common/path/get-basename.injectab
const clusterServerUrl = "https://192.168.64.3:8443";
describe("kube auth proxy tests", () => {
- let createKubeAuthProxy: (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy;
+ let createKubeAuthProxy: CreateKubeAuthProxy;
let spawnMock: jest.Mock;
let waitUntilPortIsUsedMock: jest.Mock;
let broadcastMessageMock: jest.Mock;
diff --git a/packages/core/src/main/__test__/prometheus-handler.test.ts b/packages/core/src/main/__test__/prometheus-handler.test.ts
index 59671553bb..74cab1ea74 100644
--- a/packages/core/src/main/__test__/prometheus-handler.test.ts
+++ b/packages/core/src/main/__test__/prometheus-handler.test.ts
@@ -43,7 +43,7 @@ const createTestPrometheusProvider = (kind: string, alwaysFail: ServiceResult):
},
});
-describe("ContextHandler", () => {
+describe("PrometheusHandler", () => {
let di: DiContainer;
let cluster: Cluster;
diff --git a/packages/core/src/main/cluster/request-api-versions.ts b/packages/core/src/main/cluster/api-versions-requester.ts
similarity index 64%
rename from packages/core/src/main/cluster/request-api-versions.ts
rename to packages/core/src/main/cluster/api-versions-requester.ts
index a9b5074549..0bb2c65f6c 100644
--- a/packages/core/src/main/cluster/request-api-versions.ts
+++ b/packages/core/src/main/cluster/api-versions-requester.ts
@@ -15,8 +15,11 @@ export interface ClusterData {
readonly id: string;
}
-export type RequestApiVersions = (cluster: ClusterData) => AsyncResult;
+export interface ApiVersionsRequester {
+ request(cluster: ClusterData): AsyncResult;
+ readonly orderNumber: number;
+}
-export const requestApiVersionsInjectionToken = getInjectionToken({
+export const apiVersionsRequesterInjectionToken = getInjectionToken({
id: "request-api-versions-token",
});
diff --git a/packages/core/src/main/cluster/cluster-connection.injectable.ts b/packages/core/src/main/cluster/cluster-connection.injectable.ts
index c5d9a5f165..3f40898fc7 100644
--- a/packages/core/src/main/cluster/cluster-connection.injectable.ts
+++ b/packages/core/src/main/cluster/cluster-connection.injectable.ts
@@ -6,10 +6,7 @@
import { type KubeConfig, HttpError } from "@kubernetes/client-node";
import { reaction, comparer, runInAction } from "mobx";
import { ClusterStatus } from "../../common/cluster-types";
-import type { CreateAuthorizationReview } from "../../common/cluster/authorization-review.injectable";
-import type { Cluster } from "../../common/cluster/cluster";
import type { CreateListNamespaces } from "../../common/cluster/list-namespaces.injectable";
-import type { RequestNamespaceListPermissionsFor, RequestNamespaceListPermissions } from "../../common/cluster/request-namespace-list-permissions.injectable";
import type { BroadcastMessage } from "../../common/ipc/broadcast-message.injectable";
import { clusterListNamespaceForbiddenChannel } from "../../common/ipc/cluster";
import type { Logger } from "../../common/logger";
@@ -25,7 +22,6 @@ import type { RequestApiResources } from "./request-api-resources.injectable";
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import broadcastConnectionUpdateInjectable from "./broadcast-connection-update.injectable";
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
-import createAuthorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
import createListNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
import kubeAuthProxyServerInjectable from "./kube-auth-proxy-server.injectable";
import loadProxyKubeconfigInjectable from "./load-proxy-kubeconfig.injectable";
@@ -33,21 +29,31 @@ import loggerInjectable from "../../common/logger.injectable";
import prometheusHandlerInjectable from "./prometheus-handler/prometheus-handler.injectable";
import removeProxyKubeconfigInjectable from "./remove-proxy-kubeconfig.injectable";
import requestApiResourcesInjectable from "./request-api-resources.injectable";
-import requestNamespaceListPermissionsForInjectable from "../../common/cluster/request-namespace-list-permissions.injectable";
import type { DetectClusterMetadata } from "../cluster-detectors/detect-cluster-metadata.injectable";
import type { FallibleOnlyClusterMetadataDetector } from "../cluster-detectors/token";
import clusterVersionDetectorInjectable from "../cluster-detectors/cluster-version-detector.injectable";
import detectClusterMetadataInjectable from "../cluster-detectors/detect-cluster-metadata.injectable";
import { replaceObservableObject } from "../../common/utils/replace-observable-object";
+import type { CreateAuthorizationApi } from "../../common/cluster/create-authorization-api.injectable";
+import type { CreateCanI } from "../../common/cluster/create-can-i.injectable";
+import type { CreateRequestNamespaceListPermissions, RequestNamespaceListPermissions } from "../../common/cluster/create-request-namespace-list-permissions.injectable";
+import type { Cluster } from "../../common/cluster/cluster";
+import createAuthorizationApiInjectable from "../../common/cluster/create-authorization-api.injectable";
+import createCanIInjectable from "../../common/cluster/create-can-i.injectable";
+import createRequestNamespaceListPermissionsInjectable from "../../common/cluster/create-request-namespace-list-permissions.injectable";
+import type { CreateCoreApi } from "../../common/cluster/create-core-api.injectable";
+import createCoreApiInjectable from "../../common/cluster/create-core-api.injectable";
interface Dependencies {
readonly logger: Logger;
readonly prometheusHandler: ClusterPrometheusHandler;
readonly kubeAuthProxyServer: KubeAuthProxyServer;
readonly clusterVersionDetector: FallibleOnlyClusterMetadataDetector;
- createAuthorizationReview: CreateAuthorizationReview;
+ createCanI: CreateCanI;
requestApiResources: RequestApiResources;
- requestNamespaceListPermissionsFor: RequestNamespaceListPermissionsFor;
+ createRequestNamespaceListPermissions: CreateRequestNamespaceListPermissions;
+ createAuthorizationApi: CreateAuthorizationApi;
+ createCoreApi: CreateCoreApi;
createListNamespaces: CreateListNamespaces;
detectClusterMetadata: DetectClusterMetadata;
broadcastMessage: BroadcastMessage;
@@ -224,8 +230,9 @@ class ClusterConnection {
private async refreshAccessibility(): Promise {
this.dependencies.logger.info(`[CLUSTER]: refreshAccessibility`, this.cluster.getMeta());
const proxyConfig = await this.dependencies.loadProxyKubeconfig();
- const canI = this.dependencies.createAuthorizationReview(proxyConfig);
- const requestNamespaceListPermissions = this.dependencies.requestNamespaceListPermissionsFor(proxyConfig);
+ const api = this.dependencies.createAuthorizationApi(proxyConfig);
+ const canI = this.dependencies.createCanI(api);
+ const requestNamespaceListPermissions = this.dependencies.createRequestNamespaceListPermissions(api);
const isAdmin = await canI({
namespace: "kube-system",
@@ -360,7 +367,8 @@ class ClusterConnection {
}
try {
- const listNamespaces = this.dependencies.createListNamespaces(proxyConfig);
+ const api = this.dependencies.createCoreApi(proxyConfig);
+ const listNamespaces = this.dependencies.createListNamespaces(api);
return await listNamespaces();
} catch (error) {
@@ -403,13 +411,15 @@ const clusterConnectionInjectable = getInjectable({
prometheusHandler: di.inject(prometheusHandlerInjectable, cluster),
broadcastConnectionUpdate: di.inject(broadcastConnectionUpdateInjectable, cluster),
broadcastMessage: di.inject(broadcastMessageInjectable),
- createAuthorizationReview: di.inject(createAuthorizationReviewInjectable),
createListNamespaces: di.inject(createListNamespacesInjectable),
detectClusterMetadata: di.inject(detectClusterMetadataInjectable),
loadProxyKubeconfig: di.inject(loadProxyKubeconfigInjectable, cluster),
removeProxyKubeconfig: di.inject(removeProxyKubeconfigInjectable, cluster),
requestApiResources: di.inject(requestApiResourcesInjectable),
- requestNamespaceListPermissionsFor: di.inject(requestNamespaceListPermissionsForInjectable),
+ createAuthorizationApi: di.inject(createAuthorizationApiInjectable),
+ createCoreApi: di.inject(createCoreApiInjectable),
+ createCanI: di.inject(createCanIInjectable),
+ createRequestNamespaceListPermissions: di.inject(createRequestNamespaceListPermissionsInjectable),
},
cluster,
),
diff --git a/packages/core/src/main/cluster/kube-auth-proxy-server.injectable.ts b/packages/core/src/main/cluster/kube-auth-proxy-server.injectable.ts
index 9942c40168..ff70af3a7b 100644
--- a/packages/core/src/main/cluster/kube-auth-proxy-server.injectable.ts
+++ b/packages/core/src/main/cluster/kube-auth-proxy-server.injectable.ts
@@ -8,7 +8,7 @@ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import type { Cluster } from "../../common/cluster/cluster";
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
import kubeAuthProxyCertificateInjectable from "../kube-auth-proxy/kube-auth-proxy-certificate.injectable";
-import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
+import type { KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
export interface KubeAuthProxyServer {
getApiTarget(isLongRunningRequest?: boolean): Promise;
diff --git a/packages/core/src/main/cluster/request-api-resources.injectable.ts b/packages/core/src/main/cluster/request-api-resources.injectable.ts
index d6a21e3ce4..14776bb93a 100644
--- a/packages/core/src/main/cluster/request-api-resources.injectable.ts
+++ b/packages/core/src/main/cluster/request-api-resources.injectable.ts
@@ -7,11 +7,12 @@ import { getInjectable } from "@ogre-tools/injectable";
import loggerInjectable from "../../common/logger.injectable";
import type { KubeApiResource } from "../../common/rbac";
import type { Cluster } from "../../common/cluster/cluster";
-import { requestApiVersionsInjectionToken } from "./request-api-versions";
+import { apiVersionsRequesterInjectionToken } from "./api-versions-requester";
import { backoffCaller, withConcurrencyLimit } from "@k8slens/utilities";
import requestKubeApiResourcesForInjectable from "./request-kube-api-resources-for.injectable";
import type { AsyncResult } from "@k8slens/utilities";
import broadcastConnectionUpdateInjectable from "./broadcast-connection-update.injectable";
+import { byOrderNumber } from "../../common/utils/composable-responsibilities/orderable/orderable";
export type RequestApiResources = (cluster: Cluster) => AsyncResult;
@@ -24,7 +25,8 @@ const requestApiResourcesInjectable = getInjectable({
id: "request-api-resources",
instantiate: (di): RequestApiResources => {
const logger = di.inject(loggerInjectable);
- const apiVersionRequesters = di.injectMany(requestApiVersionsInjectionToken);
+ const apiVersionRequesters = di.injectMany(apiVersionsRequesterInjectionToken)
+ .sort(byOrderNumber);
const requestKubeApiResourcesFor = di.inject(requestKubeApiResourcesForInjectable);
return async (...args) => {
@@ -35,7 +37,7 @@ const requestApiResourcesInjectable = getInjectable({
const groupLists: KubeResourceListGroup[] = [];
for (const apiVersionRequester of apiVersionRequesters) {
- const result = await backoffCaller(() => apiVersionRequester(cluster), {
+ const result = await backoffCaller(() => apiVersionRequester.request(cluster), {
onIntermediateError: (error, attempt) => {
broadcastConnectionUpdate({
message: `Failed to list kube API resource kinds, attempt ${attempt}: ${error}`,
diff --git a/packages/core/src/main/cluster/request-core-api-versions.injectable.ts b/packages/core/src/main/cluster/request-core-api-versions.injectable.ts
index b709eb9356..5b059d6f7f 100644
--- a/packages/core/src/main/cluster/request-core-api-versions.injectable.ts
+++ b/packages/core/src/main/cluster/request-core-api-versions.injectable.ts
@@ -5,33 +5,36 @@
import type { V1APIVersions } from "@kubernetes/client-node";
import { getInjectable } from "@ogre-tools/injectable";
import k8sRequestInjectable from "../k8s-request.injectable";
-import { requestApiVersionsInjectionToken } from "./request-api-versions";
+import { apiVersionsRequesterInjectionToken } from "./api-versions-requester";
const requestCoreApiVersionsInjectable = getInjectable({
id: "request-core-api-versions",
instantiate: (di) => {
const k8sRequest = di.inject(k8sRequestInjectable);
- return async (cluster) => {
- try {
- const { versions } = await k8sRequest(cluster, "/api") as V1APIVersions;
+ return {
+ request: async (cluster) => {
+ try {
+ const { versions } = await k8sRequest(cluster, "/api") as V1APIVersions;
- return {
- callWasSuccessful: true,
- response: versions.map(version => ({
- group: "",
- path: `/api/${version}`,
- })),
- };
- } catch (error) {
- return {
- callWasSuccessful: false,
- error: error as Error,
- };
- }
+ return {
+ callWasSuccessful: true,
+ response: versions.map(version => ({
+ group: "",
+ path: `/api/${version}`,
+ })),
+ };
+ } catch (error) {
+ return {
+ callWasSuccessful: false,
+ error: error as Error,
+ };
+ }
+ },
+ orderNumber: 10,
};
},
- injectionToken: requestApiVersionsInjectionToken,
+ injectionToken: apiVersionsRequesterInjectionToken,
});
export default requestCoreApiVersionsInjectable;
diff --git a/packages/core/src/main/cluster/request-kube-api-resources-for.injectable.ts b/packages/core/src/main/cluster/request-kube-api-resources-for.injectable.ts
index 65e326eed7..80720a9ef1 100644
--- a/packages/core/src/main/cluster/request-kube-api-resources-for.injectable.ts
+++ b/packages/core/src/main/cluster/request-kube-api-resources-for.injectable.ts
@@ -8,7 +8,7 @@ import type { Cluster } from "../../common/cluster/cluster";
import type { KubeApiResource } from "../../common/rbac";
import type { AsyncResult } from "@k8slens/utilities";
import k8sRequestInjectable from "../k8s-request.injectable";
-import type { KubeResourceListGroup } from "./request-api-versions";
+import type { KubeResourceListGroup } from "./api-versions-requester";
export type RequestKubeApiResources = (grouping: KubeResourceListGroup) => AsyncResult;
diff --git a/packages/core/src/main/cluster/request-non-core-api-versions.injectable.ts b/packages/core/src/main/cluster/request-non-core-api-versions.injectable.ts
index 84e62f6b80..7e5abbc44d 100644
--- a/packages/core/src/main/cluster/request-non-core-api-versions.injectable.ts
+++ b/packages/core/src/main/cluster/request-non-core-api-versions.injectable.ts
@@ -6,35 +6,40 @@ import type { V1APIGroupList } from "@kubernetes/client-node";
import { getInjectable } from "@ogre-tools/injectable";
import { iter } from "@k8slens/utilities";
import k8sRequestInjectable from "../k8s-request.injectable";
-import { requestApiVersionsInjectionToken } from "./request-api-versions";
+import { apiVersionsRequesterInjectionToken } from "./api-versions-requester";
const requestNonCoreApiVersionsInjectable = getInjectable({
id: "request-non-core-api-versions",
instantiate: (di) => {
const k8sRequest = di.inject(k8sRequestInjectable);
- return async (cluster) => {
- try {
- const { groups } = await k8sRequest(cluster, "/apis") as V1APIGroupList;
+ return {
+ request: async (cluster) => {
+ try {
+ const { groups } = (await k8sRequest(cluster, "/apis")) as V1APIGroupList;
- return {
- callWasSuccessful: true,
- response: iter.chain(groups.values())
- .flatMap(group => group.versions.map(version => ({
- group: group.name,
- path: `/apis/${version.groupVersion}`,
- })))
- .collect(v => [...v]),
- };
- } catch (error) {
- return {
- callWasSuccessful: false,
- error: error as Error,
- };
- }
+ return {
+ callWasSuccessful: true,
+ response: iter.chain(groups.values())
+ .flatMap((group) =>
+ group.versions.map((version) => ({
+ group: group.name,
+ path: `/apis/${version.groupVersion}`,
+ })),
+ )
+ .collect((v) => [...v]),
+ };
+ } catch (error) {
+ return {
+ callWasSuccessful: false,
+ error: error as Error,
+ };
+ }
+ },
+ orderNumber: 20,
};
},
- injectionToken: requestApiVersionsInjectionToken,
+ injectionToken: apiVersionsRequesterInjectionToken,
});
export default requestNonCoreApiVersionsInjectable;
diff --git a/packages/core/src/main/cluster/request-non-core-api-versions.test.ts b/packages/core/src/main/cluster/request-non-core-api-versions.test.ts
index c38359b6a5..ca4dcd01b7 100644
--- a/packages/core/src/main/cluster/request-non-core-api-versions.test.ts
+++ b/packages/core/src/main/cluster/request-non-core-api-versions.test.ts
@@ -10,13 +10,13 @@ import type { DiContainer } from "@ogre-tools/injectable";
import { getDiForUnitTesting } from "../getDiForUnitTesting";
import type { K8sRequest } from "../k8s-request.injectable";
import k8sRequestInjectable from "../k8s-request.injectable";
-import type { RequestApiVersions } from "./request-api-versions";
+import type { ApiVersionsRequester } from "./api-versions-requester";
import requestNonCoreApiVersionsInjectable from "./request-non-core-api-versions.injectable";
describe("requestNonCoreApiVersions", () => {
let di: DiContainer;
let k8sRequestMock: AsyncFnMock;
- let requestNonCoreApiVersions: RequestApiVersions;
+ let requestNonCoreApiVersions: ApiVersionsRequester;
beforeEach(() => {
di = getDiForUnitTesting();
@@ -28,10 +28,10 @@ describe("requestNonCoreApiVersions", () => {
});
describe("when called", () => {
- let versionsRequest: ReturnType;
+ let versionsRequest: ReturnType;
beforeEach(() => {
- versionsRequest = requestNonCoreApiVersions({ id: "some-cluster-id" });
+ versionsRequest = requestNonCoreApiVersions.request({ id: "some-cluster-id" });
});
it("should request all api groups", () => {
diff --git a/packages/core/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts b/packages/core/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts
index 56260347bf..fd4acf0d7a 100644
--- a/packages/core/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts
+++ b/packages/core/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { KubeAuthProxyDependencies } from "./kube-auth-proxy";
-import { KubeAuthProxy } from "./kube-auth-proxy";
+import { KubeAuthProxyImpl } from "./kube-auth-proxy";
import type { Cluster } from "../../common/cluster/cluster";
import spawnInjectable from "../child-process/spawn.injectable";
import kubeAuthProxyCertificateInjectable from "./kube-auth-proxy-certificate.injectable";
@@ -15,6 +15,13 @@ import getPortFromStreamInjectable from "../utils/get-port-from-stream.injectabl
import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable";
import broadcastConnectionUpdateInjectable from "../cluster/broadcast-connection-update.injectable";
+export interface KubeAuthProxy {
+ readonly apiPrefix: string;
+ readonly port: number;
+ run: () => Promise;
+ exit: () => void;
+}
+
export type CreateKubeAuthProxy = (cluster: Cluster, env: NodeJS.ProcessEnv) => KubeAuthProxy;
const createKubeAuthProxyInjectable = getInjectable({
@@ -33,7 +40,7 @@ const createKubeAuthProxyInjectable = getInjectable({
return (cluster, env) => {
const clusterUrl = new URL(cluster.apiUrl.get());
- return new KubeAuthProxy({
+ return new KubeAuthProxyImpl({
...dependencies,
proxyCert: di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname),
broadcastConnectionUpdate: di.inject(broadcastConnectionUpdateInjectable, cluster),
diff --git a/packages/core/src/main/kube-auth-proxy/kube-auth-proxy.ts b/packages/core/src/main/kube-auth-proxy/kube-auth-proxy.ts
index 9a9b5a249f..160566c3a5 100644
--- a/packages/core/src/main/kube-auth-proxy/kube-auth-proxy.ts
+++ b/packages/core/src/main/kube-auth-proxy/kube-auth-proxy.ts
@@ -16,6 +16,7 @@ import type { Logger } from "../../common/logger";
import type { WaitUntilPortIsUsed } from "./wait-until-port-is-used/wait-until-port-is-used.injectable";
import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable";
import type { BroadcastConnectionUpdate } from "../cluster/broadcast-connection-update.injectable";
+import type { KubeAuthProxy } from "./create-kube-auth-proxy.injectable";
const startingServeMatcher = "starting to serve on (?.+)";
const startingServeRegex = Object.assign(TypedRegEx(startingServeMatcher, "i"), {
@@ -33,7 +34,7 @@ export interface KubeAuthProxyDependencies {
broadcastConnectionUpdate: BroadcastConnectionUpdate;
}
-export class KubeAuthProxy {
+export class KubeAuthProxyImpl implements KubeAuthProxy {
public readonly apiPrefix = `/${randomBytes(8).toString("hex")}`;
public get port(): number {
diff --git a/packages/core/src/main/kubeconfig-manager/kubeconfig-manager.ts b/packages/core/src/main/kubeconfig-manager/kubeconfig-manager.ts
index 9d28306bbf..276692c500 100644
--- a/packages/core/src/main/kubeconfig-manager/kubeconfig-manager.ts
+++ b/packages/core/src/main/kubeconfig-manager/kubeconfig-manager.ts
@@ -85,7 +85,7 @@ export class KubeconfigManager {
return this.tempFilePath = await this.createProxyKubeconfig();
} catch (error) {
- throw new Error(`Failed to creat temp config for auth-proxy: ${error}`);
+ throw new Error(`Failed to create temp config for auth-proxy: ${error}`);
}
}
diff --git a/packages/core/src/test-utils/cast.ts b/packages/core/src/test-utils/cast.ts
new file mode 100644
index 0000000000..a3e7329cdd
--- /dev/null
+++ b/packages/core/src/test-utils/cast.ts
@@ -0,0 +1,6 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+export const cast = (data: Partial): T => data as T;
diff --git a/packages/core/src/test-utils/mock-interface.ts b/packages/core/src/test-utils/mock-interface.ts
new file mode 100644
index 0000000000..e9870282b3
--- /dev/null
+++ b/packages/core/src/test-utils/mock-interface.ts
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { AsyncFnMock } from "@async-fn/jest";
+
+type GetMockedType =
+ T extends (...args: any[]) => Promise
+ ? AsyncFnMock
+ : T extends (...args: any[]) => any
+ ? jest.MockedFunction
+ : T;
+
+export type Mocked = {
+ -readonly [P in keyof T]: GetMockedType;
+};
diff --git a/packages/utility-features/test-utils/src/flush-promises.ts b/packages/utility-features/test-utils/src/flush-promises.ts
index c2fdeff99e..fa3271654a 100644
--- a/packages/utility-features/test-utils/src/flush-promises.ts
+++ b/packages/utility-features/test-utils/src/flush-promises.ts
@@ -2,6 +2,9 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import { setImmediate } from "timers";
+import { setImmediate, setTimeout } from "timers/promises";
-export const flushPromises = () => new Promise(setImmediate);
+export const flushPromises = async () => {
+ await setImmediate();
+ await setTimeout(5);
+};
From 4b1d740d6154d06b655cc106b09b5a39aad2703a Mon Sep 17 00:00:00 2001
From: Sebastian Malton
Date: Wed, 22 Mar 2023 11:51:26 -0400
Subject: [PATCH 6/9] Fix behaviour of auto generated CRD KubeApis and
KubeObjectStores (#7384)
* Simplify CRD KubeApi registrations
- Switch to auto injectable registrations
Signed-off-by: Sebastian Malton
* Make sure that stores can still be retrieved
Signed-off-by: Sebastian Malton
* Cleanup get extension fake to simplify impl
Signed-off-by: Sebastian Malton
* Simplify logic for extensionInjectable
Signed-off-by: Sebastian Malton
* Fix test in differencing registrator
Signed-off-by: Sebastian Malton
* Cleanup code style
Signed-off-by: Sebastian Malton
* Fix some tests
Signed-off-by: Sebastian Malton
* Fix HPA details tests
Signed-off-by: Sebastian Malton
---------
Signed-off-by: Sebastian Malton
---
.../k8s-api/__tests__/api-manager.test.ts | 90 +++++++++++++++
.../common/k8s-api/api-manager/api-manager.ts | 35 +++++-
.../auto-registration-emitter.injectable.ts | 2 -
.../k8s-api/api-manager/crd-api-token.ts | 11 ++
...create-custom-resource-store.injectable.ts | 27 +++++
.../k8s-api/api-manager/manager.injectable.ts | 6 +
.../src/common/k8s-api/kube-object.store.ts | 2 +-
.../src/common/utils/registrator-helper.ts | 25 +++++
.../extension/extension.injectable.ts | 50 +++------
...-characters-in-page-registrations.test.tsx | 3 +-
...setup-auto-crd-api-creations.injectable.ts | 53 +++++++++
.../setup-auto-registration.injectable.ts | 52 +--------
.../hpa-details.test.tsx | 16 +++
.../definition.store.injectable.ts | 2 -
.../+custom-resources/definition.store.ts | 19 +---
.../test-utils/get-application-builder.tsx | 103 ++++++------------
.../test-utils/get-extension-fake.ts | 8 +-
17 files changed, 315 insertions(+), 189 deletions(-)
create mode 100644 packages/core/src/common/k8s-api/api-manager/crd-api-token.ts
create mode 100644 packages/core/src/common/k8s-api/api-manager/create-custom-resource-store.injectable.ts
create mode 100644 packages/core/src/common/utils/registrator-helper.ts
create mode 100644 packages/core/src/renderer/before-frame-starts/runnables/setup-auto-crd-api-creations.injectable.ts
diff --git a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts
index 6ea6327038..48fdb3e544 100644
--- a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts
+++ b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts
@@ -4,6 +4,7 @@
*/
import type { DiContainer } from "@ogre-tools/injectable";
+import { getInjectable } from "@ogre-tools/injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../../renderer/cluster-frame-context/for-namespaced-resources.injectable";
import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable";
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
@@ -21,6 +22,9 @@ import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
// eslint-disable-next-line no-restricted-imports
import { KubeApi as ExternalKubeApi } from "../../../extensions/common-api/k8s-api";
import { Cluster } from "../../cluster/cluster";
+import { runInAction } from "mobx";
+import { customResourceDefinitionApiInjectionToken } from "../api-manager/crd-api-token";
+import assert from "assert";
class TestApi extends KubeApi {
protected async checkPreferredVersion() {
@@ -117,4 +121,90 @@ describe("ApiManager", () => {
});
});
});
+
+ describe("given than a CRD has a default KubeApi registered for it", () => {
+ const apiBase = "/apis/aquasecurity.github.io/v1alpha1/vulnerabilityreports";
+
+ beforeEach(() => {
+ runInAction(() => {
+ di.register(getInjectable({
+ id: `default-kube-api-for-custom-resource-definition-${apiBase}`,
+ instantiate: (di) => {
+ const objectConstructor = class extends KubeObject {
+ static readonly kind = "VulnerabilityReport";
+ static readonly namespaced = true;
+ static readonly apiBase = apiBase;
+ };
+
+ return Object.assign(
+ new KubeApi({
+ logger: di.inject(loggerInjectable),
+ maybeKubeApi: di.inject(maybeKubeApiInjectable),
+ }, { objectConstructor }),
+ {
+ myField: 1,
+ },
+ );
+ },
+ injectionToken: customResourceDefinitionApiInjectionToken,
+ }));
+ });
+ });
+
+ it("can be retrieved from apiManager", () => {
+ expect(apiManager.getApi(apiBase)).toMatchObject({
+ myField: 1,
+ });
+ });
+
+ it("can have a default KubeObjectStore instance retrieved for it", () => {
+ expect(apiManager.getStore(apiBase)).toBeInstanceOf(KubeObjectStore);
+ });
+
+ describe("given that an extension registers an api with the same apibase", () => {
+ beforeEach(() => {
+ void Object.assign(new ExternalKubeApi({
+ objectConstructor: KubeObject,
+ apiBase,
+ kind: "VulnerabilityReport",
+ }), {
+ myField: 2,
+ });
+ });
+
+ it("the extension's instance is retrievable instead from apiManager", () => {
+ expect(apiManager.getApi(apiBase)).toMatchObject({
+ myField: 2,
+ });
+ });
+
+ it("can have a default KubeObjectStore instance retrieved for it", () => {
+ expect(apiManager.getStore(apiBase)).toBeInstanceOf(KubeObjectStore);
+ });
+
+ describe("given that an extension registers a store for the same apibase", () => {
+ beforeEach(() => {
+ const api = apiManager.getApi(apiBase);
+
+ assert(api);
+
+ apiManager.registerStore(Object.assign(
+ new KubeObjectStore({
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ logger: di.inject(loggerInjectable),
+ }, api),
+ {
+ someField: 2,
+ },
+ ));
+ });
+
+ it("can gets the custom KubeObjectStore instance instead", () => {
+ expect(apiManager.getStore(apiBase)).toMatchObject({
+ someField: 2,
+ });
+ });
+ });
+ });
+ });
});
diff --git a/packages/core/src/common/k8s-api/api-manager/api-manager.ts b/packages/core/src/common/k8s-api/api-manager/api-manager.ts
index f6c2758921..6b60ba8e7a 100644
--- a/packages/core/src/common/k8s-api/api-manager/api-manager.ts
+++ b/packages/core/src/common/k8s-api/api-manager/api-manager.ts
@@ -10,7 +10,8 @@ import { autorun, action, observable } from "mobx";
import type { KubeApi } from "../kube-api";
import type { KubeObject, ObjectReference } from "../kube-object";
import { parseKubeApi, createKubeApiURL } from "../kube-api-parse";
-import { iter } from "@k8slens/utilities";
+import { getOrInsertWith, iter } from "@k8slens/utilities";
+import type { CreateCustomResourceStore } from "./create-custom-resource-store.injectable";
export type RegisterableStore = Store extends KubeObjectStore
? Store
@@ -26,13 +27,15 @@ export type FindApiCallback = (api: KubeApi) => boolean;
interface Dependencies {
readonly apis: IComputedValue;
+ readonly crdApis: IComputedValue;
readonly stores: IComputedValue;
+ createCustomResourceStore: CreateCustomResourceStore;
}
export class ApiManager {
private readonly externalApis = observable.array();
private readonly externalStores = observable.array();
-
+ private readonly defaultCrdStores = observable.map();
private readonly apis = observable.map();
constructor(private readonly dependencies: Dependencies) {
@@ -56,6 +59,12 @@ export class ApiManager {
}
}
+ for (const crdApi of this.dependencies.crdApis.get()) {
+ if (!newState.has(crdApi.apiBase)) {
+ newState.set(crdApi.apiBase, crdApi);
+ }
+ }
+
this.apis.replace(newState);
});
}
@@ -110,6 +119,16 @@ export class ApiManager {
this.externalStores.push(store);
}
+ private apiIsDefaultCrdApi(api: KubeApi): boolean {
+ for (const crdApi of this.dependencies.crdApis.get()) {
+ if (crdApi.apiBase === api.apiBase) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
getStore(api: string | undefined): KubeObjectStore | undefined;
getStore(api: RegisterableApi): KubeObjectStoreFrom | undefined;
/**
@@ -130,9 +149,19 @@ export class ApiManager {
return undefined;
}
- return iter.chain(this.dependencies.stores.get().values())
+ const defaultResult = iter.chain(this.dependencies.stores.get().values())
.concat(this.externalStores.values())
.find(store => store.api.apiBase === api.apiBase);
+
+ if (defaultResult) {
+ return defaultResult;
+ }
+
+ if (this.apiIsDefaultCrdApi(api)) {
+ return getOrInsertWith(this.defaultCrdStores, api.apiBase, () => this.dependencies.createCustomResourceStore(api));
+ }
+
+ return undefined;
}
lookupApiLink(ref: ObjectReference, parentObject?: KubeObject): string {
diff --git a/packages/core/src/common/k8s-api/api-manager/auto-registration-emitter.injectable.ts b/packages/core/src/common/k8s-api/api-manager/auto-registration-emitter.injectable.ts
index d9a68a988c..714e9d8952 100644
--- a/packages/core/src/common/k8s-api/api-manager/auto-registration-emitter.injectable.ts
+++ b/packages/core/src/common/k8s-api/api-manager/auto-registration-emitter.injectable.ts
@@ -5,11 +5,9 @@
import { getInjectable } from "@ogre-tools/injectable";
import EventEmitter from "events";
import type TypedEventEmitter from "typed-emitter";
-import type { CustomResourceDefinition } from "../endpoints";
import type { KubeApi } from "../kube-api";
export interface LegacyAutoRegistration {
- customResourceDefinition: (crd: CustomResourceDefinition) => void;
kubeApi: (api: KubeApi) => void;
}
diff --git a/packages/core/src/common/k8s-api/api-manager/crd-api-token.ts b/packages/core/src/common/k8s-api/api-manager/crd-api-token.ts
new file mode 100644
index 0000000000..d8f0c918c2
--- /dev/null
+++ b/packages/core/src/common/k8s-api/api-manager/crd-api-token.ts
@@ -0,0 +1,11 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import { getInjectionToken } from "@ogre-tools/injectable";
+import type { KubeApi } from "../kube-api";
+
+export const customResourceDefinitionApiInjectionToken = getInjectionToken({
+ id: "custom-resource-definition-api-token",
+});
diff --git a/packages/core/src/common/k8s-api/api-manager/create-custom-resource-store.injectable.ts b/packages/core/src/common/k8s-api/api-manager/create-custom-resource-store.injectable.ts
new file mode 100644
index 0000000000..fbf46e2b42
--- /dev/null
+++ b/packages/core/src/common/k8s-api/api-manager/create-custom-resource-store.injectable.ts
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../../renderer/cluster-frame-context/for-namespaced-resources.injectable";
+import loggerInjectable from "../../logger.injectable";
+import type { KubeApi } from "../kube-api";
+import type { KubeObject } from "../kube-object";
+import type { KubeObjectStoreDependencies } from "../kube-object.store";
+import { CustomResourceStore } from "./resource.store";
+
+export type CreateCustomResourceStore = (api: KubeApi) => CustomResourceStore;
+
+const createCustomResourceStoreInjectable = getInjectable({
+ id: "create-custom-resource-store",
+ instantiate: (di): CreateCustomResourceStore => {
+ const deps: KubeObjectStoreDependencies = {
+ context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
+ logger: di.inject(loggerInjectable),
+ };
+
+ return (api) => new CustomResourceStore(deps, api);
+ },
+});
+
+export default createCustomResourceStoreInjectable;
diff --git a/packages/core/src/common/k8s-api/api-manager/manager.injectable.ts b/packages/core/src/common/k8s-api/api-manager/manager.injectable.ts
index f0b61c28b6..2ffd41a296 100644
--- a/packages/core/src/common/k8s-api/api-manager/manager.injectable.ts
+++ b/packages/core/src/common/k8s-api/api-manager/manager.injectable.ts
@@ -9,6 +9,8 @@ import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-f
import { kubeObjectStoreInjectionToken } from "./kube-object-store-token";
import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token";
import { computed } from "mobx";
+import { customResourceDefinitionApiInjectionToken } from "./crd-api-token";
+import createCustomResourceStoreInjectable from "./create-custom-resource-store.injectable";
const apiManagerInjectable = getInjectable({
id: "api-manager",
@@ -23,6 +25,10 @@ const apiManagerInjectable = getInjectable({
stores: storesAndApisCanBeCreated
? computedInjectMany(kubeObjectStoreInjectionToken)
: computed(() => []),
+ crdApis: storesAndApisCanBeCreated
+ ? computedInjectMany(customResourceDefinitionApiInjectionToken)
+ : computed(() => []),
+ createCustomResourceStore: di.inject(createCustomResourceStoreInjectable),
});
},
});
diff --git a/packages/core/src/common/k8s-api/kube-object.store.ts b/packages/core/src/common/k8s-api/kube-object.store.ts
index 09b26d4142..fa17fd1f2c 100644
--- a/packages/core/src/common/k8s-api/kube-object.store.ts
+++ b/packages/core/src/common/k8s-api/kube-object.store.ts
@@ -88,7 +88,7 @@ export interface KubeObjectStoreDependencies {
readonly logger: Logger;
}
-export abstract class KubeObjectStore<
+export class KubeObjectStore<
K extends KubeObject = KubeObject,
A extends KubeApi = KubeApi>,
D extends KubeJsonApiDataFor = KubeApiDataFrom,
diff --git a/packages/core/src/common/utils/registrator-helper.ts b/packages/core/src/common/utils/registrator-helper.ts
new file mode 100644
index 0000000000..4a9cc5c2d2
--- /dev/null
+++ b/packages/core/src/common/utils/registrator-helper.ts
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import { iter } from "@k8slens/utilities";
+import type { DiContainerForInjection, Injectable } from "@ogre-tools/injectable";
+
+// Register new injectables and deregister removed injectables by id
+
+export const injectableDifferencingRegistratorWith = (di: DiContainerForInjection) => (
+ (rawCurrent: Injectable[], rawPrevious: Injectable[] = []) => {
+ const current = new Map(rawCurrent.map(inj => [inj.id, inj]));
+ const previous = new Map(rawPrevious.map(inj => [inj.id, inj]));
+ const toAdd = iter.chain(current.entries())
+ .filter(([id]) => !previous.has(id))
+ .collect(entries => new Map(entries));
+ const toRemove = iter.chain(previous.entries())
+ .filter(([id]) => !current.has(id))
+ .collect(entries => new Map(entries));
+
+ di.deregister(...toRemove.values());
+ di.register(...toAdd.values());
+ }
+);
diff --git a/packages/core/src/extensions/extension-loader/extension/extension.injectable.ts b/packages/core/src/extensions/extension-loader/extension/extension.injectable.ts
index 07f054d3fd..d54d997d09 100644
--- a/packages/core/src/extensions/extension-loader/extension/extension.injectable.ts
+++ b/packages/core/src/extensions/extension-loader/extension/extension.injectable.ts
@@ -2,29 +2,18 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import type { Injectable } from "@ogre-tools/injectable";
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
-import { difference, find, map } from "lodash";
import { reaction, runInAction } from "mobx";
import { disposer } from "@k8slens/utilities";
import type { LensExtension } from "../../lens-extension";
import { extensionRegistratorInjectionToken } from "../extension-registrator-injection-token";
+import { injectableDifferencingRegistratorWith } from "../../../common/utils/registrator-helper";
export interface Extension {
register: () => void;
deregister: () => void;
}
-const idsToInjectables = (ids: string[], injectables: Injectable[]) => ids.map(id => {
- const injectable = find(injectables, { id });
-
- if (!injectable) {
- throw new Error(`Injectable ${id} not found`);
- }
-
- return injectable;
-});
-
const extensionInjectable = getInjectable({
id: "extension",
@@ -35,36 +24,27 @@ const extensionInjectable = getInjectable({
instantiate: (childDi) => {
const extensionRegistrators = childDi.injectMany(extensionRegistratorInjectionToken);
const reactionDisposer = disposer();
+ const injectableDifferencingRegistrator = injectableDifferencingRegistratorWith(childDi);
return {
register: () => {
- extensionRegistrators.forEach((getInjectablesOfExtension) => {
- const injectables = getInjectablesOfExtension(instance);
+ for (const extensionRegistrator of extensionRegistrators) {
+ const injectables = extensionRegistrator(instance);
- reactionDisposer.push(
- // injectables is either an array or a computed array, in which case
- // we need to update the registered injectables with a reaction every time they change
- reaction(
- () => Array.isArray(injectables) ? injectables : injectables.get(),
- (currentInjectables, previousInjectables = []) => {
- // Register new injectables and deregister removed injectables by id
- const currentIds = map(currentInjectables, "id");
- const previousIds = map(previousInjectables, "id");
- const idsToAdd = difference(currentIds, previousIds);
- const idsToRemove = previousIds.filter(previousId => !currentIds.includes(previousId));
-
- if (idsToRemove.length > 0) {
- childDi.deregister(...idsToInjectables(idsToRemove, previousInjectables));
- }
-
- if (idsToAdd.length > 0) {
- childDi.register(...idsToInjectables(idsToAdd, currentInjectables));
- }
- }, {
+ if (Array.isArray(injectables)) {
+ runInAction(() => {
+ injectableDifferencingRegistrator(injectables);
+ });
+ } else {
+ reactionDisposer.push(reaction(
+ () => injectables.get(),
+ injectableDifferencingRegistrator,
+ {
fireImmediately: true,
},
));
- });
+ }
+ }
},
deregister: () => {
diff --git a/packages/core/src/features/extension-special-characters-in-page-registrations.test.tsx b/packages/core/src/features/extension-special-characters-in-page-registrations.test.tsx
index ac038a5743..b9760eba79 100644
--- a/packages/core/src/features/extension-special-characters-in-page-registrations.test.tsx
+++ b/packages/core/src/features/extension-special-characters-in-page-registrations.test.tsx
@@ -31,8 +31,7 @@ describe("extension special characters in page registrations", () => {
describe("when navigating to route with ID having special characters", () => {
beforeEach(() => {
- const testExtension =
- builder.extensions.get("some-extension-id").applicationWindows.only;
+ const testExtension = builder.extensions.get("some-extension-id").applicationWindows.only;
testExtension.navigate("/some-page-id/");
});
diff --git a/packages/core/src/renderer/before-frame-starts/runnables/setup-auto-crd-api-creations.injectable.ts b/packages/core/src/renderer/before-frame-starts/runnables/setup-auto-crd-api-creations.injectable.ts
new file mode 100644
index 0000000000..c23a34d767
--- /dev/null
+++ b/packages/core/src/renderer/before-frame-starts/runnables/setup-auto-crd-api-creations.injectable.ts
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { reaction } from "mobx";
+import { customResourceDefinitionApiInjectionToken } from "../../../common/k8s-api/api-manager/crd-api-token";
+import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
+import { KubeApi } from "../../../common/k8s-api/kube-api";
+import { KubeObject } from "../../../common/k8s-api/kube-object";
+import maybeKubeApiInjectable from "../../../common/k8s-api/maybe-kube-api.injectable";
+import loggerInjectable from "../../../common/logger.injectable";
+import { injectableDifferencingRegistratorWith } from "../../../common/utils/registrator-helper";
+import customResourceDefinitionStoreInjectable from "../../components/+custom-resources/definition.store.injectable";
+import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens";
+
+const setupAutoCrdApiCreationsInjectable = getInjectable({
+ id: "setup-auto-crd-api-creations",
+ instantiate: (di) => ({
+ run: () => {
+ const customResourceDefinitionStore = di.inject(customResourceDefinitionStoreInjectable);
+ const injectableDifferencingRegistrator = injectableDifferencingRegistratorWith(di);
+
+ reaction(
+ () => customResourceDefinitionStore.getItems().map(toCrdApiInjectable),
+ injectableDifferencingRegistrator,
+ {
+ fireImmediately: true,
+ },
+ );
+ },
+ }),
+ injectionToken: beforeClusterFrameStartsSecondInjectionToken,
+});
+
+export default setupAutoCrdApiCreationsInjectable;
+
+const toCrdApiInjectable = (crd: CustomResourceDefinition) => getInjectable({
+ id: `default-kube-api-for-custom-resource-definition-${crd.getResourceApiBase()}`,
+ instantiate: (di) => {
+ const objectConstructor = class extends KubeObject {
+ static readonly kind = crd.getResourceKind();
+ static readonly namespaced = crd.isNamespaced();
+ static readonly apiBase = crd.getResourceApiBase();
+ };
+
+ return new KubeApi({
+ logger: di.inject(loggerInjectable),
+ maybeKubeApi: di.inject(maybeKubeApiInjectable),
+ }, { objectConstructor });
+ },
+ injectionToken: customResourceDefinitionApiInjectionToken,
+});
diff --git a/packages/core/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts b/packages/core/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts
index dbc77a92b8..78b1f90015 100644
--- a/packages/core/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts
+++ b/packages/core/src/renderer/before-frame-starts/runnables/setup-auto-registration.injectable.ts
@@ -5,71 +5,22 @@
import { getInjectable } from "@ogre-tools/injectable";
import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
-import { CustomResourceStore } from "../../../common/k8s-api/api-manager/resource.store";
-import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
-import type { KubeApiDependencies } from "../../../common/k8s-api/kube-api";
-import { KubeApi } from "../../../common/k8s-api/kube-api";
-import { KubeObject } from "../../../common/k8s-api/kube-object";
+import type { KubeApi } from "../../../common/k8s-api/kube-api";
import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens";
-import type { KubeObjectStoreDependencies } from "../../../common/k8s-api/kube-object.store";
-import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
-import loggerInjectable from "../../../common/logger.injectable";
-import maybeKubeApiInjectable from "../../../common/k8s-api/maybe-kube-api.injectable";
const setupAutoRegistrationInjectable = getInjectable({
id: "setup-auto-registration",
instantiate: (di) => ({
run: () => {
const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
- const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = [];
const beforeApiManagerInitializationApis: KubeApi[] = [];
- const kubeApiDependencies: KubeApiDependencies = {
- logger: di.inject(loggerInjectable),
- maybeKubeApi: di.inject(maybeKubeApiInjectable),
- };
- const kubeObjectStoreDependencies: KubeObjectStoreDependencies = {
- context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
- logger: di.inject(loggerInjectable),
- };
let initialized = false;
- const autoInitCustomResourceStore = (crd: CustomResourceDefinition) => {
- const objectConstructor = class extends KubeObject {
- static readonly kind = crd.getResourceKind();
- static readonly namespaced = crd.isNamespaced();
- static readonly apiBase = crd.getResourceApiBase();
- };
-
- const api = (() => {
- const rawApi = apiManager.getApi(objectConstructor.apiBase);
-
- if (rawApi) {
- return rawApi;
- }
-
- const api = new KubeApi(kubeApiDependencies, { objectConstructor });
-
- apiManager.registerApi(api);
-
- return api;
- })();
-
- if (!apiManager.getStore(api)) {
- apiManager.registerStore(new CustomResourceStore(kubeObjectStoreDependencies, api));
- }
- };
const autoInitKubeApi = (api: KubeApi) => {
apiManager.registerApi(api);
};
autoRegistrationEmitter
- .on("customResourceDefinition", (crd) => {
- if (initialized) {
- autoInitCustomResourceStore(crd);
- } else {
- beforeApiManagerInitializationCrds.push(crd);
- }
- })
.on("kubeApi", (api) => {
if (initialized) {
autoInitKubeApi(api);
@@ -81,7 +32,6 @@ const setupAutoRegistrationInjectable = getInjectable({
// NOTE: this MUST happen after the event emitter listeners are registered
const apiManager = di.inject(apiManagerInjectable);
- beforeApiManagerInitializationCrds.forEach(autoInitCustomResourceStore);
beforeApiManagerInitializationApis.forEach(autoInitKubeApi);
initialized = true;
},
diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx
index 48c2183055..aa2d2a3b32 100644
--- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx
+++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx
@@ -4,8 +4,13 @@
*/
import type { RenderResult } from "@testing-library/react";
import React from "react";
+import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
+import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
+import { Cluster } from "../../../common/cluster/cluster";
import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints";
+import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
+import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import type { DiRender } from "../test-utils/renderFor";
import { renderFor } from "../test-utils/renderFor";
import { HpaDetails } from "./hpa-details";
@@ -41,6 +46,17 @@ describe("", () => {
beforeEach(() => {
const di = getDiForUnitTesting();
+ di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
+ di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
+ di.override(storesAndApisCanBeCreatedInjectable, () => true);
+ di.override(hostedClusterInjectable, () => new Cluster({
+ contextName: "some-context-name",
+ id: "some-cluster-id",
+ kubeConfigPath: "/some-path-to-a-kubeconfig",
+ }, {
+ clusterServerUrl: "https://localhost:8080",
+ }));
+
render = renderFor(di);
});
diff --git a/packages/core/src/renderer/components/+custom-resources/definition.store.injectable.ts b/packages/core/src/renderer/components/+custom-resources/definition.store.injectable.ts
index f1d8f635b8..dbbd2460aa 100644
--- a/packages/core/src/renderer/components/+custom-resources/definition.store.injectable.ts
+++ b/packages/core/src/renderer/components/+custom-resources/definition.store.injectable.ts
@@ -4,7 +4,6 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
-import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import customResourceDefinitionApiInjectable from "../../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
@@ -20,7 +19,6 @@ const customResourceDefinitionStoreInjectable = getInjectable({
const api = di.inject(customResourceDefinitionApiInjectable);
return new CustomResourceDefinitionStore({
- autoRegistration: di.inject(autoRegistrationEmitterInjectable),
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
diff --git a/packages/core/src/renderer/components/+custom-resources/definition.store.ts b/packages/core/src/renderer/components/+custom-resources/definition.store.ts
index 310e51709a..78ef3982a9 100644
--- a/packages/core/src/renderer/components/+custom-resources/definition.store.ts
+++ b/packages/core/src/renderer/components/+custom-resources/definition.store.ts
@@ -3,37 +3,22 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import { computed, reaction, makeObservable } from "mobx";
+import { computed, makeObservable } from "mobx";
import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import type { CustomResourceDefinition, CustomResourceDefinitionApi } from "../../../common/k8s-api/endpoints/custom-resource-definition.api";
import type { KubeObject } from "../../../common/k8s-api/kube-object";
-import type TypedEventEmitter from "typed-emitter";
-import type { LegacyAutoRegistration } from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
import autoBind from "auto-bind";
-export interface CustomResourceDefinitionStoreDependencies extends KubeObjectStoreDependencies {
- readonly autoRegistration: TypedEventEmitter;
-}
-
export class CustomResourceDefinitionStore extends KubeObjectStore {
constructor(
- protected readonly dependencies: CustomResourceDefinitionStoreDependencies,
+ dependencies: KubeObjectStoreDependencies,
api: CustomResourceDefinitionApi,
opts?: KubeObjectStoreOptions,
) {
super(dependencies, api, opts);
makeObservable(this);
autoBind(this);
-
- reaction(
- () => this.getItems(),
- crds => {
- for (const crd of crds) {
- this.dependencies.autoRegistration.emit("customResourceDefinition", crd);
- }
- },
- );
}
protected sortItems(items: CustomResourceDefinition[]) {
diff --git a/packages/core/src/renderer/components/test-utils/get-application-builder.tsx b/packages/core/src/renderer/components/test-utils/get-application-builder.tsx
index 4ae3441e76..731c5ba8c7 100644
--- a/packages/core/src/renderer/components/test-utils/get-application-builder.tsx
+++ b/packages/core/src/renderer/components/test-utils/get-application-builder.tsx
@@ -53,7 +53,7 @@ import { applicationWindowInjectionToken } from "../../../main/start-main-applic
import closeAllWindowsInjectable from "../../../main/start-main-application/lens-window/hide-all-windows/close-all-windows.injectable";
import type { LensWindow } from "../../../main/start-main-application/lens-window/application-window/create-lens-window.injectable";
import type { FakeExtensionOptions } from "./get-extension-fake";
-import { getExtensionFakeForMain, getExtensionFakeForRenderer } from "./get-extension-fake";
+import { getMainExtensionFakeWith, getRendererExtensionFakeWith } from "./get-extension-fake";
import namespaceApiInjectable from "../../../common/k8s-api/endpoints/namespace.api.injectable";
import { Namespace } from "../../../common/k8s-api/endpoints";
import { getOverrideFsWithFakes } from "../../../test-utils/override-fs-with-fakes";
@@ -594,49 +594,28 @@ export const getApplicationBuilder = () => {
},
enable: (...extensions) => {
- builder.afterWindowStart(({ windowDi }) => {
- const rendererExtensionInstances = extensions.map((options) =>
- getExtensionFakeForRenderer(
- windowDi,
- options.id,
- options.name,
- options.rendererOptions || {},
- ),
- );
+ builder.afterWindowStart(action(({ windowDi }) => {
+ extensions
+ .map(getRendererExtensionFakeWith(windowDi))
+ .forEach(enableExtensionFor(windowDi, rendererExtensionsStateInjectable));
+ }));
- rendererExtensionInstances.forEach(
- enableExtensionFor(windowDi, rendererExtensionsStateInjectable),
- );
- });
-
- builder.afterApplicationStart(({ mainDi }) => {
- const mainExtensionInstances = extensions.map((extension) =>
- getExtensionFakeForMain(mainDi, extension.id, extension.name, extension.mainOptions || {}),
- );
-
- runInAction(() => {
- mainExtensionInstances.forEach(
- enableExtensionFor(mainDi, mainExtensionsStateInjectable),
- );
- });
- });
+ builder.afterApplicationStart(action(({ mainDi }) => {
+ extensions
+ .map(getMainExtensionFakeWith(mainDi))
+ .forEach(enableExtensionFor(mainDi, mainExtensionsStateInjectable));
+ }));
},
disable: (...extensions) => {
builder.afterWindowStart(({ windowDi }) => {
extensions
- .map((ext) => ext.id)
- .forEach(
- disableExtensionFor(windowDi, rendererExtensionsStateInjectable),
- );
+ .forEach(disableExtensionFor(windowDi, rendererExtensionsStateInjectable));
});
builder.afterApplicationStart(({ mainDi }) => {
extensions
- .map((ext) => ext.id)
- .forEach(
- disableExtensionFor(mainDi, mainExtensionsStateInjectable),
- );
+ .forEach(disableExtensionFor(mainDi, mainExtensionsStateInjectable));
});
},
},
@@ -835,49 +814,29 @@ const selectOptionFor = (builder: ApplicationBuilder, menuId: string) => (labelT
userEvent.click(option);
};
-const enableExtensionFor = (
- di: DiContainer,
- stateInjectable: Injectable, any, any>,
-) => {
+function enableExtensionFor(di: DiContainer, stateInjectable: Injectable, any, any>) {
const extensionState = di.inject(stateInjectable);
- const getExtension = (extension: LensExtension) =>
- di.inject(extensionInjectable, extension);
+ return (instance: LensExtension) => {
+ const extension = di.inject(extensionInjectable, instance);
- return (extensionInstance: LensExtension) => {
- const extension = getExtension(extensionInstance);
-
- runInAction(() => {
- extension.register();
- extensionState.set(extensionInstance.id, extensionInstance);
- });
+ extension.register();
+ extensionState.set(instance.id, instance);
};
-};
+}
-const disableExtensionFor =
- (
- di: DiContainer,
- stateInjectable: Injectable, unknown, void>,
- ) =>
- (id: string) => {
- const getExtension = (extension: LensExtension) =>
- di.inject(extensionInjectable, extension);
+function disableExtensionFor(di: DiContainer, stateInjectable: Injectable, unknown, void>) {
+ return (extension: FakeExtensionOptions) => {
+ const extensionsState = di.inject(stateInjectable);
+ const instance = extensionsState.get(extension.id);
- const extensionsState = di.inject(stateInjectable);
+ if (!instance) {
+ throw new Error(`Tried to disable extension with ID "${extension.id}", but it wasn't enabled`);
+ }
- const instance = extensionsState.get(id);
+ const injectable = di.inject(extensionInjectable, instance);
- if (!instance) {
- throw new Error(
- `Tried to disable extension with ID "${id}", but it wasn't enabled`,
- );
- }
-
- const injectable = getExtension(instance);
-
- runInAction(() => {
- injectable.deregister();
-
- extensionsState.delete(id);
- });
- };
+ injectable.deregister();
+ extensionsState.delete(extension.id);
+ };
+}
diff --git a/packages/core/src/renderer/components/test-utils/get-extension-fake.ts b/packages/core/src/renderer/components/test-utils/get-extension-fake.ts
index 8079207daa..675b7e93e2 100644
--- a/packages/core/src/renderer/components/test-utils/get-extension-fake.ts
+++ b/packages/core/src/renderer/components/test-utils/get-extension-fake.ts
@@ -27,7 +27,7 @@ export interface FakeExtensionOptions {
mainOptions?: Partial;
}
-export const getExtensionFakeForMain = (di: DiContainer, id: string, name: string, options: Partial) => {
+export const getMainExtensionFakeWith = (di: DiContainer) => ({ id, name, mainOptions = {}}: FakeExtensionOptions) => {
const instance = new TestExtensionMain({
id,
absolutePath: "irrelevant",
@@ -44,7 +44,7 @@ export const getExtensionFakeForMain = (di: DiContainer, id: string, name: strin
manifestPath: "irrelevant",
});
- Object.assign(instance, options);
+ Object.assign(instance, mainOptions);
(instance as Writable)[lensExtensionDependencies] = {
fileSystemProvisionerStore: di.inject(fileSystemProvisionerStoreInjectable),
@@ -56,7 +56,7 @@ export const getExtensionFakeForMain = (di: DiContainer, id: string, name: strin
return instance;
};
-export const getExtensionFakeForRenderer = (di: DiContainer, id: string, name: string, options: Partial) => {
+export const getRendererExtensionFakeWith = (di: DiContainer) => ({ id, name, rendererOptions = {}}: FakeExtensionOptions) => {
const instance = new TestExtensionRenderer({
id,
absolutePath: "irrelevant",
@@ -73,7 +73,7 @@ export const getExtensionFakeForRenderer = (di: DiContainer, id: string, name: s
manifestPath: "irrelevant",
});
- Object.assign(instance, options);
+ Object.assign(instance, rendererOptions);
(instance as Writable)[lensExtensionDependencies] = {
categoryRegistry: di.inject(catalogCategoryRegistryInjectable),
From 4a3f08d798dd535b6b6e1749f562c38a715b7acd Mon Sep 17 00:00:00 2001
From: Alex Andreev
Date: Thu, 23 Mar 2023 11:37:10 +0300
Subject: [PATCH 7/9] Fix Check for updates menu item title capitalisation
(#7396)
* Check for updates -> Check for Updates
Signed-off-by: Alex Andreev
* Revert debugging changes
Signed-off-by: Alex Andreev
* Adding ellipsis to every Check for Updates items
Signed-off-by: Alex Andreev
---------
Signed-off-by: Alex Andreev
---
.../main/check-for-updates-menu-item.injectable.ts | 2 +-
.../installing-update-using-tray.test.ts | 8 ++++----
.../tray-items/check-for-updates-tray-item.injectable.ts | 4 ++--
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/packages/core/src/features/application-update/child-features/application-update-using-application-menu/main/check-for-updates-menu-item.injectable.ts b/packages/core/src/features/application-update/child-features/application-update-using-application-menu/main/check-for-updates-menu-item.injectable.ts
index fe997e62fb..97b9acf9f8 100644
--- a/packages/core/src/features/application-update/child-features/application-update-using-application-menu/main/check-for-updates-menu-item.injectable.ts
+++ b/packages/core/src/features/application-update/child-features/application-update-using-application-menu/main/check-for-updates-menu-item.injectable.ts
@@ -28,7 +28,7 @@ const checkForUpdatesMenuItemInjectable = getInjectable({
id: "check-for-updates",
parentId: isMac ? "mac" : "help",
orderNumber: isMac ? 20 : 50,
- label: "Check for updates",
+ label: "Check for Updates...",
isShown: updatingIsEnabled,
onClick: () => {
diff --git a/packages/core/src/features/application-update/child-features/application-update-using-tray/installing-update-using-tray.test.ts b/packages/core/src/features/application-update/child-features/application-update-using-tray/installing-update-using-tray.test.ts
index d5caa63149..f18f288469 100644
--- a/packages/core/src/features/application-update/child-features/application-update-using-tray/installing-update-using-tray.test.ts
+++ b/packages/core/src/features/application-update/child-features/application-update-using-tray/installing-update-using-tray.test.ts
@@ -146,7 +146,7 @@ describe("installing update using tray", () => {
it("name of tray item for checking updates indicates that checking is happening", () => {
expect(
builder.tray.get("check-for-updates")?.label,
- ).toBe("Checking for updates...");
+ ).toBe("Checking for Updates...");
});
it("user cannot install update yet", () => {
@@ -177,7 +177,7 @@ describe("installing update using tray", () => {
it("name of tray item for checking updates no longer indicates that checking is happening", () => {
expect(
builder.tray.get("check-for-updates")?.label,
- ).toBe("Check for updates");
+ ).toBe("Check for Updates...");
});
it("renders", () => {
@@ -241,7 +241,7 @@ describe("installing update using tray", () => {
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
expect(
builder.tray.get("check-for-updates")?.label,
- ).toBe("Check for updates");
+ ).toBe("Check for Updates...");
});
it("renders", () => {
@@ -269,7 +269,7 @@ describe("installing update using tray", () => {
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
expect(
builder.tray.get("check-for-updates")?.label,
- ).toBe("Check for updates");
+ ).toBe("Check for Updates...");
});
it("renders", () => {
diff --git a/packages/core/src/features/application-update/child-features/application-update-using-tray/main/tray-items/check-for-updates-tray-item.injectable.ts b/packages/core/src/features/application-update/child-features/application-update-using-tray/main/tray-items/check-for-updates-tray-item.injectable.ts
index e904abaa99..3f05ddb71e 100644
--- a/packages/core/src/features/application-update/child-features/application-update-using-tray/main/tray-items/check-for-updates-tray-item.injectable.ts
+++ b/packages/core/src/features/application-update/child-features/application-update-using-tray/main/tray-items/check-for-updates-tray-item.injectable.ts
@@ -47,10 +47,10 @@ const checkForUpdatesTrayItemInjectable = getInjectable({
}
if (checkingForUpdatesState.value.get()) {
- return "Checking for updates...";
+ return "Checking for Updates...";
}
- return "Check for updates";
+ return "Check for Updates...";
}),
enabled: computed(() => !checkingForUpdatesState.value.get() && !downloadingUpdateState.value.get()),
From a0d13e80031c7424607b6c50135ec0e0a49df5ae Mon Sep 17 00:00:00 2001
From: Alex Andreev
Date: Thu, 23 Mar 2023 11:40:56 +0300
Subject: [PATCH 8/9] Adding package exporting injection tokens for cluster
settings (#7395)
* Add clusterIconSettingsComponentInjectionToken
Signed-off-by: Alex Andreev
* Adding tests for new token
Signed-off-by: Alex Andreev
* Create cluster-settings package
Signed-off-by: Alex Andreev
* Fix cluster-settings package build process
Signed-off-by: Alex Andreev
* Use tolens from @k8slens/cluster-settings
Signed-off-by: Alex Andreev
* Linter fixes
Signed-off-by: Alex Andreev
* Remove typescript 5.x.x from cluster-settings package
Signed-off-by: Alex Andreev
* Adding prepare:test command to package.json
Signed-off-by: Alex Andreev
* Test small cleanup
Signed-off-by: Alex Andreev
* Fix getByText check
Signed-off-by: Alex Andreev
* Clean up
Signed-off-by: Alex Andreev
* Linter fix
Signed-off-by: Alex Andreev
---------
Signed-off-by: Alex Andreev
---
package-lock.json | 24 ++++++++
packages/cluster-settings/.swcrc | 9 +++
packages/cluster-settings/README.md | 3 +
packages/cluster-settings/package.json | 31 ++++++++++
packages/cluster-settings/src/index.ts | 30 ++++++++++
packages/cluster-settings/tsconfig.json | 18 ++++++
packages/core/package.json | 1 +
.../__snapshots__/icon-settings.test.tsx.snap | 52 ++++++++++++++++
.../__tests__/icon-settings.test.tsx | 59 ++++++++++++++++++-
...ter-settings-menu-clear-item.injectable.ts | 2 +-
.../cluster-settings-menu-injection-token.ts | 17 ------
.../cluster-settings/icon-settings.tsx | 14 ++++-
12 files changed, 237 insertions(+), 23 deletions(-)
create mode 100644 packages/cluster-settings/.swcrc
create mode 100644 packages/cluster-settings/README.md
create mode 100644 packages/cluster-settings/package.json
create mode 100644 packages/cluster-settings/src/index.ts
create mode 100644 packages/cluster-settings/tsconfig.json
delete mode 100644 packages/core/src/renderer/components/cluster-settings/cluster-settings-menu-injection-token.ts
diff --git a/package-lock.json b/package-lock.json
index d00efc567c..946b5f8168 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4524,6 +4524,10 @@
"resolved": "packages/bump-version-for-cron",
"link": true
},
+ "node_modules/@k8slens/cluster-settings": {
+ "resolved": "packages/cluster-settings",
+ "link": true
+ },
"node_modules/@k8slens/computed-channel": {
"resolved": "packages/technical-features/messaging/computed-channel",
"link": true
@@ -34142,6 +34146,25 @@
"integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==",
"dev": true
},
+ "packages/cluster-settings": {
+ "name": "@k8slens/cluster-settings",
+ "version": "6.5.0-alpha.1",
+ "license": "MIT",
+ "devDependencies": {
+ "@ogre-tools/injectable": "^15.1.2",
+ "@swc/cli": "^0.1.61",
+ "@swc/core": "^1.3.37",
+ "@types/node": "^16.18.11",
+ "@types/semver": "^7.3.13",
+ "rimraf": "^4.1.2"
+ }
+ },
+ "packages/cluster-settings/node_modules/@types/node": {
+ "version": "16.18.18",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz",
+ "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==",
+ "dev": true
+ },
"packages/core": {
"name": "@k8slens/core",
"version": "6.5.0-alpha.3",
@@ -34150,6 +34173,7 @@
"@astronautlabs/jsonpath": "^1.1.0",
"@hapi/call": "^9.0.1",
"@hapi/subtext": "^7.1.0",
+ "@k8slens/cluster-settings": "^6.5.0-alpha.1",
"@k8slens/node-fetch": "^6.5.0-alpha.1",
"@kubernetes/client-node": "^0.18.1",
"@material-ui/styles": "^4.11.5",
diff --git a/packages/cluster-settings/.swcrc b/packages/cluster-settings/.swcrc
new file mode 100644
index 0000000000..8e7a530f16
--- /dev/null
+++ b/packages/cluster-settings/.swcrc
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://json.schemastore.org/swcrc",
+ "jsc": {
+ "parser": {
+ "syntax": "typescript"
+ },
+ "target": "es2022"
+ }
+}
diff --git a/packages/cluster-settings/README.md b/packages/cluster-settings/README.md
new file mode 100644
index 0000000000..c3d3b890f4
--- /dev/null
+++ b/packages/cluster-settings/README.md
@@ -0,0 +1,3 @@
+# Description
+
+The package exports tokens needed for external configuration of Cluster Settings page.
diff --git a/packages/cluster-settings/package.json b/packages/cluster-settings/package.json
new file mode 100644
index 0000000000..199347dcf4
--- /dev/null
+++ b/packages/cluster-settings/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "@k8slens/cluster-settings",
+ "version": "6.5.0-alpha.1",
+ "description": "Injection token exporter for cluster settings configuration",
+ "license": "MIT",
+ "private": false,
+ "mode": "production",
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org/"
+ },
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "clean": "rimraf dist/",
+ "generate-types": "tsc --d --declarationDir ./dist --declarationMap --emitDeclarationOnly",
+ "build": "npm run generate-types && swc ./src/index.ts -d ./dist",
+ "prepare:test": "npm run build"
+ },
+ "devDependencies": {
+ "@ogre-tools/injectable": "^15.1.2",
+ "@swc/cli": "^0.1.61",
+ "@swc/core": "^1.3.37",
+ "@types/node": "^16.18.11",
+ "@types/semver": "^7.3.13",
+ "rimraf": "^4.1.2"
+ }
+}
diff --git a/packages/cluster-settings/src/index.ts b/packages/cluster-settings/src/index.ts
new file mode 100644
index 0000000000..181c69d46a
--- /dev/null
+++ b/packages/cluster-settings/src/index.ts
@@ -0,0 +1,30 @@
+import { getInjectionToken } from "@ogre-tools/injectable";
+
+type ClusterPreferences = {
+ clusterName?: string;
+ icon?: string | null;
+}
+
+export interface ClusterIconMenuItem {
+ id: string;
+ title: string;
+ disabled?: (preferences: ClusterPreferences) => boolean;
+ onClick: (preferences: ClusterPreferences) => void;
+}
+
+export interface ClusterIconSettingComponentProps {
+ preferences: ClusterPreferences;
+}
+
+export interface ClusterIconSettingsComponent {
+ id: string;
+ Component: React.ComponentType;
+}
+
+export const clusterIconSettingsMenuInjectionToken = getInjectionToken({
+ id: "cluster-icon-settings-menu-injection-token",
+});
+
+export const clusterIconSettingsComponentInjectionToken = getInjectionToken({
+ id: "cluster-icon-settings-component-injection-token",
+});
\ No newline at end of file
diff --git a/packages/cluster-settings/tsconfig.json b/packages/cluster-settings/tsconfig.json
new file mode 100644
index 0000000000..534a5fd447
--- /dev/null
+++ b/packages/cluster-settings/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist/",
+ "paths": {
+ "*": [
+ "node_modules/*",
+ "types/*"
+ ]
+ },
+ },
+ "include": [
+ "src/**/*",
+ ],
+ "exclude": [
+ "node_modules",
+ ]
+}
diff --git a/packages/core/package.json b/packages/core/package.json
index 42c39d6181..ad6e7e45ae 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -120,6 +120,7 @@
"@astronautlabs/jsonpath": "^1.1.0",
"@hapi/call": "^9.0.1",
"@hapi/subtext": "^7.1.0",
+ "@k8slens/cluster-settings": "^6.5.0-alpha.1",
"@k8slens/node-fetch": "^6.5.0-alpha.1",
"@kubernetes/client-node": "^0.18.1",
"@material-ui/styles": "^4.11.5",
diff --git a/packages/core/src/renderer/components/cluster-settings/__tests__/__snapshots__/icon-settings.test.tsx.snap b/packages/core/src/renderer/components/cluster-settings/__tests__/__snapshots__/icon-settings.test.tsx.snap
index 97eac8fca2..6c22436089 100644
--- a/packages/core/src/renderer/components/cluster-settings/__tests__/__snapshots__/icon-settings.test.tsx.snap
+++ b/packages/core/src/renderer/components/cluster-settings/__tests__/__snapshots__/icon-settings.test.tsx.snap
@@ -51,3 +51,55 @@ exports[`Icon settings given no external registrations for cluster settings menu
+
+