mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Make starting of application modular and unit testable (#5324)
* Introduce injection token as competition for injectable setup to have better control of timing of different setups Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition to setup app paths Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for setupping IPC channel listeners Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Stop running setups in unit tests without need Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to running injection token based setups over legacy DI setups in unit tests Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate naming for running setups Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to running injection token based setups over legacy DI setups in application roots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt to typing changes in injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce concept of runnable as a way to delegate runs to Open Closed Principle compliant runnables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Deprecate vars in favor of injectables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate loading of extensions to injectable instead of setup-code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Flag injectable causing side effects Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt injectable to auto register using a plugin instead of internal feature that no longer exists Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Simplify late registrations Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce tokens for runnables of specific application events for Open Closed Principle Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate setup events to more granular timeslots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for catalog syncing using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for application menu using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for electron application name using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for immer using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for MobX strictness using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for application proxy using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for system certifications using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for system shutdown using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for main window visibility using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for application quit using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for after application is ready using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for application tray using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for deep linking using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reimplement setup for root frame using runnables instead of non-OCP in index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove multiple usages of shared global state Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove recently reimplemented stuff from index.ts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract implementation for intent over technical phenomena (here electron events) Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate naming of event timing window Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Move directories for timing windows among peers Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate event naming Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce function to remove duplication from things that are startable and stoppable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate implementation of something startable and stoppable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate naming Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate more startables and stoppables Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce abstractions for electron application events Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce more abstractions for electron Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Abstract even more Electron specifics to make them overridable in unit tests Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Override dependency for causing side effects Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make running of many delegatees able to be hierarchical Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Bump async-fn Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make startable-stoppable also restartable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract "isIntegrationTesting" as dependency for having a side-effect Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make temporal dependency apparent Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate all setupping related to single feature to same runnable Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Abstract command line arguments to make them overridable in tests Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Deprecate some globals Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce injectable for __static directory to eliminate side effect on import Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract responsibilities from electron Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract async initialization from sync-injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Accumulate more global overrides Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make lifecycle of injectable store work as expected Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract responsibilities to delegatees Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Rename delegate for accuracy Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Rename another delegate for accuracy Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Rename yet another delegate for accuracy Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add new global overrides Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate variable naming Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Rename injectable for accuracy Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make difference between soft and hard quit of application apparent Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make quit triggered by auto update manifest as hard quit instead of just soft quit Soft quit is the stand-by quit where only the renderer quits. Hard quit is the full quit of also main. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce abstraction for publishing and subscribing between processes Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Revert "Introduce abstraction for publishing and subscribing between processes" This reverts commit 46f2d5a5f28bddcf5ffe124b1c590b19e7b9a15f. Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt code and unit tests to recent changes in application setup and event handling Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Group overrides by category Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Abstract event of application activation from electron Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Abstract event of device shutdown from electron Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate runnables related to Electron Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make startableStoppables have ID for better error logging Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make navigating to Helm Charts not blow up Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add general override for behavioural tests Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshot Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt vars after merge Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix code style Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix error about multiple states for React Router being present at once Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Kludge around test setup which is difficult because of circular dependencies Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix setupping of sentry after rebase Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Ensure that LensProxy is setupped before starting the main window Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant import Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate to use injectable instead of var for static file directory Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Deprecate another var with injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak timing of runnables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract tray icon path as injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make splash screen not blow up by providing compile-time environment variables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce a way to run many synchronous runnables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make runnables for before application is ready synchronous as this is technical limitation of Electron See: https://github.com/electron/electron/issues/21370 Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Kill dead code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix application quit by calling Electron's event.preventDefault in a very specific way and preventing async Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix missing injectable postfix in file name Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak timeslot of a runnable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make it possible to not stop something that was never started Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make quit of hidden Lens not blow up Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract responsibilities of WindowManager as injectables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate code for application window Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Simplify directory structures for runnables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Flag injectable causing side-effect Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract helpers for testing promises to test-utils Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make script for running unit tests support targeting Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make startable stoppable support async starting Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce reactive way to get theme from operating system Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update yarn.lock after rebase Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix code style Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove code-style changes that are not welcome Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using dependency over using explicit side-effect Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Simplify naming Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Kill dead code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add empty mocks comply to lint Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove global state Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract responsibility of setting dependencies to own injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Flag injectable causing side effects Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract responsibility to injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak typing Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak naming of runnables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Revert code-style changes Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak types Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract injectable with side-effect for exact overriding Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak name of timeslot to make it more apparent that new content for application load can be added Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak name of the splash window for loading to avoid confusion with similarly named phenomenon Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak comment Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Rename injectable for brevity Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate LensProxy related stuff to directory Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Rename injectables for accuracy Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove usage of extension for injectable for being YAGNI Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Prevent restarting a startableStoppable when already being started Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Rename callback in ApplicationBuilder for accuracy Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix conflicts after rebase Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix merge conflicts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Revert switch to react-router-dom by installing history as dependency Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make test more strict Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make runnable give friendly error about incorrect hierarchy for easier debugging Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix code-style Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix code style Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using dependency instead of legacy-di Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix timing of injects Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> Co-authored-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
1 parent
b57d48e39e
commit
54b92aa9b6
302 files changed
+6388
-2025
No files matched your search
@@ -19,6 +19,8 @@ import listNamespacesInjectable from "../../common/cluster/list-namespaces.injec
|
||||
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
|
||||
import type { ClusterContextHandler } from "../context-handler/context-handler";
|
||||
import { parse } from "url";
|
||||
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";
|
||||
|
||||
console = new Console(process.stdout, process.stderr); // fix mockFS
|
||||
|
||||
@@ -26,7 +28,7 @@ describe("create clusters", () => {
|
||||
let cluster: Cluster;
|
||||
let createCluster: (model: ClusterModel) => Cluster;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
@@ -55,8 +57,8 @@ describe("create clusters", () => {
|
||||
}),
|
||||
});
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
|
||||
di.override(authorizationReviewInjectable, () => () => () => Promise.resolve(true));
|
||||
di.override(listNamespacesInjectable, () => () => () => Promise.resolve([ "default" ]));
|
||||
di.override(createContextHandlerInjectable, () => (cluster) => ({
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
import { UserStore } from "../../common/user-store";
|
||||
import type { ClusterContextHandler } from "../context-handler/context-handler";
|
||||
import type { PrometheusService } from "../prometheus";
|
||||
import { PrometheusProvider, PrometheusProviderRegistry } from "../prometheus";
|
||||
import type { PrometheusService, PrometheusProviderRegistry } from "../prometheus";
|
||||
import { PrometheusProvider } from "../prometheus";
|
||||
import mockFs from "mock-fs";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||
import prometheusProviderRegistryInjectable from "../prometheus/prometheus-provider-registry.injectable";
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
@@ -74,8 +75,9 @@ const clusterStub = {
|
||||
|
||||
describe("ContextHandler", () => {
|
||||
let createContextHandler: (cluster: Cluster) => ClusterContextHandler | undefined;
|
||||
let prometheusProviderRegistry: PrometheusProviderRegistry;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
mockFs({
|
||||
@@ -84,15 +86,12 @@ describe("ContextHandler", () => {
|
||||
|
||||
di.override(createKubeAuthProxyInjectable, () => ({} as any));
|
||||
|
||||
await di.runSetups();
|
||||
prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable);
|
||||
|
||||
createContextHandler = di.inject(createContextHandlerInjectable);
|
||||
|
||||
PrometheusProviderRegistry.createInstance();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
PrometheusProviderRegistry.resetInstance();
|
||||
UserStore.resetInstance();
|
||||
mockFs.restore();
|
||||
});
|
||||
@@ -104,17 +103,16 @@ describe("ContextHandler", () => {
|
||||
[0, 2],
|
||||
[0, 3],
|
||||
])("should throw from %d success(es) after %d failure(s)", async (successes, failures) => {
|
||||
const reg = PrometheusProviderRegistry.getInstance();
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < failures; i += 1) {
|
||||
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
||||
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||
}
|
||||
|
||||
for (let i = 0; i < successes; i += 1) {
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
@@ -135,17 +133,16 @@ describe("ContextHandler", () => {
|
||||
[2, 2],
|
||||
[2, 3],
|
||||
])("should pick the first provider of %d success(es) after %d failure(s)", async (successes, failures) => {
|
||||
const reg = PrometheusProviderRegistry.getInstance();
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < failures; i += 1) {
|
||||
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
||||
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||
}
|
||||
|
||||
for (let i = 0; i < successes; i += 1) {
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
}
|
||||
|
||||
// TODO: Unit test shouldn't access protected or private methods
|
||||
@@ -166,17 +163,16 @@ describe("ContextHandler", () => {
|
||||
[2, 2],
|
||||
[2, 3],
|
||||
])("should pick the first provider of %d success(es) before %d failure(s)", async (successes, failures) => {
|
||||
const reg = PrometheusProviderRegistry.getInstance();
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < successes; i += 1) {
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
}
|
||||
|
||||
for (let i = 0; i < failures; i += 1) {
|
||||
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
||||
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||
}
|
||||
|
||||
// TODO: Unit test shouldn't access protected or private methods
|
||||
@@ -197,23 +193,22 @@ describe("ContextHandler", () => {
|
||||
[2, 2],
|
||||
[2, 3],
|
||||
])("should pick the first provider of %d success(es) between %d failure(s)", async (successes, failures) => {
|
||||
const reg = PrometheusProviderRegistry.getInstance();
|
||||
let count = 0;
|
||||
const beforeSuccesses = Math.floor(successes / 2);
|
||||
const afterSuccesses = successes - beforeSuccesses;
|
||||
|
||||
for (let i = 0; i < beforeSuccesses; i += 1) {
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
}
|
||||
|
||||
for (let i = 0; i < failures; i += 1) {
|
||||
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
||||
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||
}
|
||||
|
||||
for (let i = 0; i < afterSuccesses; i += 1) {
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
}
|
||||
|
||||
// TODO: Unit test shouldn't access protected or private methods
|
||||
@@ -225,12 +220,11 @@ describe("ContextHandler", () => {
|
||||
});
|
||||
|
||||
it("shouldn't pick the second provider of 2 success(es) after 1 failure(s)", async () => {
|
||||
const reg = PrometheusProviderRegistry.getInstance();
|
||||
let count = 0;
|
||||
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Failure));
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Failure));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||
|
||||
// TODO: Unit test shouldn't access protected or private methods
|
||||
const contextHandler = createContextHandler(clusterStub) as unknown as { getPrometheusService(): Promise<PrometheusService> };
|
||||
|
||||
@@ -57,6 +57,8 @@ import path from "path";
|
||||
import spawnInjectable from "../child-process/spawn.injectable";
|
||||
import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||
import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.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";
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
@@ -99,6 +101,9 @@ describe("kube auth proxy tests", () => {
|
||||
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
|
||||
|
||||
di.override(spawnInjectable, () => mockSpawn);
|
||||
|
||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||
@@ -106,8 +111,6 @@ describe("kube auth proxy tests", () => {
|
||||
|
||||
mockFs(mockMinikubeConfig);
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
createCluster = di.inject(createClusterInjectionToken);
|
||||
|
||||
createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import { KubeconfigManager } from "../kubeconfig-manager/kubeconfig-manager";
|
||||
import mockFs from "mock-fs";
|
||||
@@ -20,6 +19,7 @@ import { parse } from "url";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import type { Logger } from "../../common/logger";
|
||||
import assert from "assert";
|
||||
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
|
||||
console = new Console(process.stdout, process.stderr); // fix mockFS
|
||||
|
||||
@@ -33,6 +33,7 @@ describe("kubeconfig manager tests", () => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
|
||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||
|
||||
loggerMock = {
|
||||
warn: jest.fn(),
|
||||
@@ -68,8 +69,6 @@ describe("kubeconfig manager tests", () => {
|
||||
}),
|
||||
});
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
di.override(createContextHandlerInjectable, () => (cluster) => ({
|
||||
restartServer: jest.fn(),
|
||||
stopServer: jest.fn(),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { isLongRunningRequest } from "../lens-proxy";
|
||||
import { isLongRunningRequest } from "../lens-proxy/lens-proxy";
|
||||
|
||||
describe("isLongRunningRequest", () => {
|
||||
it("returns true on watches", () => {
|
||||
|
||||
@@ -26,11 +26,9 @@ jest.mock("electron", () => ({
|
||||
describe("static-file-route", () => {
|
||||
let handleStaticFileRoute: Route<Buffer, "/{path*}">;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
handleStaticFileRoute = di.inject(staticFileRouteInjectable);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,11 +3,19 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import electronAppInjectable from "../get-electron-app-path/electron-app/electron-app.injectable";
|
||||
import packageInfo from "../../../../package.json";
|
||||
import isDevelopmentInjectable from "../../../common/vars/is-development.injectable";
|
||||
|
||||
const appNameInjectable = getInjectable({
|
||||
id: "app-name",
|
||||
instantiate: (di) => di.inject(electronAppInjectable).name,
|
||||
|
||||
instantiate: (di) => {
|
||||
const isDevelopment = di.inject(isDevelopmentInjectable);
|
||||
|
||||
return `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default appNameInjectable;
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type {
|
||||
DiContainerForSetup } from "@ogre-tools/injectable";
|
||||
import {
|
||||
getInjectable,
|
||||
} from "@ogre-tools/injectable";
|
||||
|
||||
import {
|
||||
appPathsInjectionToken,
|
||||
appPathsIpcChannel,
|
||||
} from "../../common/app-paths/app-path-injection-token";
|
||||
|
||||
import registerChannelInjectable from "./register-channel/register-channel.injectable";
|
||||
import { getAppPaths } from "./get-app-paths";
|
||||
import getElectronAppPathInjectable from "./get-electron-app-path/get-electron-app-path.injectable";
|
||||
import setElectronAppPathInjectable from "./set-electron-app-path/set-electron-app-path.injectable";
|
||||
import appNameInjectable from "./app-name/app-name.injectable";
|
||||
import directoryForIntegrationTestingInjectable from "./directory-for-integration-testing/directory-for-integration-testing.injectable";
|
||||
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
||||
|
||||
const appPathsInjectable = getInjectable({
|
||||
id: "app-paths",
|
||||
|
||||
setup: async (di) => {
|
||||
const directoryForIntegrationTesting = await di.inject(
|
||||
directoryForIntegrationTestingInjectable,
|
||||
);
|
||||
|
||||
if (directoryForIntegrationTesting) {
|
||||
await setupPathForAppDataInIntegrationTesting(di, directoryForIntegrationTesting);
|
||||
}
|
||||
|
||||
await setupPathForUserData(di);
|
||||
await registerAppPathsChannel(di);
|
||||
},
|
||||
|
||||
instantiate: (di) =>
|
||||
getAppPaths({ getAppPath: di.inject(getElectronAppPathInjectable) }),
|
||||
|
||||
injectionToken: appPathsInjectionToken,
|
||||
});
|
||||
|
||||
export default appPathsInjectable;
|
||||
|
||||
const registerAppPathsChannel = async (di: DiContainerForSetup) => {
|
||||
const registerChannel = await di.inject(registerChannelInjectable);
|
||||
const appPaths = await di.inject(appPathsInjectable);
|
||||
|
||||
registerChannel(appPathsIpcChannel, () => appPaths);
|
||||
};
|
||||
|
||||
const setupPathForUserData = async (di: DiContainerForSetup) => {
|
||||
const setElectronAppPath = await di.inject(setElectronAppPathInjectable);
|
||||
const appName = await di.inject(appNameInjectable);
|
||||
const getAppPath = await di.inject(getElectronAppPathInjectable);
|
||||
const joinPaths = await di.inject(joinPathsInjectable);
|
||||
|
||||
const appDataPath = getAppPath("appData");
|
||||
|
||||
setElectronAppPath("userData", joinPaths(appDataPath, appName));
|
||||
};
|
||||
|
||||
// Todo: this kludge is here only until we have a proper place to setup integration testing.
|
||||
const setupPathForAppDataInIntegrationTesting = async (di: DiContainerForSetup, appDataPath: string) => {
|
||||
const setElectronAppPath = await di.inject(setElectronAppPathInjectable);
|
||||
|
||||
setElectronAppPath("appData", appDataPath);
|
||||
};
|
||||
+7
-1
@@ -3,10 +3,16 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import environmentVariablesInjectable from "../../../common/utils/environment-variables.injectable";
|
||||
|
||||
const directoryForIntegrationTestingInjectable = getInjectable({
|
||||
id: "directory-for-integration-testing",
|
||||
instantiate: () => process.env.CICD,
|
||||
|
||||
instantiate: (di) => {
|
||||
const environmentVariables = di.inject(environmentVariablesInjectable);
|
||||
|
||||
return environmentVariables.CICD;
|
||||
},
|
||||
});
|
||||
|
||||
export default directoryForIntegrationTestingInjectable;
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import electronAppInjectable from "./electron-app/electron-app.injectable";
|
||||
import electronAppInjectable from "../../electron-app/electron-app.injectable";
|
||||
import { getElectronAppPath } from "./get-electron-app-path";
|
||||
|
||||
const getElectronAppPathInjectable = getInjectable({
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import electronAppInjectable from "./electron-app/electron-app.injectable";
|
||||
import electronAppInjectable from "../../electron-app/electron-app.injectable";
|
||||
import getElectronAppPathInjectable from "./get-electron-app-path.injectable";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import type { App } from "electron";
|
||||
@@ -13,7 +13,7 @@ import { joinPathsFake } from "../../../common/test-utils/join-paths-fake";
|
||||
describe("get-electron-app-path", () => {
|
||||
let getElectronAppPath: (name: string) => string;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: false });
|
||||
|
||||
const appStub = {
|
||||
@@ -35,8 +35,6 @@ describe("get-electron-app-path", () => {
|
||||
di.override(registerChannelInjectable, () => () => undefined);
|
||||
di.override(joinPathsInjectable, () => joinPathsFake);
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
getElectronAppPath = di.inject(getElectronAppPathInjectable) as (name: string) => string;
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { PathName } from "../../../common/app-paths/app-path-names";
|
||||
import electronAppInjectable from "../get-electron-app-path/electron-app/electron-app.injectable";
|
||||
import electronAppInjectable from "../../electron-app/electron-app.injectable";
|
||||
|
||||
const setElectronAppPathInjectable = getInjectable({
|
||||
id: "set-electron-app-path",
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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 type { AppPaths } from "../../common/app-paths/app-path-injection-token";
|
||||
import getElectronAppPathInjectable from "./get-electron-app-path/get-electron-app-path.injectable";
|
||||
import setElectronAppPathInjectable from "./set-electron-app-path/set-electron-app-path.injectable";
|
||||
import appNameInjectable from "./app-name/app-name.injectable";
|
||||
import directoryForIntegrationTestingInjectable from "./directory-for-integration-testing/directory-for-integration-testing.injectable";
|
||||
import appPathsStateInjectable from "../../common/app-paths/app-paths-state.injectable";
|
||||
import { pathNames } from "../../common/app-paths/app-path-names";
|
||||
import { fromPairs, map } from "lodash/fp";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { appPathsIpcChannel } from "../../common/app-paths/app-path-injection-token";
|
||||
import registerChannelInjectable from "./register-channel/register-channel.injectable";
|
||||
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
||||
import { beforeElectronIsReadyInjectionToken } from "../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
|
||||
|
||||
const setupAppPathsInjectable = getInjectable({
|
||||
id: "setup-app-paths",
|
||||
|
||||
instantiate: (di) => {
|
||||
const setElectronAppPath = di.inject(setElectronAppPathInjectable);
|
||||
const appName = di.inject(appNameInjectable);
|
||||
const getAppPath = di.inject(getElectronAppPathInjectable);
|
||||
const appPathsState = di.inject(appPathsStateInjectable);
|
||||
const registerChannel = di.inject(registerChannelInjectable);
|
||||
const directoryForIntegrationTesting = di.inject(directoryForIntegrationTestingInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
if (directoryForIntegrationTesting) {
|
||||
setElectronAppPath("appData", directoryForIntegrationTesting);
|
||||
}
|
||||
|
||||
const appDataPath = getAppPath("appData");
|
||||
|
||||
setElectronAppPath("userData", joinPaths(appDataPath, appName));
|
||||
|
||||
const appPaths = pipeline(
|
||||
pathNames,
|
||||
map(name => [name, getAppPath(name)]),
|
||||
fromPairs,
|
||||
) as AppPaths;
|
||||
|
||||
appPathsState.set(appPaths);
|
||||
|
||||
registerChannel(appPathsIpcChannel, () => appPaths);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeElectronIsReadyInjectionToken,
|
||||
});
|
||||
|
||||
export default setupAppPathsInjectable;
|
||||
@@ -41,11 +41,15 @@ autoUpdater.logger = {
|
||||
debug: message => logger.debug(`[AUTO-UPDATE]: electron-updater: %s`, message),
|
||||
};
|
||||
|
||||
interface Dependencies {
|
||||
isAutoUpdateEnabled: () => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* starts the automatic update checking
|
||||
* @param interval milliseconds between interval to check on, defaults to 2h
|
||||
*/
|
||||
export const startUpdateChecking = once(function (interval = 1000 * 60 * 60 * 2): void {
|
||||
export const startUpdateChecking = ({ isAutoUpdateEnabled } : Dependencies) => once(function (interval = 1000 * 60 * 60 * 2): void {
|
||||
if (!isAutoUpdateEnabled() || isTestEnv) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,16 +10,16 @@ import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import { computeDiff as computeDiffFor, configToModels } from "../kubeconfig-sync/manager";
|
||||
import mockFs from "mock-fs";
|
||||
import fs from "fs";
|
||||
import { ClusterManager } from "../../cluster-manager";
|
||||
import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import { ClusterStore } from "../../../common/cluster-store/cluster-store";
|
||||
import getConfigurationFileModelInjectable
|
||||
from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||
import appVersionInjectable
|
||||
from "../../../common/get-configuration-file-model/app-version/app-version.injectable";
|
||||
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||
import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable";
|
||||
import clusterManagerInjectable from "../../cluster-manager.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";
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
@@ -45,12 +45,8 @@ describe("kubeconfig-sync.source tests", () => {
|
||||
|
||||
mockFs();
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
computeDiff = computeDiffFor({
|
||||
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
||||
createCluster: di.inject(createClusterInjectionToken),
|
||||
});
|
||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
|
||||
|
||||
di.override(clusterStoreInjectable, () =>
|
||||
ClusterStore.createInstance({ createCluster: () => null as never }),
|
||||
@@ -59,14 +55,17 @@ describe("kubeconfig-sync.source tests", () => {
|
||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||
di.permitSideEffects(appVersionInjectable);
|
||||
|
||||
di.inject(clusterStoreInjectable);
|
||||
computeDiff = computeDiffFor({
|
||||
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
||||
createCluster: di.inject(createClusterInjectionToken),
|
||||
clusterManager: di.inject(clusterManagerInjectable),
|
||||
});
|
||||
|
||||
ClusterManager.createInstance();
|
||||
di.inject(clusterStoreInjectable);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
ClusterManager.resetInstance();
|
||||
ClusterStore.resetInstance();
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import { KubeconfigSyncManager } from "./manager";
|
||||
import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token";
|
||||
import clusterManagerInjectable from "../../cluster-manager.injectable";
|
||||
import catalogEntityRegistryInjectable from "../../catalog/entity-registry.injectable";
|
||||
|
||||
const kubeconfigSyncManagerInjectable = getInjectable({
|
||||
@@ -14,6 +15,7 @@ const kubeconfigSyncManagerInjectable = getInjectable({
|
||||
instantiate: (di) => new KubeconfigSyncManager({
|
||||
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
||||
createCluster: di.inject(createClusterInjectionToken),
|
||||
clusterManager: di.inject(clusterManagerInjectable),
|
||||
entityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -17,7 +17,8 @@ import { bytesToUnits, getOrInsertWith, iter, noop } from "../../../common/utils
|
||||
import logger from "../../logger";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers";
|
||||
import { catalogEntityFromCluster, ClusterManager } from "../../cluster-manager";
|
||||
import type { ClusterManager } from "../../cluster-manager";
|
||||
import { catalogEntityFromCluster } from "../../cluster-manager";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import { ClusterStore } from "../../../common/cluster-store/cluster-store";
|
||||
import { createHash } from "crypto";
|
||||
@@ -51,9 +52,10 @@ const ignoreGlobs = [
|
||||
const folderSyncMaxAllowedFileReadSize = 2 * 1024 * 1024; // 2 MiB
|
||||
const fileSyncMaxAllowedFileReadSize = 16 * folderSyncMaxAllowedFileReadSize; // 32 MiB
|
||||
|
||||
interface Dependencies {
|
||||
interface KubeconfigSyncManagerDependencies {
|
||||
readonly directoryForKubeConfigs: string;
|
||||
readonly entityRegistry: CatalogEntityRegistry;
|
||||
readonly clusterManager: ClusterManager;
|
||||
createCluster: (model: ClusterModel) => Cluster;
|
||||
}
|
||||
|
||||
@@ -64,7 +66,7 @@ export class KubeconfigSyncManager {
|
||||
protected syncing = false;
|
||||
protected syncListDisposer?: Disposer;
|
||||
|
||||
constructor(protected readonly dependencies: Dependencies) {
|
||||
constructor(protected readonly dependencies: KubeconfigSyncManagerDependencies) {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@@ -165,8 +167,14 @@ export function configToModels(rootConfig: KubeConfig, filePath: string): Update
|
||||
type RootSourceValue = [Cluster, CatalogEntity];
|
||||
type RootSource = ObservableMap<string, RootSourceValue>;
|
||||
|
||||
interface ComputeDiffDependencies {
|
||||
directoryForKubeConfigs: string;
|
||||
createCluster: (model: ClusterModel) => Cluster;
|
||||
clusterManager: ClusterManager;
|
||||
}
|
||||
|
||||
// exported for testing
|
||||
export const computeDiff = ({ directoryForKubeConfigs, createCluster }: Pick<Dependencies, "createCluster" | "directoryForKubeConfigs">) => (contents: string, source: RootSource, filePath: string): void => {
|
||||
export const computeDiff = ({ directoryForKubeConfigs, createCluster, clusterManager }: ComputeDiffDependencies) => (contents: string, source: RootSource, filePath: string): void => {
|
||||
runInAction(() => {
|
||||
try {
|
||||
const { config, error } = loadConfigFromString(contents);
|
||||
@@ -186,7 +194,7 @@ export const computeDiff = ({ directoryForKubeConfigs, createCluster }: Pick<Dep
|
||||
// remove and disconnect clusters that were removed from the config
|
||||
if (!model) {
|
||||
// remove from the deleting set, so that if a new context of the same name is added, it isn't marked as deleting
|
||||
ClusterManager.getInstance().deleting.delete(value[0].id);
|
||||
clusterManager.deleting.delete(value[0].id);
|
||||
|
||||
value[0].disconnect();
|
||||
source.delete(contextName);
|
||||
@@ -241,7 +249,7 @@ interface DiffChangedConfigArgs {
|
||||
maxAllowedFileReadSize: number;
|
||||
}
|
||||
|
||||
const diffChangedConfigFor = (dependencies: Dependencies) => ({ filePath, source, stats, maxAllowedFileReadSize }: DiffChangedConfigArgs): Disposer => {
|
||||
const diffChangedConfigFor = (dependencies: ComputeDiffDependencies) => ({ filePath, source, stats, maxAllowedFileReadSize }: DiffChangedConfigArgs): Disposer => {
|
||||
logger.debug(`${logPrefix} file changed`, { filePath });
|
||||
|
||||
if (stats.size >= maxAllowedFileReadSize) {
|
||||
@@ -297,7 +305,7 @@ const diffChangedConfigFor = (dependencies: Dependencies) => ({ filePath, source
|
||||
return cleanup;
|
||||
};
|
||||
|
||||
const watchFileChanges = (filePath: string, dependencies: Dependencies): [IComputedValue<CatalogEntity[]>, Disposer] => {
|
||||
const watchFileChanges = (filePath: string, dependencies: ComputeDiffDependencies): [IComputedValue<CatalogEntity[]>, Disposer] => {
|
||||
const rootSource = observable.map<string, ObservableMap<string, RootSourceValue>>();
|
||||
const derivedSource = computed(() => Array.from(iter.flatMap(rootSource.values(), from => iter.map(from.values(), child => child[1]))));
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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 { syncWeblinks } from "./weblinks";
|
||||
import weblinkStoreInjectable from "../../common/weblink-store.injectable";
|
||||
import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable";
|
||||
|
||||
const syncWeblinksInjectable = getInjectable({
|
||||
id: "sync-weblinks",
|
||||
|
||||
instantiate: (di) => syncWeblinks({
|
||||
weblinkStore: di.inject(weblinkStoreInjectable),
|
||||
catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default syncWeblinksInjectable;
|
||||
@@ -4,9 +4,9 @@
|
||||
*/
|
||||
|
||||
import { computed, observable, reaction } from "mobx";
|
||||
import { WeblinkStore } from "../../common/weblink-store";
|
||||
import type { WeblinkStore } from "../../common/weblink-store";
|
||||
import { WebLink } from "../../common/catalog-entities";
|
||||
import { catalogEntityRegistry } from "../catalog";
|
||||
import type { CatalogEntityRegistry } from "../catalog";
|
||||
import got from "got";
|
||||
import type { Disposer } from "../../common/utils";
|
||||
import { random } from "lodash";
|
||||
@@ -28,9 +28,12 @@ async function validateLink(link: WebLink) {
|
||||
}
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
weblinkStore: WeblinkStore;
|
||||
catalogEntityRegistry: CatalogEntityRegistry;
|
||||
}
|
||||
|
||||
export function syncWeblinks() {
|
||||
const weblinkStore = WeblinkStore.getInstance();
|
||||
export const syncWeblinks = ({ weblinkStore, catalogEntityRegistry }: Dependencies) => () => {
|
||||
const webLinkEntities = observable.map<string, [WebLink, Disposer]>();
|
||||
|
||||
function periodicallyCheckLink(link: WebLink): Disposer {
|
||||
@@ -87,4 +90,4 @@ export function syncWeblinks() {
|
||||
}, { fireImmediately: true });
|
||||
|
||||
catalogEntityRegistry.addComputedSource("weblinks", computed(() => Array.from(webLinkEntities.values(), ([link]) => link)));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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 { startCatalogSyncToRenderer } from "../catalog-pusher";
|
||||
import { getStartableStoppable } from "../../common/utils/get-startable-stoppable";
|
||||
import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable";
|
||||
|
||||
const catalogSyncToRendererInjectable = getInjectable({
|
||||
id: "catalog-sync-to-renderer",
|
||||
|
||||
instantiate: (di) => {
|
||||
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
|
||||
return getStartableStoppable("catalog-sync", () =>
|
||||
startCatalogSyncToRenderer(catalogEntityRegistry),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default catalogSyncToRendererInjectable;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { afterRootFrameIsReadyInjectionToken } from "../start-main-application/runnable-tokens/after-root-frame-is-ready-injection-token";
|
||||
import catalogSyncToRendererInjectable from "./catalog-sync-to-renderer.injectable";
|
||||
|
||||
const startCatalogSyncInjectable = getInjectable({
|
||||
id: "start-catalog-sync",
|
||||
|
||||
instantiate: (di) => {
|
||||
const catalogSyncToRenderer = di.inject(catalogSyncToRendererInjectable);
|
||||
|
||||
return {
|
||||
run: async () => {
|
||||
await catalogSyncToRenderer.start();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: afterRootFrameIsReadyInjectionToken,
|
||||
});
|
||||
|
||||
export default startCatalogSyncInjectable;
|
||||
@@ -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 catalogSyncToRendererInjectable from "./catalog-sync-to-renderer.injectable";
|
||||
import { beforeQuitOfFrontEndInjectionToken } from "../start-main-application/runnable-tokens/before-quit-of-front-end-injection-token";
|
||||
|
||||
const stopCatalogSyncInjectable = getInjectable({
|
||||
id: "stop-catalog-sync",
|
||||
|
||||
instantiate: (di) => {
|
||||
const catalogSyncToRenderer = di.inject(catalogSyncToRendererInjectable);
|
||||
|
||||
return {
|
||||
run: async () => {
|
||||
if (catalogSyncToRenderer.started) {
|
||||
await catalogSyncToRenderer.stop();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeQuitOfFrontEndInjectionToken,
|
||||
});
|
||||
|
||||
export default stopCatalogSyncInjectable;
|
||||
@@ -8,6 +8,7 @@ import { CatalogEntityRegistry } from "./entity-registry";
|
||||
|
||||
const catalogEntityRegistryInjectable = getInjectable({
|
||||
id: "catalog-entity-registry",
|
||||
|
||||
instantiate: (di) => new CatalogEntityRegistry({
|
||||
hasCategoryForEntity: di.inject(hasCategoryForEntityInjectable),
|
||||
}),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import type { RequestPromiseOptions } from "request-promise-native";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { k8sRequest } from "../k8s-request";
|
||||
import type { K8sRequest } from "../k8s-request.injectable";
|
||||
|
||||
export interface ClusterDetectionResult {
|
||||
value: string | number | boolean;
|
||||
@@ -15,11 +15,11 @@ export interface ClusterDetectionResult {
|
||||
export abstract class BaseClusterDetector {
|
||||
abstract readonly key: string;
|
||||
|
||||
constructor(public readonly cluster: Cluster) {}
|
||||
constructor(public readonly cluster: Cluster, private _k8sRequest: K8sRequest) {}
|
||||
|
||||
abstract detect(): Promise<ClusterDetectionResult | null>;
|
||||
|
||||
protected async k8sRequest<T = any>(path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
||||
return k8sRequest(this.cluster, path, options);
|
||||
return this._k8sRequest(this.cluster, path, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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 { VersionDetector } from "./version-detector";
|
||||
import k8sRequestInjectable from "../k8s-request.injectable";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
|
||||
const createVersionDetectorInjectable = getInjectable({
|
||||
id: "create-version-detector",
|
||||
|
||||
instantiate: (di) => {
|
||||
const k8sRequest = di.inject(k8sRequestInjectable);
|
||||
|
||||
return (cluster: Cluster) =>
|
||||
new VersionDetector(cluster, k8sRequest);
|
||||
},
|
||||
});
|
||||
|
||||
export default createVersionDetectorInjectable;
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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 { DetectorRegistry } from "./detector-registry";
|
||||
import k8sRequestInjectable from "../k8s-request.injectable";
|
||||
|
||||
const detectorRegistryInjectable = getInjectable({
|
||||
id: "detector-registry",
|
||||
|
||||
instantiate: (di) =>
|
||||
new DetectorRegistry({ k8sRequest: di.inject(k8sRequestInjectable) }),
|
||||
});
|
||||
|
||||
export default detectorRegistryInjectable;
|
||||
@@ -5,13 +5,19 @@
|
||||
|
||||
import { observable } from "mobx";
|
||||
import type { ClusterMetadata } from "../../common/cluster-types";
|
||||
import { Singleton } from "../../common/utils";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector";
|
||||
import type { K8sRequest } from "../k8s-request.injectable";
|
||||
|
||||
export type DetectorConstructor = new (cluster: Cluster) => BaseClusterDetector;
|
||||
interface Dependencies {
|
||||
k8sRequest: K8sRequest;
|
||||
}
|
||||
|
||||
export type DetectorConstructor = new (cluster: Cluster, k8sRequest: K8sRequest) => BaseClusterDetector;
|
||||
|
||||
export class DetectorRegistry {
|
||||
constructor(private dependencies: Dependencies) {}
|
||||
|
||||
export class DetectorRegistry extends Singleton {
|
||||
registry = observable.array<DetectorConstructor>([], { deep: false });
|
||||
|
||||
add(detectorClass: DetectorConstructor): this {
|
||||
@@ -24,7 +30,7 @@ export class DetectorRegistry extends Singleton {
|
||||
const results: { [key: string]: ClusterDetectionResult } = {};
|
||||
|
||||
for (const detectorClass of this.registry) {
|
||||
const detector = new detectorClass(cluster);
|
||||
const detector = new detectorClass(cluster, this.dependencies.k8sRequest);
|
||||
|
||||
try {
|
||||
const data = await detector.detect();
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { ClusterManager } from "./cluster-manager";
|
||||
import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable";
|
||||
import catalogEntityRegistryInjectable from "./catalog/entity-registry.injectable";
|
||||
|
||||
const clusterManagerInjectable = getInjectable({
|
||||
id: "cluster-manager",
|
||||
|
||||
instantiate: (di) => {
|
||||
const clusterManager = new ClusterManager({
|
||||
store: di.inject(clusterStoreInjectable),
|
||||
catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||
});
|
||||
|
||||
clusterManager.init();
|
||||
|
||||
return clusterManager;
|
||||
},
|
||||
});
|
||||
|
||||
export default clusterManagerInjectable;
|
||||
+29
-26
@@ -9,52 +9,55 @@ import { action, makeObservable, observable, observe, reaction, toJS } from "mob
|
||||
import type { Cluster } from "../common/cluster/cluster";
|
||||
import logger from "./logger";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import { getClusterIdFromHost, isErrnoException, Singleton } from "../common/utils";
|
||||
import { catalogEntityRegistry } from "./catalog";
|
||||
import { getClusterIdFromHost, isErrnoException } from "../common/utils";
|
||||
import type { KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster";
|
||||
import { isKubernetesCluster, KubernetesCluster, LensKubernetesClusterStatus } from "../common/catalog-entities/kubernetes-cluster";
|
||||
import { ipcMainOn } from "../common/ipc";
|
||||
import { once } from "lodash";
|
||||
import { ClusterStore } from "../common/cluster-store/cluster-store";
|
||||
import type { ClusterStore } from "../common/cluster-store/cluster-store";
|
||||
import type { ClusterId } from "../common/cluster-types";
|
||||
import type { CatalogEntityRegistry } from "./catalog";
|
||||
|
||||
const logPrefix = "[CLUSTER-MANAGER]:";
|
||||
|
||||
const lensSpecificClusterStatuses: Set<string> = new Set(Object.values(LensKubernetesClusterStatus));
|
||||
|
||||
export class ClusterManager extends Singleton {
|
||||
private store = ClusterStore.getInstance();
|
||||
interface Dependencies {
|
||||
store: ClusterStore;
|
||||
catalogEntityRegistry: CatalogEntityRegistry;
|
||||
}
|
||||
|
||||
export class ClusterManager {
|
||||
deleting = observable.set<ClusterId>();
|
||||
|
||||
@observable visibleCluster: ClusterId | undefined = undefined;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
constructor(private dependencies: Dependencies) {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
init = once(() => {
|
||||
// reacting to every cluster's state change and total amount of items
|
||||
reaction(
|
||||
() => this.store.clustersList.map(c => c.getState()),
|
||||
() => this.updateCatalog(this.store.clustersList),
|
||||
() => this.dependencies.store.clustersList.map(c => c.getState()),
|
||||
() => this.updateCatalog(this.dependencies.store.clustersList),
|
||||
{ fireImmediately: false },
|
||||
);
|
||||
|
||||
// reacting to every cluster's preferences change and total amount of items
|
||||
reaction(
|
||||
() => this.store.clustersList.map(c => toJS(c.preferences)),
|
||||
() => this.updateCatalog(this.store.clustersList),
|
||||
() => this.dependencies.store.clustersList.map(c => toJS(c.preferences)),
|
||||
() => this.updateCatalog(this.dependencies.store.clustersList),
|
||||
{ fireImmediately: false },
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => catalogEntityRegistry.filterItemsByPredicate(isKubernetesCluster),
|
||||
() => this.dependencies.catalogEntityRegistry.filterItemsByPredicate(isKubernetesCluster),
|
||||
entities => this.syncClustersFromCatalog(entities),
|
||||
);
|
||||
|
||||
reaction(() => [
|
||||
catalogEntityRegistry.filterItemsByPredicate(isKubernetesCluster),
|
||||
this.dependencies.catalogEntityRegistry.filterItemsByPredicate(isKubernetesCluster),
|
||||
this.visibleCluster,
|
||||
] as const, ([entities, visibleCluster]) => {
|
||||
for (const entity of entities) {
|
||||
@@ -68,7 +71,7 @@ export class ClusterManager extends Singleton {
|
||||
|
||||
observe(this.deleting, change => {
|
||||
if (change.type === "add") {
|
||||
this.updateEntityStatus(catalogEntityRegistry.findById(change.newValue) as KubernetesCluster);
|
||||
this.updateEntityStatus(this.dependencies.catalogEntityRegistry.findById(change.newValue) as KubernetesCluster);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -86,13 +89,13 @@ export class ClusterManager extends Singleton {
|
||||
}
|
||||
|
||||
protected updateEntityFromCluster(cluster: Cluster) {
|
||||
const index = catalogEntityRegistry.items.findIndex((entity) => entity.getId() === cluster.id);
|
||||
const index = this.dependencies.catalogEntityRegistry.items.findIndex((entity) => entity.getId() === cluster.id);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entity = catalogEntityRegistry.items[index] as KubernetesCluster;
|
||||
const entity = this.dependencies.catalogEntityRegistry.items[index] as KubernetesCluster;
|
||||
|
||||
this.updateEntityStatus(entity, cluster);
|
||||
|
||||
@@ -133,7 +136,7 @@ export class ClusterManager extends Singleton {
|
||||
cluster.preferences.icon = undefined;
|
||||
}
|
||||
|
||||
catalogEntityRegistry.items.splice(index, 1, entity);
|
||||
this.dependencies.catalogEntityRegistry.items.splice(index, 1, entity);
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -180,7 +183,7 @@ export class ClusterManager extends Singleton {
|
||||
@action
|
||||
protected syncClustersFromCatalog(entities: KubernetesCluster[]) {
|
||||
for (const entity of entities) {
|
||||
const cluster = this.store.getById(entity.getId());
|
||||
const cluster = this.dependencies.store.getById(entity.getId());
|
||||
|
||||
if (!cluster) {
|
||||
const model = {
|
||||
@@ -195,7 +198,7 @@ export class ClusterManager extends Singleton {
|
||||
* Add the bare minimum of data to ClusterStore. And especially no
|
||||
* preferences, as those might be configured by the entity's source
|
||||
*/
|
||||
this.store.addCluster(model);
|
||||
this.dependencies.store.addCluster(model);
|
||||
} catch (error) {
|
||||
if (isErrnoException(error) && error.code === "ENOENT" && error.path === entity.spec.kubeconfigPath) {
|
||||
logger.warn(`${logPrefix} kubeconfig file disappeared`, model);
|
||||
@@ -234,7 +237,7 @@ export class ClusterManager extends Singleton {
|
||||
|
||||
protected onNetworkOffline = () => {
|
||||
logger.info(`${logPrefix} network is offline`);
|
||||
this.store.clustersList.forEach((cluster) => {
|
||||
this.dependencies.store.clustersList.forEach((cluster) => {
|
||||
if (!cluster.disconnected) {
|
||||
cluster.online = false;
|
||||
cluster.accessible = false;
|
||||
@@ -245,7 +248,7 @@ export class ClusterManager extends Singleton {
|
||||
|
||||
protected onNetworkOnline = () => {
|
||||
logger.info(`${logPrefix} network is online`);
|
||||
this.store.clustersList.forEach((cluster) => {
|
||||
this.dependencies.store.clustersList.forEach((cluster) => {
|
||||
if (!cluster.disconnected) {
|
||||
cluster.refreshConnectionStatus().catch((e) => e);
|
||||
}
|
||||
@@ -253,12 +256,12 @@ export class ClusterManager extends Singleton {
|
||||
};
|
||||
|
||||
stop() {
|
||||
this.store.clusters.forEach((cluster: Cluster) => {
|
||||
this.dependencies.store.clusters.forEach((cluster: Cluster) => {
|
||||
cluster.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
getClusterForRequest(req: http.IncomingMessage): Cluster | undefined {
|
||||
getClusterForRequest = (req: http.IncomingMessage): Cluster | undefined => {
|
||||
if (!req.headers.host) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -266,7 +269,7 @@ export class ClusterManager extends Singleton {
|
||||
// lens-server is connecting to 127.0.0.1:<port>/<uid>
|
||||
if (req.url && req.headers.host.startsWith("127.0.0.1")) {
|
||||
const clusterId = req.url.split("/")[1];
|
||||
const cluster = this.store.getById(clusterId);
|
||||
const cluster = this.dependencies.store.getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
// we need to swap path prefix so that request is proxied to kube api
|
||||
@@ -276,8 +279,8 @@ export class ClusterManager extends Singleton {
|
||||
return cluster;
|
||||
}
|
||||
|
||||
return this.store.getById(getClusterIdFromHost(req.headers.host));
|
||||
}
|
||||
return this.dependencies.store.getById(getClusterIdFromHost(req.headers.host));
|
||||
};
|
||||
}
|
||||
|
||||
export function catalogEntityFromCluster(cluster: Cluster) {
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { PrometheusProvider, PrometheusService } from "../prometheus/provider-registry";
|
||||
import { PrometheusProviderRegistry } from "../prometheus/provider-registry";
|
||||
import type { PrometheusProvider, PrometheusService, PrometheusProviderRegistry } from "../prometheus/provider-registry";
|
||||
import type { ClusterPrometheusPreferences } from "../../common/cluster-types";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type httpProxy from "http-proxy";
|
||||
@@ -28,8 +27,9 @@ interface PrometheusServicePreferences {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
createKubeAuthProxy: CreateKubeAuthProxy;
|
||||
authProxyCa: string;
|
||||
readonly createKubeAuthProxy: CreateKubeAuthProxy;
|
||||
readonly authProxyCa: string;
|
||||
readonly prometheusProviderRegistry: PrometheusProviderRegistry;
|
||||
}
|
||||
|
||||
export interface ClusterContextHandler {
|
||||
@@ -79,11 +79,11 @@ export class ContextHandler implements ClusterContextHandler {
|
||||
this.prometheusProvider = service.id;
|
||||
}
|
||||
|
||||
return PrometheusProviderRegistry.getInstance().getByKind(this.prometheusProvider);
|
||||
return this.dependencies.prometheusProviderRegistry.getByKind(this.prometheusProvider);
|
||||
}
|
||||
|
||||
protected listPotentialProviders(): PrometheusProvider[] {
|
||||
const registry = PrometheusProviderRegistry.getInstance();
|
||||
const registry = this.dependencies.prometheusProviderRegistry;
|
||||
const provider = this.prometheusProvider && registry.getByKind(this.prometheusProvider);
|
||||
|
||||
if (provider) {
|
||||
|
||||
@@ -10,23 +10,25 @@ import { ContextHandler } from "./context-handler";
|
||||
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||
import { getKubeAuthProxyCertificate } from "../kube-auth-proxy/get-kube-auth-proxy-certificate";
|
||||
import URLParse from "url-parse";
|
||||
import prometheusProviderRegistryInjectable from "../prometheus/prometheus-provider-registry.injectable";
|
||||
|
||||
const createContextHandlerInjectable = getInjectable({
|
||||
id: "create-context-handler",
|
||||
|
||||
instantiate: (di) => {
|
||||
const createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable);
|
||||
const prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable);
|
||||
|
||||
return (cluster: Cluster): ClusterContextHandler | undefined => {
|
||||
return (cluster: Cluster): ClusterContextHandler => {
|
||||
const clusterUrl = new URLParse(cluster.apiUrl);
|
||||
|
||||
return new ContextHandler(
|
||||
{
|
||||
createKubeAuthProxy,
|
||||
authProxyCa: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate).cert,
|
||||
},
|
||||
cluster,
|
||||
);
|
||||
const dependencies = {
|
||||
createKubeAuthProxy,
|
||||
prometheusProviderRegistry,
|
||||
authProxyCa: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate).cert,
|
||||
};
|
||||
|
||||
return new ContextHandler(dependencies, cluster);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,6 +13,8 @@ import { createClusterInjectionToken } from "../../common/cluster/create-cluster
|
||||
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
|
||||
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import detectorRegistryInjectable from "../cluster-detectors/detector-registry.injectable";
|
||||
import createVersionDetectorInjectable from "../cluster-detectors/create-version-detector.injectable";
|
||||
|
||||
const createClusterInjectable = getInjectable({
|
||||
id: "create-cluster",
|
||||
@@ -26,6 +28,8 @@ const createClusterInjectable = getInjectable({
|
||||
createAuthorizationReview: di.inject(authorizationReviewInjectable),
|
||||
createListNamespaces: di.inject(listNamespacesInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
detectorRegistry: di.inject(detectorRegistryInjectable),
|
||||
createVersionDetector: di.inject(createVersionDetectorInjectable),
|
||||
};
|
||||
|
||||
return (model) => new Cluster(dependencies, model);
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import logger from "./logger";
|
||||
|
||||
/**
|
||||
* Installs Electron developer tools in the development build.
|
||||
* The dependency is not bundled to the production build.
|
||||
*/
|
||||
export const installDeveloperTools = () => {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
logger.info("🤓 Installing developer tools");
|
||||
import("electron-devtools-installer")
|
||||
.then(({ default: devToolsInstaller, REACT_DEVELOPER_TOOLS }) => devToolsInstaller([REACT_DEVELOPER_TOOLS]))
|
||||
.then((name) => logger.info(`[DEVTOOLS-INSTALLER]: installed ${name}`))
|
||||
.catch(error => logger.error(`[DEVTOOLS-INSTALLER]: failed`, { error }));
|
||||
}
|
||||
};
|
||||
File renamed without changes.
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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 { autoUpdater } from "electron";
|
||||
|
||||
const autoUpdaterInjectable = getInjectable({
|
||||
id: "auto-updater",
|
||||
instantiate: () => autoUpdater,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default autoUpdaterInjectable;
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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 electronAppInjectable from "../electron-app.injectable";
|
||||
|
||||
const disableHardwareAccelerationInjectable = getInjectable({
|
||||
id: "disable-hardware-acceleration",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
|
||||
return () => {
|
||||
app.disableHardwareAcceleration();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default disableHardwareAccelerationInjectable;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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 { dialog } from "electron";
|
||||
|
||||
const electronDialogInjectable = getInjectable({
|
||||
id: "electron-dialog",
|
||||
instantiate: () => dialog,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default electronDialogInjectable;
|
||||
@@ -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 electronAppInjectable from "../electron-app.injectable";
|
||||
|
||||
const exitAppInjectable = getInjectable({
|
||||
id: "exit-app",
|
||||
|
||||
instantiate: (di) => () => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
|
||||
app.exit(0);
|
||||
},
|
||||
});
|
||||
|
||||
export default exitAppInjectable;
|
||||
@@ -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 electronAppInjectable from "../electron-app.injectable";
|
||||
|
||||
const getCommandLineSwitchInjectable = getInjectable({
|
||||
id: "get-command-line-switch",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
|
||||
return (name: string) => app.commandLine.getSwitchValue(name);
|
||||
},
|
||||
});
|
||||
|
||||
export default getCommandLineSwitchInjectable;
|
||||
@@ -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 nativeThemeInjectable from "./native-theme.injectable";
|
||||
|
||||
const getElectronThemeInjectable = getInjectable({
|
||||
id: "get-electron-theme",
|
||||
|
||||
instantiate: (di) => {
|
||||
const nativeTheme = di.inject(nativeThemeInjectable);
|
||||
|
||||
return () => nativeTheme.shouldUseDarkColors ? "dark" : "light";
|
||||
},
|
||||
});
|
||||
|
||||
export default getElectronThemeInjectable;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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 { nativeTheme } from "electron";
|
||||
|
||||
const nativeThemeInjectable = getInjectable({
|
||||
id: "native-theme",
|
||||
instantiate: () => nativeTheme,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default nativeThemeInjectable;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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 { powerMonitor } from "electron";
|
||||
|
||||
const powerMonitorInjectable = getInjectable({
|
||||
id: "power-monitor",
|
||||
instantiate: () => powerMonitor,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default powerMonitorInjectable;
|
||||
@@ -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 { protocol } from "electron";
|
||||
import getAbsolutePathInjectable from "../../../common/path/get-absolute-path.injectable";
|
||||
|
||||
const registerFileProtocolInjectable = getInjectable({
|
||||
id: "register-file-protocol",
|
||||
|
||||
instantiate: (di) => {
|
||||
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
|
||||
|
||||
return (name: string, basePath: string) => {
|
||||
protocol.registerFileProtocol(name, (request, callback) => {
|
||||
const filePath = request.url.replace(`${name}://`, "");
|
||||
const absPath = getAbsolutePath(basePath, filePath);
|
||||
|
||||
callback({ path: absPath });
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default registerFileProtocolInjectable;
|
||||
@@ -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 electronAppInjectable from "../electron-app.injectable";
|
||||
|
||||
const requestSingleInstanceLockInjectable = getInjectable({
|
||||
id: "request-single-instance-lock",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
|
||||
return () => app.requestSingleInstanceLock();
|
||||
},
|
||||
});
|
||||
|
||||
export default requestSingleInstanceLockInjectable;
|
||||
@@ -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 electronAppInjectable from "../electron-app.injectable";
|
||||
import isMacInjectable from "../../../common/vars/is-mac.injectable";
|
||||
import commandLineArgumentsInjectable from "../../utils/command-line-arguments.injectable";
|
||||
|
||||
const shouldStartHiddenInjectable = getInjectable({
|
||||
id: "should-start-hidden",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
const isMac = di.inject(isMacInjectable);
|
||||
const commandLineArguments = di.inject(commandLineArgumentsInjectable);
|
||||
|
||||
// Start the app without showing the main window when auto starting on login
|
||||
// (On Windows and Linux, we get a flag. On MacOS, we get special API.)
|
||||
return (
|
||||
commandLineArguments.includes("--hidden") ||
|
||||
(isMac && app.getLoginItemSettings().wasOpenedAsHidden)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default shouldStartHiddenInjectable;
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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 electronDialogInjectable from "./electron-dialog.injectable";
|
||||
|
||||
const showErrorPopupInjectable = getInjectable({
|
||||
id: "show-error-popup",
|
||||
|
||||
instantiate: (di) => {
|
||||
const dialog = di.inject(electronDialogInjectable);
|
||||
|
||||
return (heading: string, message: string) => {
|
||||
dialog.showErrorBox(heading, message);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default showErrorPopupInjectable;
|
||||
@@ -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 electronDialogInjectable from "./electron-dialog.injectable";
|
||||
|
||||
export type ShowMessagePopup = (title: string, message: string, detail: string) => void;
|
||||
|
||||
const showMessagePopupInjectable = getInjectable({
|
||||
id: "show-message-popup",
|
||||
|
||||
instantiate: (di): ShowMessagePopup => {
|
||||
const dialog = di.inject(electronDialogInjectable);
|
||||
|
||||
return async (title, message, detail) => {
|
||||
await dialog.showMessageBox({
|
||||
title,
|
||||
message,
|
||||
detail,
|
||||
type: "info",
|
||||
buttons: ["Close"],
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default showMessagePopupInjectable;
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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 { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
||||
import operatingSystemThemeStateInjectable from "../../theme/operating-system-theme-state.injectable";
|
||||
import nativeThemeInjectable from "./native-theme.injectable";
|
||||
import getElectronThemeInjectable from "./get-electron-theme.injectable";
|
||||
|
||||
const syncThemeFromOperatingSystemInjectable = getInjectable({
|
||||
id: "sync-theme-from-operating-system",
|
||||
|
||||
instantiate: (di) => {
|
||||
const currentThemeState = di.inject(operatingSystemThemeStateInjectable);
|
||||
const nativeTheme = di.inject(nativeThemeInjectable);
|
||||
const getElectronTheme = di.inject(getElectronThemeInjectable);
|
||||
|
||||
return getStartableStoppable("sync-theme-from-operating-system", () => {
|
||||
const updateThemeState = () => {
|
||||
const newTheme = getElectronTheme();
|
||||
|
||||
currentThemeState.set(newTheme);
|
||||
};
|
||||
|
||||
nativeTheme.on("updated", updateThemeState);
|
||||
|
||||
return () => {
|
||||
nativeTheme.off("updated", updateThemeState);
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default syncThemeFromOperatingSystemInjectable;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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 electronAppInjectable from "../electron-app.injectable";
|
||||
|
||||
const waitForElectronToBeReadyInjectable = getInjectable({
|
||||
id: "wait-for-electron-to-be-ready",
|
||||
|
||||
instantiate: (di) => () => di.inject(electronAppInjectable).whenReady(),
|
||||
});
|
||||
|
||||
export default waitForElectronToBeReadyInjectable;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
|
||||
import lensProtocolRouterMainInjectable from "../../protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable";
|
||||
|
||||
const cleanUpDeepLinkingInjectable = getInjectable({
|
||||
id: "clean-up-deep-linking",
|
||||
|
||||
instantiate: (di) => {
|
||||
const lensProtocolRouterMain = di.inject(lensProtocolRouterMainInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
lensProtocolRouterMain.cleanup();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||
});
|
||||
|
||||
export default cleanUpDeepLinkingInjectable;
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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/before-quit-of-front-end-injection-token";
|
||||
import electronAppInjectable from "../../electron-app.injectable";
|
||||
import { lensWindowInjectionToken } from "../../../start-main-application/lens-window/application-window/lens-window-injection-token";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { filter, isEmpty } from "lodash/fp";
|
||||
|
||||
const hideDockForLastClosedWindowInjectable = getInjectable({
|
||||
id: "hide-dock-when-there-are-no-windows",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
const getLensWindows = () => di.injectMany(lensWindowInjectionToken);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
const visibleWindows = pipeline(
|
||||
getLensWindows(),
|
||||
filter(window => !!window.visible),
|
||||
);
|
||||
|
||||
if (isEmpty(visibleWindows)) {
|
||||
app.dock?.hide();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeQuitOfFrontEndInjectionToken,
|
||||
});
|
||||
|
||||
export default hideDockForLastClosedWindowInjectable;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 electronAppInjectable from "../../electron-app.injectable";
|
||||
import { afterWindowIsOpenedInjectionToken } from "../../../start-main-application/runnable-tokens/after-window-is-opened-injection-token";
|
||||
|
||||
const showDockForFirstOpenedWindowInjectable = getInjectable({
|
||||
id: "show-dock-for-first-opened-window",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
app.dock?.show();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: afterWindowIsOpenedInjectionToken,
|
||||
});
|
||||
|
||||
export default showDockForFirstOpenedWindowInjectable;
|
||||
@@ -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 { beforeElectronIsReadyInjectionToken } from "../../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
|
||||
import requestSingleInstanceLockInjectable from "../features/request-single-instance-lock.injectable";
|
||||
import exitAppInjectable from "../features/exit-app.injectable";
|
||||
|
||||
const enforceSingleApplicationInstanceInjectable = getInjectable({
|
||||
id: "enforce-single-application-instance",
|
||||
|
||||
instantiate: (di) => {
|
||||
const requestSingleInstanceLock = di.inject(requestSingleInstanceLockInjectable);
|
||||
const exitApp = di.inject(exitAppInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
if (!requestSingleInstanceLock()) {
|
||||
exitApp();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeElectronIsReadyInjectionToken,
|
||||
});
|
||||
|
||||
export default enforceSingleApplicationInstanceInjectable;
|
||||
@@ -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 appNameInjectable from "../../app-paths/app-name/app-name.injectable";
|
||||
import { beforeElectronIsReadyInjectionToken } from "../../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
|
||||
import electronAppInjectable from "../electron-app.injectable";
|
||||
|
||||
const setupApplicationNameInjectable = getInjectable({
|
||||
id: "setup-application-name",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
const appName = di.inject(appNameInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
app.setName(appName);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeElectronIsReadyInjectionToken,
|
||||
});
|
||||
|
||||
export default setupApplicationNameInjectable;
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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 electronAppInjectable from "../electron-app.injectable";
|
||||
import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import commandLineArgumentsInjectable from "../../utils/command-line-arguments.injectable";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { find, startsWith, toLower, map } from "lodash/fp";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
|
||||
|
||||
const setupDeepLinkingInjectable = getInjectable({
|
||||
id: "setup-deep-linking",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const openDeepLinkForUrl = di.inject(openDeepLinkInjectable);
|
||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||
|
||||
const firstInstanceCommandLineArguments = di.inject(
|
||||
commandLineArgumentsInjectable,
|
||||
);
|
||||
|
||||
return {
|
||||
run: async () => {
|
||||
logger.info(`📟 Setting protocol client for lens://`);
|
||||
|
||||
if (app.setAsDefaultProtocolClient("lens")) {
|
||||
logger.info("📟 Protocol client register succeeded ✅");
|
||||
} else {
|
||||
logger.info("📟 Protocol client register failed ❗");
|
||||
}
|
||||
|
||||
const url = getDeepLinkUrl(firstInstanceCommandLineArguments);
|
||||
|
||||
if (url) {
|
||||
await openDeepLinkForUrl(url);
|
||||
}
|
||||
|
||||
app.on("open-url", async (event, url) => {
|
||||
event.preventDefault();
|
||||
|
||||
await openDeepLinkForUrl(url);
|
||||
});
|
||||
|
||||
app.on(
|
||||
"second-instance",
|
||||
|
||||
async (_, secondInstanceCommandLineArguments) => {
|
||||
const url = getDeepLinkUrl(secondInstanceCommandLineArguments);
|
||||
|
||||
await showApplicationWindow();
|
||||
|
||||
if (url) {
|
||||
await openDeepLinkForUrl(url);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupDeepLinkingInjectable;
|
||||
|
||||
const getDeepLinkUrl = (commandLineArguments: string[]) =>
|
||||
pipeline(commandLineArguments, map(toLower), find(startsWith("lens://")));
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 loggerInjectable from "../../../common/logger.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
|
||||
const setupDeveloperToolsInDevelopmentEnvironmentInjectable = getInjectable({
|
||||
id: "setup-developer-tools-in-development-environment",
|
||||
|
||||
instantiate: (di) => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("🤓 Installing developer tools");
|
||||
|
||||
import("electron-devtools-installer")
|
||||
.then(({ default: devToolsInstaller, REACT_DEVELOPER_TOOLS }) =>
|
||||
devToolsInstaller([REACT_DEVELOPER_TOOLS]),
|
||||
)
|
||||
.then((name) =>
|
||||
logger.info(`[DEVTOOLS-INSTALLER]: installed ${name}`),
|
||||
)
|
||||
.catch((error) =>
|
||||
logger.error(`[DEVTOOLS-INSTALLER]: failed`, { error }),
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupDeveloperToolsInDevelopmentEnvironmentInjectable;
|
||||
@@ -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 powerMonitorInjectable from "../features/power-monitor.injectable";
|
||||
import exitAppInjectable from "../features/exit-app.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
|
||||
const setupDeviceShutdownInjectable = getInjectable({
|
||||
id: "setup-device-shutdown",
|
||||
|
||||
instantiate: (di) => {
|
||||
const powerMonitor = di.inject(powerMonitorInjectable);
|
||||
const exitApp = di.inject(exitAppInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
powerMonitor.on("shutdown", async () => {
|
||||
exitApp();
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupDeviceShutdownInjectable;
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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 directoryForLensLocalStorageInjectable from "../../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
|
||||
import { setupIpcMainHandlers } from "./setup-ipc-main-handlers";
|
||||
import loggerInjectable from "../../../../common/logger.injectable";
|
||||
import clusterManagerInjectable from "../../../cluster-manager.injectable";
|
||||
import applicationMenuItemsInjectable from "../../../menu/application-menu-items.injectable";
|
||||
import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable";
|
||||
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable";
|
||||
import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable";
|
||||
import askUserForFilePathsInjectable from "../../../ipc/ask-user-for-file-paths.injectable";
|
||||
|
||||
const setupIpcMainHandlersInjectable = getInjectable({
|
||||
id: "setup-ipc-main-handlers",
|
||||
|
||||
instantiate: (di) => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
const directoryForLensLocalStorage = di.inject(
|
||||
directoryForLensLocalStorageInjectable,
|
||||
);
|
||||
|
||||
const clusterManager = di.inject(clusterManagerInjectable);
|
||||
const applicationMenuItems = di.inject(applicationMenuItemsInjectable);
|
||||
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
|
||||
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
const clusterStore = di.inject(clusterStoreInjectable);
|
||||
const operatingSystemTheme = di.inject(operatingSystemThemeInjectable);
|
||||
const askUserForFilePaths = di.inject(askUserForFilePathsInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
logger.debug("[APP-MAIN] initializing ipc main handlers");
|
||||
|
||||
setupIpcMainHandlers({
|
||||
applicationMenuItems,
|
||||
getAbsolutePath,
|
||||
directoryForLensLocalStorage,
|
||||
clusterManager,
|
||||
catalogEntityRegistry,
|
||||
clusterStore,
|
||||
operatingSystemTheme,
|
||||
askUserForFilePaths,
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default setupIpcMainHandlersInjectable;
|
||||
+31
-25
@@ -2,37 +2,41 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { IpcMainInvokeEvent } from "electron";
|
||||
import { BrowserWindow, Menu } from "electron";
|
||||
import { clusterFrameMap } from "../../../common/cluster-frames";
|
||||
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterDeleteHandler, clusterSetDeletingHandler, clusterClearDeletingHandler } from "../../../common/ipc/cluster";
|
||||
import type { ClusterId } from "../../../common/cluster-types";
|
||||
import { ClusterStore } from "../../../common/cluster-store/cluster-store";
|
||||
import { appEventBus } from "../../../common/app-event-bus/event-bus";
|
||||
import { broadcastMainChannel, broadcastMessage, ipcMainHandle, ipcMainOn } from "../../../common/ipc";
|
||||
import { catalogEntityRegistry } from "../../catalog";
|
||||
import { pushCatalogToRenderer } from "../../catalog-pusher";
|
||||
import { ClusterManager } from "../../cluster-manager";
|
||||
import { ResourceApplier } from "../../resource-applier";
|
||||
import { clusterFrameMap } from "../../../../common/cluster-frames";
|
||||
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterDeleteHandler, clusterSetDeletingHandler, clusterClearDeletingHandler } from "../../../../common/ipc/cluster";
|
||||
import type { ClusterId } from "../../../../common/cluster-types";
|
||||
import { ClusterStore } from "../../../../common/cluster-store/cluster-store";
|
||||
import { appEventBus } from "../../../../common/app-event-bus/event-bus";
|
||||
import { broadcastMainChannel, broadcastMessage, ipcMainHandle, ipcMainOn } from "../../../../common/ipc";
|
||||
import type { CatalogEntityRegistry } from "../../../catalog";
|
||||
import { pushCatalogToRenderer } from "../../../catalog-pusher";
|
||||
import type { ClusterManager } from "../../../cluster-manager";
|
||||
import { ResourceApplier } from "../../../resource-applier";
|
||||
import { remove } from "fs-extra";
|
||||
import { onLocationChange, handleWindowAction } from "../../ipc/window";
|
||||
import { openFilePickingDialogChannel } from "../../../common/ipc/dialog";
|
||||
import { showOpenDialog } from "../../ipc/dialog";
|
||||
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../common/ipc/window";
|
||||
import { getNativeColorTheme } from "../../native-theme";
|
||||
import { getNativeThemeChannel } from "../../../common/ipc/native-theme";
|
||||
import type { GetAbsolutePath } from "../../../common/path/get-absolute-path.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import type { MenuItemOpts } from "../../menu/application-menu-items.injectable";
|
||||
import type { GetAbsolutePath } from "../../../../common/path/get-absolute-path.injectable";
|
||||
import type { MenuItemOpts } from "../../../menu/application-menu-items.injectable";
|
||||
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window";
|
||||
import { handleWindowAction, onLocationChange } from "../../../ipc/window";
|
||||
import { openFilePickingDialogChannel } from "../../../../common/ipc/dialog";
|
||||
import { getNativeThemeChannel } from "../../../../common/ipc/native-theme";
|
||||
import type { Theme } from "../../../theme/operating-system-theme-state.injectable";
|
||||
import type { AskUserForFilePaths } from "../../../ipc/ask-user-for-file-paths.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
directoryForLensLocalStorage: string;
|
||||
getAbsolutePath: GetAbsolutePath;
|
||||
applicationMenuItems: IComputedValue<MenuItemOpts[]>;
|
||||
clusterManager: ClusterManager;
|
||||
catalogEntityRegistry: CatalogEntityRegistry;
|
||||
clusterStore: ClusterStore;
|
||||
operatingSystemTheme: IComputedValue<Theme>;
|
||||
askUserForFilePaths: AskUserForFilePaths;
|
||||
}
|
||||
|
||||
export const initIpcMainHandlers = ({ applicationMenuItems, directoryForLensLocalStorage, getAbsolutePath }: Dependencies) => () => {
|
||||
export const setupIpcMainHandlers = ({ applicationMenuItems, directoryForLensLocalStorage, getAbsolutePath, clusterManager, catalogEntityRegistry, clusterStore, operatingSystemTheme, askUserForFilePaths }: Dependencies) => {
|
||||
ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
||||
return ClusterStore.getInstance()
|
||||
.getById(clusterId)
|
||||
@@ -51,7 +55,7 @@ export const initIpcMainHandlers = ({ applicationMenuItems, directoryForLensLoca
|
||||
});
|
||||
|
||||
ipcMainOn(clusterVisibilityHandler, (event, clusterId?: ClusterId) => {
|
||||
ClusterManager.getInstance().visibleCluster = clusterId;
|
||||
clusterManager.visibleCluster = clusterId;
|
||||
});
|
||||
|
||||
ipcMainHandle(clusterRefreshHandler, (event, clusterId: ClusterId) => {
|
||||
@@ -97,11 +101,11 @@ export const initIpcMainHandlers = ({ applicationMenuItems, directoryForLensLoca
|
||||
});
|
||||
|
||||
ipcMainHandle(clusterSetDeletingHandler, (event, clusterId: string) => {
|
||||
ClusterManager.getInstance().deleting.add(clusterId);
|
||||
clusterManager.deleting.add(clusterId);
|
||||
});
|
||||
|
||||
ipcMainHandle(clusterClearDeletingHandler, (event, clusterId: string) => {
|
||||
ClusterManager.getInstance().deleting.delete(clusterId);
|
||||
clusterManager.deleting.delete(clusterId);
|
||||
});
|
||||
|
||||
ipcMainHandle(clusterKubectlApplyAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => {
|
||||
@@ -146,7 +150,7 @@ export const initIpcMainHandlers = ({ applicationMenuItems, directoryForLensLoca
|
||||
|
||||
ipcMainOn(windowLocationChangedChannel, () => onLocationChange());
|
||||
|
||||
ipcMainHandle(openFilePickingDialogChannel, (event, opts) => showOpenDialog(opts));
|
||||
ipcMainHandle(openFilePickingDialogChannel, (event, opts) => askUserForFilePaths(opts));
|
||||
|
||||
ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args));
|
||||
|
||||
@@ -164,6 +168,8 @@ export const initIpcMainHandlers = ({ applicationMenuItems, directoryForLensLoca
|
||||
});
|
||||
|
||||
ipcMainHandle(getNativeThemeChannel, () => {
|
||||
return getNativeColorTheme();
|
||||
return operatingSystemTheme.get();
|
||||
});
|
||||
|
||||
clusterStore.provideInitialFromMain();
|
||||
};
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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 electronAppInjectable from "../electron-app.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
|
||||
|
||||
const setupMainWindowVisibilityAfterActivationInjectable = getInjectable({
|
||||
id: "setup-main-window-visibility-after-activation",
|
||||
|
||||
instantiate: (di) => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
app.on("activate", async (_, windowIsVisible) => {
|
||||
logger.info("APP:ACTIVATE", { hasVisibleWindows: windowIsVisible });
|
||||
|
||||
if (!windowIsVisible) {
|
||||
await showApplicationWindow();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupMainWindowVisibilityAfterActivationInjectable;
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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 { beforeElectronIsReadyInjectionToken } from "../../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
|
||||
import electronAppInjectable from "../electron-app.injectable";
|
||||
import { runManyFor } from "../../../common/runnable/run-many-for";
|
||||
import { afterWindowIsOpenedInjectionToken } from "../../start-main-application/runnable-tokens/after-window-is-opened-injection-token";
|
||||
|
||||
const setupRunnablesAfterWindowIsOpenedInjectable = getInjectable({
|
||||
id: "setup-runnables-after-window-is-opened",
|
||||
|
||||
instantiate: (di) => {
|
||||
const afterWindowIsOpened = runManyFor(di)(afterWindowIsOpenedInjectionToken);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
|
||||
app.on("browser-window-created", async () => {
|
||||
await afterWindowIsOpened();
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeElectronIsReadyInjectionToken,
|
||||
});
|
||||
|
||||
export default setupRunnablesAfterWindowIsOpenedInjectable;
|
||||
+60
@@ -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 { beforeElectronIsReadyInjectionToken } from "../../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
|
||||
import { beforeQuitOfFrontEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-front-end-injection-token";
|
||||
import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
|
||||
import electronAppInjectable from "../electron-app.injectable";
|
||||
import isIntegrationTestingInjectable from "../../../common/vars/is-integration-testing.injectable";
|
||||
import autoUpdaterInjectable from "../features/auto-updater.injectable";
|
||||
import { runManySyncFor } from "../../../common/runnable/run-many-sync-for";
|
||||
|
||||
const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({
|
||||
id: "setup-closing-of-application",
|
||||
|
||||
instantiate: (di) => {
|
||||
const runMany = runManySyncFor(di);
|
||||
|
||||
const runRunnablesBeforeQuitOfFrontEnd = runMany(
|
||||
beforeQuitOfFrontEndInjectionToken,
|
||||
);
|
||||
|
||||
const runRunnablesBeforeQuitOfBackEnd = runMany(
|
||||
beforeQuitOfBackEndInjectionToken,
|
||||
);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
const app = di.inject(electronAppInjectable);
|
||||
|
||||
const isIntegrationTesting = di.inject(isIntegrationTestingInjectable);
|
||||
const autoUpdater = di.inject(autoUpdaterInjectable);
|
||||
|
||||
let isAutoUpdating = false;
|
||||
|
||||
autoUpdater.on("before-quit-for-update", () => {
|
||||
isAutoUpdating = true;
|
||||
});
|
||||
|
||||
app.on("will-quit", (event) => {
|
||||
runRunnablesBeforeQuitOfFrontEnd();
|
||||
|
||||
const shouldQuitBackEnd = isIntegrationTesting || isAutoUpdating;
|
||||
|
||||
if (shouldQuitBackEnd) {
|
||||
runRunnablesBeforeQuitOfBackEnd();
|
||||
} else {
|
||||
// IMPORTANT: This cannot be destructured as it would break binding of "this" for the Electron event
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeElectronIsReadyInjectionToken,
|
||||
});
|
||||
|
||||
export default setupRunnablesBeforeClosingOfApplicationInjectable;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { afterRootFrameIsReadyInjectionToken } from "../../start-main-application/runnable-tokens/after-root-frame-is-ready-injection-token";
|
||||
import startUpdateCheckingInjectable from "../../start-update-checking.injectable";
|
||||
|
||||
const setupUpdateCheckingInjectable = getInjectable({
|
||||
id: "setup-update-checking",
|
||||
|
||||
instantiate: (di) => {
|
||||
const startUpdateChecking = di.inject(startUpdateCheckingInjectable);
|
||||
|
||||
return {
|
||||
run: () => {
|
||||
startUpdateChecking();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: afterRootFrameIsReadyInjectionToken,
|
||||
});
|
||||
|
||||
export default setupUpdateCheckingInjectable;
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { app } from "electron";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { appEventBus } from "../common/app-event-bus/event-bus";
|
||||
import { ClusterManager } from "./cluster-manager";
|
||||
import logger from "./logger";
|
||||
|
||||
export function exitApp() {
|
||||
const windowManager = WindowManager.getInstance(false);
|
||||
const clusterManager = ClusterManager.getInstance(false);
|
||||
|
||||
appEventBus.emit({ name: "service", action: "close" });
|
||||
windowManager?.hide();
|
||||
clusterManager?.stop();
|
||||
logger.info("SERVICE:QUIT");
|
||||
setTimeout(() => {
|
||||
app.exit();
|
||||
}, 1000);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { lensExtensionDependencies } from "../../extensions/lens-extension";
|
||||
import type { LensMainExtensionDependencies } from "../../extensions/lens-extension-set-dependencies";
|
||||
import type { LensMainExtension } from "../../extensions/lens-main-extension";
|
||||
import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable";
|
||||
import navigateForExtensionInjectable from "../start-main-application/lens-window/navigate-for-extension.injectable";
|
||||
|
||||
const createExtensionInstanceInjectable = getInjectable({
|
||||
id: "create-extension-instance",
|
||||
@@ -16,6 +17,7 @@ const createExtensionInstanceInjectable = getInjectable({
|
||||
const deps: LensMainExtensionDependencies = {
|
||||
fileSystemProvisionerStore: di.inject(fileSystemProvisionerStoreInjectable),
|
||||
entityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||
navigate: di.inject(navigateForExtensionInjectable),
|
||||
};
|
||||
|
||||
return (ExtensionClass, extension) => {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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 type { Cluster } from "../common/cluster/cluster";
|
||||
import type { IMetricsReqParams } from "../common/k8s-api/endpoints/metrics.api";
|
||||
import k8sRequestInjectable from "./k8s-request.injectable";
|
||||
|
||||
export type GetMetrics = (cluster: Cluster, prometheusPath: string, queryParams: IMetricsReqParams & { query: string }) => Promise<any>;
|
||||
|
||||
const getMetricsInjectable = getInjectable({
|
||||
id: "get-metrics",
|
||||
|
||||
instantiate: (di): GetMetrics => {
|
||||
const k8sRequest = di.inject(k8sRequestInjectable);
|
||||
|
||||
return async (
|
||||
cluster,
|
||||
prometheusPath,
|
||||
queryParams,
|
||||
) => {
|
||||
const prometheusPrefix = cluster.preferences.prometheus?.prefix || "";
|
||||
const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`;
|
||||
|
||||
return k8sRequest(cluster, metricsPath, {
|
||||
timeout: 0,
|
||||
resolveWithFullResponse: false,
|
||||
json: true,
|
||||
method: "POST",
|
||||
form: queryParams,
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default getMetricsInjectable;
|
||||
+11
-14
@@ -4,25 +4,22 @@
|
||||
*/
|
||||
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
|
||||
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
|
||||
export const getDi = () => {
|
||||
const di = createContainer(
|
||||
getRequireContextForMainCode,
|
||||
getRequireContextForCommonExtensionCode,
|
||||
getRequireContextForCommonCode,
|
||||
);
|
||||
const di = createContainer();
|
||||
|
||||
autoRegister({
|
||||
di,
|
||||
requireContexts: [
|
||||
require.context("./", true, /\.injectable\.(ts|tsx)$/),
|
||||
require.context("../extensions", true, /\.injectable\.(ts|tsx)$/),
|
||||
require.context("../common", true, /\.injectable\.(ts|tsx)$/),
|
||||
],
|
||||
});
|
||||
|
||||
setLegacyGlobalDiForExtensionApi(di, Environments.main);
|
||||
|
||||
return di;
|
||||
};
|
||||
|
||||
const getRequireContextForMainCode = () =>
|
||||
require.context("./", true, /\.injectable\.(ts|tsx)$/);
|
||||
|
||||
const getRequireContextForCommonExtensionCode = () =>
|
||||
require.context("../extensions", true, /\.injectable\.(ts|tsx)$/);
|
||||
|
||||
const getRequireContextForCommonCode = () =>
|
||||
require.context("../common", true, /\.injectable\.(ts|tsx)$/);
|
||||
+143
-21
@@ -4,12 +4,10 @@
|
||||
*/
|
||||
|
||||
import glob from "glob";
|
||||
import { kebabCase, memoize } from "lodash/fp";
|
||||
import { kebabCase, memoize, noop } from "lodash/fp";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
|
||||
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import getElectronAppPathInjectable from "./app-paths/get-electron-app-path/get-electron-app-path.injectable";
|
||||
import setElectronAppPathInjectable from "./app-paths/set-electron-app-path/set-electron-app-path.injectable";
|
||||
import appNameInjectable from "./app-paths/app-name/app-name.injectable";
|
||||
import registerChannelInjectable from "./app-paths/register-channel/register-channel.injectable";
|
||||
import writeJsonFileInjectable from "../common/fs/write-json-file.injectable";
|
||||
@@ -26,16 +24,59 @@ import clusterStoreInjectable from "../common/cluster-store/cluster-store.inject
|
||||
import type { ClusterStore } from "../common/cluster-store/cluster-store";
|
||||
import type { Cluster } from "../common/cluster/cluster";
|
||||
import userStoreInjectable from "../common/user-store/user-store.injectable";
|
||||
import isMacInjectable from "../common/vars/is-mac.injectable";
|
||||
import isWindowsInjectable from "../common/vars/is-windows.injectable";
|
||||
import isLinuxInjectable from "../common/vars/is-linux.injectable";
|
||||
import type { UserStore } from "../common/user-store";
|
||||
import getAbsolutePathInjectable from "../common/path/get-absolute-path.injectable";
|
||||
import { getAbsolutePathFake } from "../common/test-utils/get-absolute-path-fake";
|
||||
import joinPathsInjectable from "../common/path/join-paths.injectable";
|
||||
import { joinPathsFake } from "../common/test-utils/join-paths-fake";
|
||||
import hotbarStoreInjectable from "../common/hotbars/store.injectable";
|
||||
import type { GetDiForUnitTestingOptions } from "../test-utils/get-dis-for-unit-testing";
|
||||
import { noop } from "../renderer/utils";
|
||||
import isAutoUpdateEnabledInjectable from "./is-auto-update-enabled.injectable";
|
||||
import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injectable";
|
||||
import { EventEmitter } from "../common/event-emitter";
|
||||
import type { AppEvent } from "../common/app-event-bus/event-bus";
|
||||
import commandLineArgumentsInjectable from "./utils/command-line-arguments.injectable";
|
||||
import initializeExtensionsInjectable from "./start-main-application/runnables/initialize-extensions.injectable";
|
||||
import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable";
|
||||
import registerFileProtocolInjectable from "./electron-app/features/register-file-protocol.injectable";
|
||||
import environmentVariablesInjectable from "../common/utils/environment-variables.injectable";
|
||||
import setupIpcMainHandlersInjectable from "./electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable";
|
||||
import setupLensProxyInjectable from "./start-main-application/runnables/setup-lens-proxy.injectable";
|
||||
import setupRunnablesForAfterRootFrameIsReadyInjectable from "./start-main-application/runnables/setup-runnables-for-after-root-frame-is-ready.injectable";
|
||||
import setupSentryInjectable from "./start-main-application/runnables/setup-sentry.injectable";
|
||||
import setupShellInjectable from "./start-main-application/runnables/setup-shell.injectable";
|
||||
import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable";
|
||||
import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable";
|
||||
import trayInjectable from "./tray/tray.injectable";
|
||||
import applicationMenuInjectable from "./menu/application-menu.injectable";
|
||||
import isDevelopmentInjectable from "../common/vars/is-development.injectable";
|
||||
import setupSystemCaInjectable from "./start-main-application/runnables/setup-system-ca.injectable";
|
||||
import setupDeepLinkingInjectable from "./electron-app/runnables/setup-deep-linking.injectable";
|
||||
import exitAppInjectable from "./electron-app/features/exit-app.injectable";
|
||||
import getCommandLineSwitchInjectable from "./electron-app/features/get-command-line-switch.injectable";
|
||||
import requestSingleInstanceLockInjectable from "./electron-app/features/request-single-instance-lock.injectable";
|
||||
import disableHardwareAccelerationInjectable from "./electron-app/features/disable-hardware-acceleration.injectable";
|
||||
import shouldStartHiddenInjectable from "./electron-app/features/should-start-hidden.injectable";
|
||||
import getElectronAppPathInjectable from "./app-paths/get-electron-app-path/get-electron-app-path.injectable";
|
||||
import setElectronAppPathInjectable from "./app-paths/set-electron-app-path/set-electron-app-path.injectable";
|
||||
import setupMainWindowVisibilityAfterActivationInjectable from "./electron-app/runnables/setup-main-window-visibility-after-activation.injectable";
|
||||
import setupDeviceShutdownInjectable from "./electron-app/runnables/setup-device-shutdown.injectable";
|
||||
import setupApplicationNameInjectable from "./electron-app/runnables/setup-application-name.injectable";
|
||||
import setupRunnablesBeforeClosingOfApplicationInjectable from "./electron-app/runnables/setup-runnables-before-closing-of-application.injectable";
|
||||
import showMessagePopupInjectable from "./electron-app/features/show-message-popup.injectable";
|
||||
import clusterFramesInjectable from "../common/cluster-frames.injectable";
|
||||
import type { ClusterFrameInfo } from "../common/cluster-frames";
|
||||
import { observable } from "mobx";
|
||||
import waitForElectronToBeReadyInjectable from "./electron-app/features/wait-for-electron-to-be-ready.injectable";
|
||||
import setupListenerForCurrentClusterFrameInjectable from "./start-main-application/lens-window/current-cluster-frame/setup-listener-for-current-cluster-frame.injectable";
|
||||
import ipcMainInjectable from "./app-paths/register-channel/ipc-main/ipc-main.injectable";
|
||||
import createElectronWindowForInjectable from "./start-main-application/lens-window/application-window/create-electron-window-for.injectable";
|
||||
import setupRunnablesAfterWindowIsOpenedInjectable from "./electron-app/runnables/setup-runnables-after-window-is-opened.injectable";
|
||||
import sendToChannelInElectronBrowserWindowInjectable from "./start-main-application/lens-window/application-window/send-to-channel-in-electron-browser-window.injectable";
|
||||
import broadcastMessageInjectable from "../common/ipc/broadcast-message.injectable";
|
||||
import getElectronThemeInjectable from "./electron-app/features/get-electron-theme.injectable";
|
||||
import syncThemeFromOperatingSystemInjectable from "./electron-app/features/sync-theme-from-operating-system.injectable";
|
||||
import platformInjectable from "../common/vars/platform.injectable";
|
||||
|
||||
export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) {
|
||||
const {
|
||||
@@ -59,27 +100,39 @@ export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) {
|
||||
di.preventSideEffects();
|
||||
|
||||
if (doGeneralOverrides) {
|
||||
di.override(hotbarStoreInjectable, () => ({}));
|
||||
di.override(userStoreInjectable, () => ({}));
|
||||
di.override(hotbarStoreInjectable, () => ({ load: () => {} }));
|
||||
di.override(userStoreInjectable, () => ({ startMainReactions: () => {} }) as UserStore);
|
||||
di.override(extensionsStoreInjectable, () => ({ isEnabled: (opts) => (void opts, false) }) as ExtensionsStore);
|
||||
di.override(clusterStoreInjectable, () => ({ getById: (id) => (void id, {}) as Cluster }) as ClusterStore);
|
||||
di.override(fileSystemProvisionerStoreInjectable, () => ({}) as FileSystemProvisionerStore);
|
||||
di.override(isMacInjectable, () => true);
|
||||
di.override(isWindowsInjectable, () => false);
|
||||
di.override(isLinuxInjectable, () => false);
|
||||
|
||||
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);
|
||||
di.override(joinPathsInjectable, () => joinPathsFake);
|
||||
overrideOperatingSystem(di);
|
||||
overrideRunnablesHavingSideEffects(di);
|
||||
overrideElectronFeatures(di);
|
||||
|
||||
di.override(
|
||||
getElectronAppPathInjectable,
|
||||
() => (name: string) => `some-electron-app-path-for-${kebabCase(name)}`,
|
||||
);
|
||||
di.override(isDevelopmentInjectable, () => false);
|
||||
di.override(environmentVariablesInjectable, () => ({}));
|
||||
di.override(commandLineArgumentsInjectable, () => []);
|
||||
|
||||
di.override(setElectronAppPathInjectable, () => () => undefined);
|
||||
di.override(appNameInjectable, () => "some-electron-app-name");
|
||||
di.override(clusterFramesInjectable, () => observable.map<string, ClusterFrameInfo>());
|
||||
|
||||
di.override(stopServicesAndExitAppInjectable, () => () => {});
|
||||
di.override(lensResourcesDirInjectable, () => "/irrelevant");
|
||||
|
||||
di.override(trayInjectable, () => ({ start: () => {}, stop: () => {} }));
|
||||
di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} }));
|
||||
|
||||
// TODO: Remove usages of globally exported appEventBus to get rid of this
|
||||
di.override(appEventBusInjectable, () => new EventEmitter<[AppEvent]>());
|
||||
|
||||
di.override(appNameInjectable, () => "some-app-name");
|
||||
di.override(registerChannelInjectable, () => () => undefined);
|
||||
di.override(directoryForBundledBinariesInjectable, () => "some-bin-directory");
|
||||
|
||||
di.override(broadcastMessageInjectable, () => (channel) => {
|
||||
throw new Error(`Tried to broadcast message to channel "${channel}" over IPC without explicit override.`);
|
||||
});
|
||||
|
||||
di.override(spawnInjectable, () => () => {
|
||||
return {
|
||||
stderr: { on: jest.fn(), removeAllListeners: jest.fn() },
|
||||
@@ -117,3 +170,72 @@ const getInjectableFilePaths = memoize(() => [
|
||||
...glob.sync("../extensions/**/*.injectable.{ts,tsx}", { cwd: __dirname }),
|
||||
...glob.sync("../common/**/*.injectable.{ts,tsx}", { cwd: __dirname }),
|
||||
]);
|
||||
|
||||
// TODO: Reorganize code in Runnables to get rid of requirement for override
|
||||
const overrideRunnablesHavingSideEffects = (di: DiContainer) => {
|
||||
[
|
||||
initializeExtensionsInjectable,
|
||||
setupIpcMainHandlersInjectable,
|
||||
setupLensProxyInjectable,
|
||||
setupRunnablesForAfterRootFrameIsReadyInjectable,
|
||||
setupSentryInjectable,
|
||||
setupShellInjectable,
|
||||
setupSyncingOfWeblinksInjectable,
|
||||
setupSystemCaInjectable,
|
||||
setupListenerForCurrentClusterFrameInjectable,
|
||||
setupRunnablesAfterWindowIsOpenedInjectable,
|
||||
].forEach((injectable) => {
|
||||
di.override(injectable, () => ({ run: () => {} }));
|
||||
});
|
||||
};
|
||||
|
||||
const overrideOperatingSystem = (di: DiContainer) => {
|
||||
di.override(platformInjectable, () => "darwin");
|
||||
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);
|
||||
di.override(joinPathsInjectable, () => joinPathsFake);
|
||||
};
|
||||
|
||||
const overrideElectronFeatures = (di: DiContainer) => {
|
||||
di.override(setupMainWindowVisibilityAfterActivationInjectable, () => ({
|
||||
run: () => {},
|
||||
}));
|
||||
|
||||
di.override(setupDeviceShutdownInjectable, () => ({
|
||||
run: () => {},
|
||||
}));
|
||||
|
||||
di.override(setupDeepLinkingInjectable, () => ({ run: () => {} }));
|
||||
di.override(exitAppInjectable, () => () => {});
|
||||
di.override(setupApplicationNameInjectable, () => ({ run: () => {} }));
|
||||
di.override(setupRunnablesBeforeClosingOfApplicationInjectable, () => ({ run: () => {} }));
|
||||
di.override(getCommandLineSwitchInjectable, () => () => "irrelevant");
|
||||
di.override(requestSingleInstanceLockInjectable, () => () => true);
|
||||
di.override(disableHardwareAccelerationInjectable, () => () => {});
|
||||
di.override(shouldStartHiddenInjectable, () => true);
|
||||
di.override(showMessagePopupInjectable, () => () => {});
|
||||
di.override(waitForElectronToBeReadyInjectable, () => () => Promise.resolve());
|
||||
di.override(ipcMainInjectable, () => ({}));
|
||||
di.override(getElectronThemeInjectable, () => () => "dark");
|
||||
di.override(syncThemeFromOperatingSystemInjectable, () => ({ start: () => {}, stop: () => {} }));
|
||||
|
||||
di.override(createElectronWindowForInjectable, () => () => async () => ({
|
||||
show: () => {},
|
||||
|
||||
close: () => {},
|
||||
|
||||
send: (arg) => {
|
||||
const sendFake = di.inject(sendToChannelInElectronBrowserWindowInjectable) as any;
|
||||
|
||||
sendFake(null, arg);
|
||||
},
|
||||
}));
|
||||
|
||||
di.override(
|
||||
getElectronAppPathInjectable,
|
||||
() => (name: string) => `some-electron-app-path-for-${kebabCase(name)}`,
|
||||
);
|
||||
|
||||
di.override(setElectronAppPathInjectable, () => () => {});
|
||||
di.override(isAutoUpdateEnabledInjectable, () => () => false);
|
||||
di.override(registerFileProtocolInjectable, () => () => {});
|
||||
};
|
||||
@@ -17,14 +17,11 @@ describe("Helm Service tests", () => {
|
||||
|
||||
it("list charts with deprecated entries", async () => {
|
||||
mockHelmRepoManager.mockReturnValue({
|
||||
init: jest.fn(),
|
||||
repositories: jest.fn().mockImplementation(async () => {
|
||||
return [
|
||||
{ name: "stable", url: "stableurl" },
|
||||
{ name: "experiment", url: "experimenturl" },
|
||||
];
|
||||
}),
|
||||
});
|
||||
repositories: jest.fn().mockImplementation(async () => [
|
||||
{ name: "stable", url: "stableurl" },
|
||||
{ name: "experiment", url: "experimenturl" },
|
||||
]),
|
||||
} as any);
|
||||
|
||||
const charts = await helmService.listCharts();
|
||||
|
||||
@@ -127,13 +124,12 @@ describe("Helm Service tests", () => {
|
||||
|
||||
it("list charts sorted by version in descending order", async () => {
|
||||
mockHelmRepoManager.mockReturnValue({
|
||||
init: jest.fn(),
|
||||
repositories: jest.fn().mockImplementation(async () => {
|
||||
return [
|
||||
{ name: "bitnami", url: "bitnamiurl" },
|
||||
];
|
||||
}),
|
||||
});
|
||||
} as any);
|
||||
|
||||
const charts = await helmService.listCharts();
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* 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 yaml from "js-yaml";
|
||||
import { readFile } from "fs-extra";
|
||||
import { customRequestPromise } from "../../common/request";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import logger from "../logger";
|
||||
import { execHelm } from "./exec";
|
||||
import type { HelmEnv, HelmRepo, HelmRepoConfig } from "./helm-repo-manager";
|
||||
|
||||
interface EnsuredHelmRepoManagerData {
|
||||
helmEnv: HelmEnv;
|
||||
didUpdateOnce: boolean;
|
||||
}
|
||||
|
||||
export class HelmRepoManager {
|
||||
protected helmEnv?: HelmEnv;
|
||||
protected didUpdateOnce?: boolean;
|
||||
|
||||
public async loadAvailableRepos(): Promise<HelmRepo[]> {
|
||||
const res = await customRequestPromise({
|
||||
uri: "https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json",
|
||||
json: true,
|
||||
resolveWithFullResponse: true,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
return orderBy(res.body as HelmRepo[], repo => repo.name);
|
||||
}
|
||||
|
||||
private async ensureInitialized(): Promise<EnsuredHelmRepoManagerData> {
|
||||
this.helmEnv ??= await this.parseHelmEnv();
|
||||
|
||||
const repos = await this.list(this.helmEnv);
|
||||
|
||||
if (repos.length === 0) {
|
||||
await this.addRepo({
|
||||
name: "bitnami",
|
||||
url: "https://charts.bitnami.com/bitnami",
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.didUpdateOnce) {
|
||||
await this.update();
|
||||
this.didUpdateOnce = true;
|
||||
}
|
||||
|
||||
return {
|
||||
didUpdateOnce: this.didUpdateOnce,
|
||||
helmEnv: this.helmEnv,
|
||||
};
|
||||
}
|
||||
|
||||
protected async parseHelmEnv() {
|
||||
const output = await execHelm(["env"]);
|
||||
const lines = output.split(/\r?\n/); // split by new line feed
|
||||
const env: Partial<Record<string, string>> = {};
|
||||
|
||||
lines.forEach((line: string) => {
|
||||
const [key, value] = line.split("=");
|
||||
|
||||
if (key && value) {
|
||||
env[key] = value.replace(/"/g, ""); // strip quotas
|
||||
}
|
||||
});
|
||||
|
||||
return env as HelmEnv;
|
||||
}
|
||||
|
||||
public async repo(name: string): Promise<HelmRepo | undefined> {
|
||||
const repos = await this.repositories();
|
||||
|
||||
return repos.find(repo => repo.name === name);
|
||||
}
|
||||
|
||||
private async list(helmEnv: HelmEnv): Promise<HelmRepo[]> {
|
||||
try {
|
||||
const rawConfig = await readFile(helmEnv.HELM_REPOSITORY_CONFIG, "utf8");
|
||||
const parsedConfig = yaml.load(rawConfig) as HelmRepoConfig;
|
||||
|
||||
if (typeof parsedConfig === "object" && parsedConfig) {
|
||||
return parsedConfig.repositories;
|
||||
}
|
||||
} catch {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public async repositories(): Promise<HelmRepo[]> {
|
||||
try {
|
||||
const { helmEnv } = await this.ensureInitialized();
|
||||
|
||||
const repos = await this.list(helmEnv);
|
||||
|
||||
return repos.map(repo => ({
|
||||
...repo,
|
||||
cacheFilePath: `${helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`,
|
||||
}));
|
||||
} catch (error) {
|
||||
logger.error(`[HELM]: repositories listing error`, error);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async update() {
|
||||
return execHelm([
|
||||
"repo",
|
||||
"update",
|
||||
]);
|
||||
}
|
||||
|
||||
public async addRepo({ name, url, insecureSkipTlsVerify, username, password, caFile, keyFile, certFile }: HelmRepo) {
|
||||
logger.info(`[HELM]: adding repo ${name} from ${url}`);
|
||||
const args = [
|
||||
"repo",
|
||||
"add",
|
||||
name,
|
||||
url,
|
||||
];
|
||||
|
||||
if (insecureSkipTlsVerify) {
|
||||
args.push("--insecure-skip-tls-verify");
|
||||
}
|
||||
|
||||
if (username) {
|
||||
args.push("--username", username);
|
||||
}
|
||||
|
||||
if (password) {
|
||||
args.push("--password", password);
|
||||
}
|
||||
|
||||
if (caFile) {
|
||||
args.push("--ca-file", caFile);
|
||||
}
|
||||
|
||||
if (keyFile) {
|
||||
args.push("--key-file", keyFile);
|
||||
}
|
||||
|
||||
if (certFile) {
|
||||
args.push("--cert-file", certFile);
|
||||
}
|
||||
|
||||
return execHelm(args);
|
||||
}
|
||||
|
||||
public async removeRepo({ name, url }: HelmRepo): Promise<string> {
|
||||
logger.info(`[HELM]: removing repo ${name} (${url})`);
|
||||
|
||||
return execHelm([
|
||||
"repo",
|
||||
"remove",
|
||||
name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const helmRepoManagerInjectable = getInjectable({
|
||||
id: "helm-repo-manager",
|
||||
|
||||
instantiate: () => new HelmRepoManager(),
|
||||
});
|
||||
|
||||
export default helmRepoManagerInjectable;
|
||||
@@ -3,13 +3,11 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import yaml from "js-yaml";
|
||||
import { readFile } from "fs-extra";
|
||||
import { Singleton } from "../../common/utils/singleton";
|
||||
import { customRequestPromise } from "../../common/request";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import logger from "../logger";
|
||||
import { execHelm } from "./exec";
|
||||
import {
|
||||
asLegacyGlobalSingletonForExtensionApi,
|
||||
} from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-object-for-extension-api";
|
||||
|
||||
import helmRepoManagerInjectable from "./helm-repo-manager.injectable";
|
||||
|
||||
export type HelmEnv = Partial<Record<string, string>> & {
|
||||
HELM_REPOSITORY_CACHE: string;
|
||||
@@ -32,153 +30,4 @@ export interface HelmRepo {
|
||||
password?: string;
|
||||
}
|
||||
|
||||
interface EnsuredHelmRepoManagerData {
|
||||
helmEnv: HelmEnv;
|
||||
didUpdateOnce: boolean;
|
||||
}
|
||||
|
||||
export class HelmRepoManager extends Singleton {
|
||||
protected helmEnv?: HelmEnv;
|
||||
protected didUpdateOnce?: boolean;
|
||||
|
||||
public async loadAvailableRepos(): Promise<HelmRepo[]> {
|
||||
const res = await customRequestPromise({
|
||||
uri: "https://github.com/lensapp/artifact-hub-repositories/releases/download/latest/repositories.json",
|
||||
json: true,
|
||||
resolveWithFullResponse: true,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
return orderBy(res.body as HelmRepo[], repo => repo.name);
|
||||
}
|
||||
|
||||
private async ensureInitialized(): Promise<EnsuredHelmRepoManagerData> {
|
||||
this.helmEnv ??= await this.parseHelmEnv();
|
||||
|
||||
const repos = await this.list(this.helmEnv);
|
||||
|
||||
if (repos.length === 0) {
|
||||
await this.addRepo({
|
||||
name: "bitnami",
|
||||
url: "https://charts.bitnami.com/bitnami",
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.didUpdateOnce) {
|
||||
await this.update();
|
||||
this.didUpdateOnce = true;
|
||||
}
|
||||
|
||||
return {
|
||||
didUpdateOnce: this.didUpdateOnce,
|
||||
helmEnv: this.helmEnv,
|
||||
};
|
||||
}
|
||||
|
||||
protected async parseHelmEnv() {
|
||||
const output = await execHelm(["env"]);
|
||||
const lines = output.split(/\r?\n/); // split by new line feed
|
||||
const env: Partial<Record<string, string>> = {};
|
||||
|
||||
lines.forEach((line: string) => {
|
||||
const [key, value] = line.split("=");
|
||||
|
||||
if (key && value) {
|
||||
env[key] = value.replace(/"/g, ""); // strip quotas
|
||||
}
|
||||
});
|
||||
|
||||
return env as HelmEnv;
|
||||
}
|
||||
|
||||
public async repo(name: string): Promise<HelmRepo | undefined> {
|
||||
const repos = await this.repositories();
|
||||
|
||||
return repos.find(repo => repo.name === name);
|
||||
}
|
||||
|
||||
private async list(helmEnv: HelmEnv): Promise<HelmRepo[]> {
|
||||
try {
|
||||
const rawConfig = await readFile(helmEnv.HELM_REPOSITORY_CONFIG, "utf8");
|
||||
const parsedConfig = yaml.load(rawConfig) as HelmRepoConfig;
|
||||
|
||||
if (typeof parsedConfig === "object" && parsedConfig) {
|
||||
return parsedConfig.repositories;
|
||||
}
|
||||
} catch {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public async repositories(): Promise<HelmRepo[]> {
|
||||
try {
|
||||
const { helmEnv } = await this.ensureInitialized();
|
||||
|
||||
const repos = await this.list(helmEnv);
|
||||
|
||||
return repos.map(repo => ({
|
||||
...repo,
|
||||
cacheFilePath: `${helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`,
|
||||
}));
|
||||
} catch (error) {
|
||||
logger.error(`[HELM]: repositories listing error`, error);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async update() {
|
||||
return execHelm([
|
||||
"repo",
|
||||
"update",
|
||||
]);
|
||||
}
|
||||
|
||||
public async addRepo({ name, url, insecureSkipTlsVerify, username, password, caFile, keyFile, certFile }: HelmRepo) {
|
||||
logger.info(`[HELM]: adding repo ${name} from ${url}`);
|
||||
const args = [
|
||||
"repo",
|
||||
"add",
|
||||
name,
|
||||
url,
|
||||
];
|
||||
|
||||
if (insecureSkipTlsVerify) {
|
||||
args.push("--insecure-skip-tls-verify");
|
||||
}
|
||||
|
||||
if (username) {
|
||||
args.push("--username", username);
|
||||
}
|
||||
|
||||
if (password) {
|
||||
args.push("--password", password);
|
||||
}
|
||||
|
||||
if (caFile) {
|
||||
args.push("--ca-file", caFile);
|
||||
}
|
||||
|
||||
if (keyFile) {
|
||||
args.push("--key-file", keyFile);
|
||||
}
|
||||
|
||||
if (certFile) {
|
||||
args.push("--cert-file", certFile);
|
||||
}
|
||||
|
||||
return execHelm(args);
|
||||
}
|
||||
|
||||
public async removeRepo({ name, url }: HelmRepo): Promise<string> {
|
||||
logger.info(`[HELM]: removing repo ${name} (${url})`);
|
||||
|
||||
return execHelm([
|
||||
"repo",
|
||||
"remove",
|
||||
name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
export const HelmRepoManager = asLegacyGlobalSingletonForExtensionApi(helmRepoManagerInjectable);
|
||||
+7
-357
@@ -5,366 +5,19 @@
|
||||
|
||||
// Main process
|
||||
|
||||
import { injectSystemCAs } from "../common/system-ca";
|
||||
import * as Mobx from "mobx";
|
||||
import httpProxy from "http-proxy";
|
||||
import * as LensExtensionsCommonApi from "../extensions/common-api";
|
||||
import * as LensExtensionsMainApi from "../extensions/main-api";
|
||||
import { app, autoUpdater, dialog, powerMonitor } from "electron";
|
||||
import { appName, isIntegrationTesting, isMac, isWindows, productName, staticFilesDirectory } from "../common/vars";
|
||||
import { LensProxy } from "./lens-proxy";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { ClusterManager } from "./cluster-manager";
|
||||
import { shellSync } from "./shell-sync";
|
||||
import { mangleProxyEnv } from "./proxy-env";
|
||||
import { registerFileProtocol } from "../common/register-protocol";
|
||||
import logger from "./logger";
|
||||
import { appEventBus } from "../common/app-event-bus/event-bus";
|
||||
import type { InstalledExtension } from "../extensions/extension-discovery/extension-discovery";
|
||||
import type { LensExtensionId } from "../extensions/lens-extension";
|
||||
import { installDeveloperTools } from "./developer-tools";
|
||||
import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
||||
import { ipcMainOn } from "../common/ipc";
|
||||
import { startUpdateChecking } from "./app-updater";
|
||||
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
||||
import { startCatalogSyncToRenderer } from "./catalog-pusher";
|
||||
import { catalogEntityRegistry } from "./catalog";
|
||||
import { HelmRepoManager } from "./helm/helm-repo-manager";
|
||||
import { syncWeblinks } from "./catalog-sources";
|
||||
import configurePackages from "../common/configure-packages";
|
||||
import { PrometheusProviderRegistry } from "./prometheus";
|
||||
import * as initializers from "./initializers";
|
||||
import { WeblinkStore } from "../common/weblink-store";
|
||||
import { initializeSentryReporting } from "../common/sentry";
|
||||
import { ensureDir } from "fs-extra";
|
||||
import { initMenu } from "./menu/menu";
|
||||
import { kubeApiUpgradeRequest } from "./proxy-functions";
|
||||
import { initTray } from "./tray/tray";
|
||||
import { ShellSession } from "./shell-session/shell-session";
|
||||
import { getDi } from "./getDi";
|
||||
import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable";
|
||||
import lensProtocolRouterMainInjectable from "./protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable";
|
||||
import extensionDiscoveryInjectable from "../extensions/extension-discovery/extension-discovery.injectable";
|
||||
import directoryForExesInjectable from "../common/app-paths/directory-for-exes/directory-for-exes.injectable";
|
||||
import initIpcMainHandlersInjectable from "./initializers/init-ipc-main-handlers/init-ipc-main-handlers.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import kubeconfigSyncManagerInjectable from "./catalog-sources/kubeconfig-sync/manager.injectable";
|
||||
import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable";
|
||||
import routerInjectable from "./router/router.injectable";
|
||||
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
||||
import userStoreInjectable from "../common/user-store/user-store.injectable";
|
||||
import trayMenuItemsInjectable from "./tray/tray-menu-items.injectable";
|
||||
import { broadcastNativeThemeOnUpdate } from "./native-theme";
|
||||
import assert from "assert";
|
||||
import windowManagerInjectable from "./window-manager.injectable";
|
||||
import navigateToPreferencesInjectable from "../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
|
||||
import syncGeneralCatalogEntitiesInjectable from "./catalog-sources/sync-general-catalog-entities.injectable";
|
||||
import hotbarStoreInjectable from "../common/hotbars/store.injectable";
|
||||
import applicationMenuItemsInjectable from "./menu/application-menu-items.injectable";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { init } from "@sentry/electron/main";
|
||||
import startMainApplicationInjectable from "./start-main-application/start-main-application.injectable";
|
||||
|
||||
async function main(di: DiContainer) {
|
||||
app.setName(appName);
|
||||
const di = getDi();
|
||||
|
||||
/**
|
||||
* Note: this MUST be called before electron's "ready" event has been emitted.
|
||||
*/
|
||||
initializeSentryReporting(init);
|
||||
const startApplication = di.inject(startMainApplicationInjectable);
|
||||
|
||||
await di.runSetups();
|
||||
await app.whenReady();
|
||||
|
||||
injectSystemCAs();
|
||||
|
||||
const onCloseCleanup = disposer();
|
||||
const onQuitCleanup = disposer();
|
||||
|
||||
logger.info(`📟 Setting ${productName} as protocol client for lens://`);
|
||||
|
||||
if (app.setAsDefaultProtocolClient("lens")) {
|
||||
logger.info("📟 Protocol client register succeeded ✅");
|
||||
} else {
|
||||
logger.info("📟 Protocol client register failed ❗");
|
||||
}
|
||||
|
||||
if (process.env.LENS_DISABLE_GPU) {
|
||||
app.disableHardwareAcceleration();
|
||||
}
|
||||
|
||||
logger.debug("[APP-MAIN] configuring packages");
|
||||
configurePackages();
|
||||
|
||||
mangleProxyEnv();
|
||||
|
||||
const initIpcMainHandlers = di.inject(initIpcMainHandlersInjectable);
|
||||
|
||||
logger.debug("[APP-MAIN] initializing ipc main handlers");
|
||||
initIpcMainHandlers();
|
||||
|
||||
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
|
||||
process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server");
|
||||
}
|
||||
|
||||
logger.debug("[APP-MAIN] Lens protocol routing main");
|
||||
|
||||
const lensProtocolRouterMain = di.inject(lensProtocolRouterMainInjectable);
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.exit();
|
||||
} else {
|
||||
for (const arg of process.argv) {
|
||||
if (arg.toLowerCase().startsWith("lens://")) {
|
||||
lensProtocolRouterMain.route(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
broadcastNativeThemeOnUpdate();
|
||||
|
||||
app.on("second-instance", (event, argv) => {
|
||||
logger.debug("second-instance message");
|
||||
|
||||
for (const arg of argv) {
|
||||
if (arg.toLowerCase().startsWith("lens://")) {
|
||||
lensProtocolRouterMain.route(arg);
|
||||
}
|
||||
}
|
||||
|
||||
WindowManager.getInstance(false)?.ensureMainWindow();
|
||||
});
|
||||
|
||||
app.on("activate", (event, hasVisibleWindows) => {
|
||||
logger.info("APP:ACTIVATE", { hasVisibleWindows });
|
||||
|
||||
if (!hasVisibleWindows) {
|
||||
WindowManager.getInstance(false)?.ensureMainWindow(false);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This variable should is used so that `autoUpdater.installAndQuit()` works
|
||||
*/
|
||||
let blockQuit = !isIntegrationTesting;
|
||||
|
||||
autoUpdater.on("before-quit-for-update", () => {
|
||||
logger.debug("Unblocking quit for update");
|
||||
blockQuit = false;
|
||||
});
|
||||
|
||||
app.on("will-quit", (event) => {
|
||||
logger.debug("will-quit message");
|
||||
|
||||
// This is called when the close button of the main window is clicked
|
||||
|
||||
|
||||
logger.info("APP:QUIT");
|
||||
appEventBus.emit({ name: "app", action: "close" });
|
||||
ClusterManager.getInstance(false)?.stop(); // close cluster connections
|
||||
|
||||
const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable);
|
||||
|
||||
kubeConfigSyncManager.stopSync();
|
||||
|
||||
onCloseCleanup();
|
||||
|
||||
// This is set to false here so that LPRM can wait to send future lens://
|
||||
// requests until after it loads again
|
||||
lensProtocolRouterMain.rendererLoaded = false;
|
||||
|
||||
if (blockQuit) {
|
||||
// Quit app on Cmd+Q (MacOS)
|
||||
|
||||
event.preventDefault(); // prevent app's default shutdown (e.g. required for telemetry, etc.)
|
||||
|
||||
return; // skip exit to make tray work, to quit go to app's global menu or tray's menu
|
||||
}
|
||||
|
||||
lensProtocolRouterMain.cleanup();
|
||||
onQuitCleanup();
|
||||
});
|
||||
|
||||
app.on("open-url", (event, rawUrl) => {
|
||||
logger.debug("open-url message");
|
||||
|
||||
// lens:// protocol handler
|
||||
event.preventDefault();
|
||||
lensProtocolRouterMain.route(rawUrl);
|
||||
});
|
||||
|
||||
const directoryForExes = di.inject(directoryForExesInjectable);
|
||||
|
||||
logger.info(`🚀 Starting ${productName} from "${directoryForExes}"`);
|
||||
logger.info("🐚 Syncing shell environment");
|
||||
await shellSync();
|
||||
|
||||
powerMonitor.on("shutdown", () => app.exit());
|
||||
|
||||
registerFileProtocol("static", staticFilesDirectory);
|
||||
|
||||
PrometheusProviderRegistry.createInstance();
|
||||
initializers.initPrometheusProviderRegistry();
|
||||
|
||||
/**
|
||||
* The following sync MUST be done before HotbarStore creation, because that
|
||||
* store has migrations that will remove items that previous migrations add
|
||||
* if this is not present
|
||||
*/
|
||||
const syncGeneralCatalogEntities = di.inject(syncGeneralCatalogEntitiesInjectable);
|
||||
|
||||
syncGeneralCatalogEntities();
|
||||
|
||||
logger.info("💾 Loading stores");
|
||||
|
||||
const userStore = di.inject(userStoreInjectable);
|
||||
|
||||
userStore.startMainReactions();
|
||||
|
||||
// ClusterStore depends on: UserStore
|
||||
const clusterStore = di.inject(clusterStoreInjectable);
|
||||
|
||||
clusterStore.provideInitialFromMain();
|
||||
|
||||
// HotbarStore depends on: ClusterStore
|
||||
di.inject(hotbarStoreInjectable);
|
||||
|
||||
WeblinkStore.createInstance();
|
||||
|
||||
syncWeblinks();
|
||||
|
||||
HelmRepoManager.createInstance(); // create the instance
|
||||
|
||||
const router = di.inject(routerInjectable);
|
||||
const shellApiRequest = di.inject(shellApiRequestInjectable);
|
||||
|
||||
const lensProxy = LensProxy.createInstance(router, httpProxy.createProxy(), {
|
||||
getClusterForRequest: (req) => ClusterManager.getInstance().getClusterForRequest(req),
|
||||
kubeApiUpgradeRequest,
|
||||
shellApiRequest,
|
||||
});
|
||||
|
||||
ClusterManager.createInstance().init();
|
||||
|
||||
initializers.initClusterMetadataDetectors();
|
||||
|
||||
try {
|
||||
logger.info("🔌 Starting LensProxy");
|
||||
await lensProxy.listen(); // lensProxy.port available
|
||||
} catch (error) {
|
||||
dialog.showErrorBox("Lens Error", `Could not start proxy: ${error ? String(error) : "unknown error"}`);
|
||||
|
||||
return app.exit();
|
||||
}
|
||||
|
||||
assert(lensProxy.port, "Lens Proxy failed to start");
|
||||
|
||||
// test proxy connection
|
||||
try {
|
||||
logger.info("🔎 Testing LensProxy connection ...");
|
||||
const versionFromProxy = await getAppVersionFromProxyServer(lensProxy.port);
|
||||
|
||||
if (getAppVersion() !== versionFromProxy) {
|
||||
logger.error("Proxy server responded with invalid response");
|
||||
|
||||
return app.exit();
|
||||
}
|
||||
|
||||
logger.info("⚡ LensProxy connection OK");
|
||||
} catch (error) {
|
||||
logger.error(`🛑 LensProxy: failed connection test: ${error}`);
|
||||
|
||||
const hostsPath = isWindows
|
||||
? "C:\\windows\\system32\\drivers\\etc\\hosts"
|
||||
: "/etc/hosts";
|
||||
const message = [
|
||||
`Failed connection test: ${error}`,
|
||||
"Check to make sure that no other versions of Lens are running",
|
||||
`Check ${hostsPath} to make sure that it is clean and that the localhost loopback is at the top and set to 127.0.0.1`,
|
||||
"If you have HTTP_PROXY or http_proxy set in your environment, make sure that the localhost and the ipv4 loopback address 127.0.0.1 are added to the NO_PROXY environment variable.",
|
||||
];
|
||||
|
||||
dialog.showErrorBox("Lens Proxy Error", message.join("\n\n"));
|
||||
|
||||
return app.exit();
|
||||
}
|
||||
|
||||
const extensionLoader = di.inject(extensionLoaderInjectable);
|
||||
|
||||
extensionLoader.init();
|
||||
|
||||
const extensionDiscovery = di.inject(extensionDiscoveryInjectable);
|
||||
|
||||
extensionDiscovery.init();
|
||||
|
||||
// Start the app without showing the main window when auto starting on login
|
||||
// (On Windows and Linux, we get a flag. On MacOS, we get special API.)
|
||||
const startHidden = process.argv.includes("--hidden") || (isMac && app.getLoginItemSettings().wasOpenedAsHidden);
|
||||
|
||||
logger.info("🖥️ Starting WindowManager");
|
||||
const windowManager = di.inject(windowManagerInjectable);
|
||||
|
||||
const applicationMenuItems = di.inject(applicationMenuItemsInjectable);
|
||||
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
||||
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
||||
|
||||
onQuitCleanup.push(
|
||||
initMenu(applicationMenuItems),
|
||||
initTray(windowManager, trayMenuItems, navigateToPreferences),
|
||||
() => ShellSession.cleanup(),
|
||||
);
|
||||
|
||||
installDeveloperTools();
|
||||
|
||||
if (!startHidden) {
|
||||
windowManager.ensureMainWindow();
|
||||
}
|
||||
|
||||
ipcMainOn(IpcRendererNavigationEvents.LOADED, async () => {
|
||||
onCloseCleanup.push(startCatalogSyncToRenderer(catalogEntityRegistry));
|
||||
|
||||
const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable);
|
||||
|
||||
await ensureDir(directoryForKubeConfigs);
|
||||
|
||||
const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable);
|
||||
|
||||
kubeConfigSyncManager.startSync();
|
||||
|
||||
startUpdateChecking();
|
||||
lensProtocolRouterMain.rendererLoaded = true;
|
||||
});
|
||||
|
||||
logger.info("🧩 Initializing extensions");
|
||||
|
||||
// call after windowManager to see splash earlier
|
||||
try {
|
||||
const extensions = await extensionDiscovery.load();
|
||||
|
||||
// Start watching after bundled extensions are loaded
|
||||
extensionDiscovery.watchExtensions();
|
||||
|
||||
// Subscribe to extensions that are copied or deleted to/from the extensions folder
|
||||
extensionDiscovery.events
|
||||
.on("add", (extension: InstalledExtension) => {
|
||||
extensionLoader.addExtension(extension);
|
||||
})
|
||||
.on("remove", (lensExtensionId: LensExtensionId) => {
|
||||
extensionLoader.removeExtension(lensExtensionId);
|
||||
});
|
||||
|
||||
extensionLoader.initExtensions(extensions);
|
||||
} catch (error) {
|
||||
dialog.showErrorBox("Lens Error", `Could not load extensions${error ? `: ${String(error)}` : ""}`);
|
||||
console.error(error);
|
||||
console.trace();
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
appEventBus.emit({ name: "service", action: "start" });
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
main(getDi());
|
||||
(async () => {
|
||||
await startApplication();
|
||||
})();
|
||||
|
||||
/**
|
||||
* Exports for virtual package "@k8slens/extensions" for main-process.
|
||||
@@ -376,7 +29,4 @@ const LensExtensions = {
|
||||
Main: LensExtensionsMainApi,
|
||||
};
|
||||
|
||||
export {
|
||||
Mobx,
|
||||
LensExtensions,
|
||||
};
|
||||
export { Mobx, LensExtensions };
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { ClusterIdDetector } from "../cluster-detectors/cluster-id-detector";
|
||||
import { DetectorRegistry } from "../cluster-detectors/detector-registry";
|
||||
import { DistributionDetector } from "../cluster-detectors/distribution-detector";
|
||||
import { LastSeenDetector } from "../cluster-detectors/last-seen-detector";
|
||||
import { NodesCountDetector } from "../cluster-detectors/nodes-count-detector";
|
||||
import { VersionDetector } from "../cluster-detectors/version-detector";
|
||||
|
||||
export function initClusterMetadataDetectors() {
|
||||
DetectorRegistry.createInstance()
|
||||
.add(ClusterIdDetector)
|
||||
.add(LastSeenDetector)
|
||||
.add(VersionDetector)
|
||||
.add(DistributionDetector)
|
||||
.add(NodesCountDetector);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
export * from "./metrics-providers";
|
||||
export * from "./cluster-metadata-detectors";
|
||||
@@ -1,21 +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 directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
|
||||
import { initIpcMainHandlers } from "./init-ipc-main-handlers";
|
||||
import getAbsolutePathInjectable from "../../../common/path/get-absolute-path.injectable";
|
||||
import applicationMenuItemsInjectable from "../../menu/application-menu-items.injectable";
|
||||
|
||||
const initIpcMainHandlersInjectable = getInjectable({
|
||||
id: "init-ipc-main-handlers",
|
||||
|
||||
instantiate: (di) => initIpcMainHandlers({
|
||||
applicationMenuItems: di.inject(applicationMenuItemsInjectable),
|
||||
directoryForLensLocalStorage: di.inject(directoryForLensLocalStorageInjectable),
|
||||
getAbsolutePath: di.inject(getAbsolutePathInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default initIpcMainHandlersInjectable;
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { PrometheusProviderRegistry } from "../prometheus";
|
||||
import { PrometheusHelm } from "../prometheus/helm";
|
||||
import { PrometheusLens } from "../prometheus/lens";
|
||||
import { PrometheusOperator } from "../prometheus/operator";
|
||||
import { PrometheusStacklight } from "../prometheus/stacklight";
|
||||
|
||||
export function initPrometheusProviderRegistry() {
|
||||
PrometheusProviderRegistry
|
||||
.getInstance()
|
||||
.registerProvider(new PrometheusLens())
|
||||
.registerProvider(new PrometheusHelm())
|
||||
.registerProvider(new PrometheusOperator())
|
||||
.registerProvider(new PrometheusStacklight());
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { OpenDialogOptions } from "electron";
|
||||
import { dialog } from "electron";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable";
|
||||
|
||||
// TODO: Replace leaking electron with abstraction
|
||||
export type AskUserForFilePaths = (
|
||||
dialogOptions: OpenDialogOptions
|
||||
) => Promise<{ canceled: boolean; filePaths: string[] }>;
|
||||
|
||||
const askUserForFilePathsInjectable = getInjectable({
|
||||
id: "ask-user-for-file-paths",
|
||||
|
||||
instantiate: (di): AskUserForFilePaths => {
|
||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||
|
||||
return async (dialogOptions) => {
|
||||
await showApplicationWindow();
|
||||
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog(
|
||||
dialogOptions,
|
||||
);
|
||||
|
||||
return { canceled, filePaths };
|
||||
};
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default askUserForFilePathsInjectable;
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { OpenDialogOptions } from "electron";
|
||||
import { dialog } from "electron";
|
||||
import { WindowManager } from "../window-manager";
|
||||
|
||||
export async function showOpenDialog(dialogOptions: OpenDialogOptions): Promise<{ canceled: boolean; filePaths: string[] }> {
|
||||
const window = await WindowManager.getInstance().ensureMainWindow();
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog(window, dialogOptions);
|
||||
|
||||
return { canceled, filePaths };
|
||||
}
|
||||
@@ -3,11 +3,16 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { isAutoUpdateEnabled } from "./app-updater";
|
||||
import { isPublishConfigured } from "../common/vars";
|
||||
import { autoUpdater } from "electron-updater";
|
||||
|
||||
const isAutoUpdateEnabledInjectable = getInjectable({
|
||||
id: "is-auto-update-enabled",
|
||||
instantiate: () => isAutoUpdateEnabled,
|
||||
|
||||
instantiate: () => () => {
|
||||
return autoUpdater.isUpdaterActive() && isPublishConfigured;
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { RequestPromiseOptions } from "request-promise-native";
|
||||
import request from "request-promise-native";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import type { Cluster } from "../common/cluster/cluster";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import lensProxyPortInjectable from "./lens-proxy/lens-proxy-port.injectable";
|
||||
|
||||
export type K8sRequest = (cluster: Cluster, path: string, options?: RequestPromiseOptions) => Promise<any>;
|
||||
|
||||
const k8SRequestInjectable = getInjectable({
|
||||
id: "k8s-request",
|
||||
|
||||
instantiate: (di) => {
|
||||
const lensProxyPort = di.inject(lensProxyPortInjectable);
|
||||
|
||||
return async (
|
||||
cluster: Cluster,
|
||||
path: string,
|
||||
options: RequestPromiseOptions = {},
|
||||
) => {
|
||||
const kubeProxyUrl = `http://localhost:${lensProxyPort.get()}${apiKubePrefix}`;
|
||||
|
||||
options.headers ??= {};
|
||||
options.json ??= true;
|
||||
options.timeout ??= 30000;
|
||||
options.headers.Host = `${cluster.id}.${new URL(kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest()
|
||||
|
||||
return request(kubeProxyUrl + path, options);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default k8SRequestInjectable;
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { RequestPromiseOptions } from "request-promise-native";
|
||||
import request from "request-promise-native";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import type { IMetricsReqParams } from "../common/k8s-api/endpoints/metrics.api";
|
||||
import { LensProxy } from "./lens-proxy";
|
||||
import type { Cluster } from "../common/cluster/cluster";
|
||||
|
||||
export async function k8sRequest<T = any>(cluster: Cluster, path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
||||
const kubeProxyUrl = `http://localhost:${LensProxy.getInstance().port}${apiKubePrefix}`;
|
||||
|
||||
options.headers ??= {};
|
||||
options.json ??= true;
|
||||
options.timeout ??= 30000;
|
||||
options.headers.Host = `${cluster.id}.${new URL(kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest()
|
||||
|
||||
return request(kubeProxyUrl + path, options);
|
||||
}
|
||||
|
||||
export async function getMetrics(cluster: Cluster, prometheusPath: string, queryParams: IMetricsReqParams & { query: string }): Promise<any> {
|
||||
const prometheusPrefix = cluster.preferences.prometheus?.prefix || "";
|
||||
const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`;
|
||||
|
||||
return k8sRequest(cluster, metricsPath, {
|
||||
timeout: 0,
|
||||
resolveWithFullResponse: false,
|
||||
json: true,
|
||||
method: "POST",
|
||||
form: queryParams,
|
||||
});
|
||||
}
|
||||
@@ -8,12 +8,13 @@ import directoryForTempInjectable from "../../common/app-paths/directory-for-tem
|
||||
import type { KubeconfigManagerDependencies } from "./kubeconfig-manager";
|
||||
import { KubeconfigManager } from "./kubeconfig-manager";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable";
|
||||
|
||||
export interface KubeConfigManagerInstantiationParameter {
|
||||
cluster: Cluster;
|
||||
}
|
||||
|
||||
export type CreateKubeconfigManager = (cluster: Cluster) => KubeconfigManager | undefined;
|
||||
export type CreateKubeconfigManager = (cluster: Cluster) => KubeconfigManager;
|
||||
|
||||
const createKubeconfigManagerInjectable = getInjectable({
|
||||
id: "create-kubeconfig-manager",
|
||||
@@ -22,6 +23,7 @@ const createKubeconfigManagerInjectable = getInjectable({
|
||||
const dependencies: KubeconfigManagerDependencies = {
|
||||
directoryForTemp: di.inject(directoryForTempInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
lensProxyPort: di.inject(lensProxyPortInjectable),
|
||||
};
|
||||
|
||||
return (cluster) => new KubeconfigManager(dependencies, cluster);
|
||||
|
||||
@@ -9,8 +9,6 @@ import type { ClusterContextHandler } from "../context-handler/context-handler";
|
||||
import path from "path";
|
||||
import fs from "fs-extra";
|
||||
import { dumpConfigYaml } from "../../common/kube-helpers";
|
||||
import logger from "../logger";
|
||||
import { LensProxy } from "../lens-proxy";
|
||||
import { isErrnoException } from "../../common/utils";
|
||||
import type { PartialDeep } from "type-fest";
|
||||
import type { Logger } from "../../common/logger";
|
||||
@@ -18,6 +16,7 @@ import type { Logger } from "../../common/logger";
|
||||
export interface KubeconfigManagerDependencies {
|
||||
readonly directoryForTemp: string;
|
||||
readonly logger: Logger;
|
||||
lensProxyPort: { get: () => number };
|
||||
}
|
||||
|
||||
export class KubeconfigManager {
|
||||
@@ -60,7 +59,7 @@ export class KubeconfigManager {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`[KUBECONFIG-MANAGER]: Deleting temporary kubeconfig: ${this.tempFilePath}`);
|
||||
this.dependencies.logger.info(`[KUBECONFIG-MANAGER]: Deleting temporary kubeconfig: ${this.tempFilePath}`);
|
||||
|
||||
try {
|
||||
await fs.unlink(this.tempFilePath);
|
||||
@@ -84,7 +83,7 @@ export class KubeconfigManager {
|
||||
}
|
||||
|
||||
get resolveProxyUrl() {
|
||||
return `http://127.0.0.1:${LensProxy.getInstance().port}/${this.cluster.id}`;
|
||||
return `http://127.0.0.1:${this.dependencies.lensProxyPort.get()}/${this.cluster.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +123,7 @@ export class KubeconfigManager {
|
||||
|
||||
await fs.ensureDir(path.dirname(tempFile));
|
||||
await fs.writeFile(tempFile, configYaml, { mode: 0o600 });
|
||||
logger.debug(`[KUBECONFIG-MANAGER]: Created temp kubeconfig "${contextName}" at "${tempFile}": \n${configYaml}`);
|
||||
this.dependencies.logger.debug(`[KUBECONFIG-MANAGER]: Created temp kubeconfig "${contextName}" at "${tempFile}": \n${configYaml}`);
|
||||
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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";
|
||||
|
||||
const lensProxyPortInjectable = getInjectable({
|
||||
id: "lens-proxy-port",
|
||||
|
||||
instantiate: () => {
|
||||
let _portNumber: number;
|
||||
|
||||
return {
|
||||
get: () => {
|
||||
if (!_portNumber) {
|
||||
throw new Error(
|
||||
"Tried to access port number of LensProxy while it has not been set yet.",
|
||||
);
|
||||
}
|
||||
|
||||
return _portNumber;
|
||||
},
|
||||
|
||||
set: (portNumber: number) => {
|
||||
if (_portNumber) {
|
||||
throw new Error(
|
||||
"Tried to set port number for LensProxy when it has already been set.",
|
||||
);
|
||||
}
|
||||
|
||||
_portNumber = portNumber;
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default lensProxyPortInjectable;
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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 { LensProxy } from "./lens-proxy";
|
||||
import { kubeApiUpgradeRequest } from "./proxy-functions";
|
||||
import routerInjectable from "../router/router.injectable";
|
||||
import httpProxy from "http-proxy";
|
||||
import clusterManagerInjectable from "../cluster-manager.injectable";
|
||||
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
||||
import lensProxyPortInjectable from "./lens-proxy-port.injectable";
|
||||
|
||||
const lensProxyInjectable = getInjectable({
|
||||
id: "lens-proxy",
|
||||
|
||||
instantiate: (di) => {
|
||||
const clusterManager = di.inject(clusterManagerInjectable);
|
||||
const router = di.inject(routerInjectable);
|
||||
const shellApiRequest = di.inject(shellApiRequestInjectable);
|
||||
const proxy = httpProxy.createProxy();
|
||||
const lensProxyPort = di.inject(lensProxyPortInjectable);
|
||||
|
||||
return new LensProxy({
|
||||
router,
|
||||
proxy,
|
||||
kubeApiUpgradeRequest,
|
||||
shellApiRequest,
|
||||
getClusterForRequest: clusterManager.getClusterForRequest,
|
||||
lensProxyPort,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default lensProxyInjectable;
|
||||
@@ -7,15 +7,14 @@ import net from "net";
|
||||
import type http from "http";
|
||||
import spdy from "spdy";
|
||||
import type httpProxy from "http-proxy";
|
||||
import { apiPrefix, apiKubePrefix } from "../common/vars";
|
||||
import type { Router } from "./router/router";
|
||||
import type { ClusterContextHandler } from "./context-handler/context-handler";
|
||||
import logger from "./logger";
|
||||
import { Singleton } from "../common/utils";
|
||||
import type { Cluster } from "../common/cluster/cluster";
|
||||
import { apiPrefix, apiKubePrefix } from "../../common/vars";
|
||||
import type { Router } from "../router/router";
|
||||
import type { ClusterContextHandler } from "../context-handler/context-handler";
|
||||
import logger from "../logger";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { ProxyApiRequestArgs } from "./proxy-functions";
|
||||
import { appEventBus } from "../common/app-event-bus/event-bus";
|
||||
import { getBoolean } from "./utils/parse-query";
|
||||
import { appEventBus } from "../../common/app-event-bus/event-bus";
|
||||
import { getBoolean } from "../utils/parse-query";
|
||||
import assert from "assert";
|
||||
import type { SetRequired } from "type-fest";
|
||||
|
||||
@@ -23,10 +22,13 @@ type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined;
|
||||
|
||||
export type ServerIncomingMessage = SetRequired<http.IncomingMessage, "url" | "method">;
|
||||
|
||||
export interface LensProxyFunctions {
|
||||
interface Dependencies {
|
||||
getClusterForRequest: GetClusterForRequest;
|
||||
shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
|
||||
kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
|
||||
router: Router;
|
||||
proxy: httpProxy;
|
||||
lensProxyPort: { set: (portNumber: number) => void };
|
||||
}
|
||||
|
||||
const watchParam = "watch";
|
||||
@@ -56,20 +58,13 @@ const disallowedPorts = new Set([
|
||||
10080,
|
||||
]);
|
||||
|
||||
export class LensProxy extends Singleton {
|
||||
export class LensProxy {
|
||||
protected proxyServer: http.Server;
|
||||
protected closed = false;
|
||||
protected retryCounters = new Map<string, number>();
|
||||
protected getClusterForRequest: GetClusterForRequest;
|
||||
|
||||
public port?: number;
|
||||
|
||||
constructor(protected router: Router, protected proxy: httpProxy, { shellApiRequest, kubeApiUpgradeRequest, getClusterForRequest }: LensProxyFunctions) {
|
||||
super();
|
||||
|
||||
this.configureProxy(proxy);
|
||||
|
||||
this.getClusterForRequest = getClusterForRequest;
|
||||
constructor(private dependencies: Dependencies) {
|
||||
this.configureProxy(dependencies.proxy);
|
||||
|
||||
this.proxyServer = spdy.createServer({
|
||||
spdy: {
|
||||
@@ -82,14 +77,14 @@ export class LensProxy extends Singleton {
|
||||
|
||||
this.proxyServer
|
||||
.on("upgrade", (req: ServerIncomingMessage, socket: net.Socket, head: Buffer) => {
|
||||
const cluster = getClusterForRequest(req);
|
||||
const cluster = dependencies.getClusterForRequest(req);
|
||||
|
||||
if (!cluster) {
|
||||
logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`);
|
||||
socket.destroy();
|
||||
} else {
|
||||
const isInternal = req.url.startsWith(`${apiPrefix}?`);
|
||||
const reqHandler = isInternal ? shellApiRequest : kubeApiUpgradeRequest;
|
||||
const reqHandler = isInternal ? dependencies.shellApiRequest : dependencies.kubeApiUpgradeRequest;
|
||||
|
||||
(async () => reqHandler({ req, socket, head, cluster }))()
|
||||
.catch(error => logger.error("[LENS-PROXY]: failed to handle proxy upgrade", error));
|
||||
@@ -113,13 +108,14 @@ export class LensProxy extends Singleton {
|
||||
|
||||
const { address, port } = this.proxyServer.address() as net.AddressInfo;
|
||||
|
||||
this.dependencies.lensProxyPort.set(port);
|
||||
|
||||
logger.info(`[LENS-PROXY]: Proxy server has started at ${address}:${port}`);
|
||||
|
||||
this.proxyServer.on("error", (error) => {
|
||||
logger.info(`[LENS-PROXY]: Subsequent error: ${error}`);
|
||||
});
|
||||
|
||||
this.port = port;
|
||||
appEventBus.emit({ name: "lens-proxy", action: "listen", params: { port }});
|
||||
resolve(port);
|
||||
})
|
||||
@@ -233,16 +229,16 @@ export class LensProxy extends Singleton {
|
||||
}
|
||||
|
||||
protected async handleRequest(req: ServerIncomingMessage, res: http.ServerResponse) {
|
||||
const cluster = this.getClusterForRequest(req);
|
||||
const cluster = this.dependencies.getClusterForRequest(req);
|
||||
|
||||
if (cluster) {
|
||||
const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler);
|
||||
|
||||
if (proxyTarget) {
|
||||
return this.proxy.web(req, res, proxyTarget);
|
||||
return this.dependencies.proxy.web(req, res, proxyTarget);
|
||||
}
|
||||
}
|
||||
|
||||
this.router.route(cluster, req, res);
|
||||
this.dependencies.router.route(cluster, req, res);
|
||||
}
|
||||
}
|
||||
File renamed without changes.
+1
-1
@@ -7,7 +7,7 @@ import { chunk } from "lodash";
|
||||
import type { ConnectionOptions } from "tls";
|
||||
import { connect } from "tls";
|
||||
import url from "url";
|
||||
import { apiKubePrefix } from "../../common/vars";
|
||||
import { apiKubePrefix } from "../../../common/vars";
|
||||
import type { ProxyApiRequestArgs } from "./types";
|
||||
|
||||
const skipRawHeaders = new Set(["Host", "Authorization"]);
|
||||
+4
-3
@@ -4,9 +4,9 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { shellApiRequest } from "./shell-api-request";
|
||||
import createShellSessionInjectable from "../../shell-session/create-shell-session.injectable";
|
||||
import shellRequestAuthenticatorInjectable
|
||||
from "./shell-request-authenticator/shell-request-authenticator.injectable";
|
||||
import createShellSessionInjectable from "../../../shell-session/create-shell-session.injectable";
|
||||
import shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable";
|
||||
import clusterManagerInjectable from "../../../cluster-manager.injectable";
|
||||
|
||||
const shellApiRequestInjectable = getInjectable({
|
||||
id: "shell-api-request",
|
||||
@@ -14,6 +14,7 @@ const shellApiRequestInjectable = getInjectable({
|
||||
instantiate: (di) => shellApiRequest({
|
||||
createShellSession: di.inject(createShellSessionInjectable),
|
||||
authenticateRequest: di.inject(shellRequestAuthenticatorInjectable).authenticate,
|
||||
clusterManager: di.inject(clusterManagerInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
+8
-6
@@ -3,14 +3,14 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import logger from "../../logger";
|
||||
import logger from "../../../logger";
|
||||
import type WebSocket from "ws";
|
||||
import { Server as WebSocketServer } from "ws";
|
||||
import type { ProxyApiRequestArgs } from "../types";
|
||||
import { ClusterManager } from "../../cluster-manager";
|
||||
import type { ClusterManager } from "../../../cluster-manager";
|
||||
import URLParse from "url-parse";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import type { ClusterId } from "../../../common/cluster-types";
|
||||
import type { Cluster } from "../../../../common/cluster/cluster";
|
||||
import type { ClusterId } from "../../../../common/cluster-types";
|
||||
|
||||
interface Dependencies {
|
||||
authenticateRequest: (clusterId: ClusterId, tabId: string, shellToken: string | undefined) => boolean;
|
||||
@@ -21,10 +21,12 @@ interface Dependencies {
|
||||
tabId: string;
|
||||
nodeName?: string;
|
||||
}) => { open: () => Promise<void> };
|
||||
|
||||
clusterManager: ClusterManager;
|
||||
}
|
||||
|
||||
export const shellApiRequest = ({ createShellSession, authenticateRequest }: Dependencies) => ({ req, socket, head }: ProxyApiRequestArgs): void => {
|
||||
const cluster = ClusterManager.getInstance().getClusterForRequest(req);
|
||||
export const shellApiRequest = ({ createShellSession, authenticateRequest, clusterManager }: Dependencies) => ({ req, socket, head }: ProxyApiRequestArgs): void => {
|
||||
const cluster = clusterManager.getClusterForRequest(req);
|
||||
const { query: { node: nodeName, shellToken, id: tabId }} = new URLParse(req.url, true);
|
||||
|
||||
if (!tabId || !cluster || !authenticateRequest(cluster.id, tabId, shellToken)) {
|
||||
File renamed without changes.
+3
-3
@@ -2,9 +2,9 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getOrInsertMap } from "../../../../common/utils";
|
||||
import type { ClusterId } from "../../../../common/cluster-types";
|
||||
import { ipcMainHandle } from "../../../../common/ipc";
|
||||
import { getOrInsertMap } from "../../../../../common/utils";
|
||||
import type { ClusterId } from "../../../../../common/cluster-types";
|
||||
import { ipcMainHandle } from "../../../../../common/ipc";
|
||||
import crypto from "crypto";
|
||||
import { promisify } from "util";
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import type http from "http";
|
||||
import type net from "net";
|
||||
import type { SetRequired } from "type-fest";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
|
||||
export interface ProxyApiRequestArgs {
|
||||
req: SetRequired<http.IncomingMessage, "url" | "method">;
|
||||
@@ -5,11 +5,8 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { checkForUpdates } from "../app-updater";
|
||||
import { docsUrl, productName, supportUrl } from "../../common/vars";
|
||||
import { exitApp } from "../exit-app";
|
||||
import { broadcastMessage } from "../../common/ipc";
|
||||
import { openBrowser } from "../../common/utils";
|
||||
import { showAbout } from "./menu";
|
||||
import windowManagerInjectable from "../window-manager.injectable";
|
||||
import type { MenuItemConstructorOptions } from "electron";
|
||||
import { webContents } from "electron";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
@@ -21,8 +18,13 @@ import navigateToExtensionsInjectable from "../../common/front-end-routing/route
|
||||
import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||
import navigateToWelcomeInjectable from "../../common/front-end-routing/routes/welcome/navigate-to-welcome.injectable";
|
||||
import navigateToAddClusterInjectable from "../../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable";
|
||||
import stopServicesAndExitAppInjectable from "../stop-services-and-exit-app.injectable";
|
||||
import isMacInjectable from "../../common/vars/is-mac.injectable";
|
||||
import { computed } from "mobx";
|
||||
import showAboutInjectable from "./show-about.injectable";
|
||||
import applicationWindowInjectable from "../start-main-application/lens-window/application-window/application-window.injectable";
|
||||
import reloadWindowInjectable from "../start-main-application/lens-window/reload-window.injectable";
|
||||
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable";
|
||||
|
||||
function ignoreIf(check: boolean, menuItems: MenuItemOpts[]) {
|
||||
return check ? [] : menuItems;
|
||||
@@ -41,18 +43,18 @@ const applicationMenuItemsInjectable = getInjectable({
|
||||
const isMac = di.inject(isMacInjectable);
|
||||
const isAutoUpdateEnabled = di.inject(isAutoUpdateEnabledInjectable);
|
||||
const electronMenuItems = di.inject(electronMenuItemsInjectable);
|
||||
const showAbout = di.inject(showAboutInjectable);
|
||||
const applicationWindow = di.inject(applicationWindowInjectable);
|
||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||
const reloadApplicationWindow = di.inject(reloadWindowInjectable, applicationWindow);
|
||||
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
||||
const navigateToExtensions = di.inject(navigateToExtensionsInjectable);
|
||||
const navigateToCatalog = di.inject(navigateToCatalogInjectable);
|
||||
const navigateToWelcome = di.inject(navigateToWelcomeInjectable);
|
||||
const navigateToAddCluster = di.inject(navigateToAddClusterInjectable);
|
||||
const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable);
|
||||
|
||||
return computed((): MenuItemOpts[] => {
|
||||
|
||||
// TODO: These injects should happen outside of the computed.
|
||||
// TODO: Remove temporal dependencies in WindowManager to make sure timing is correct.
|
||||
const windowManager = di.inject(windowManagerInjectable);
|
||||
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
||||
const navigateToExtensions = di.inject(navigateToExtensionsInjectable);
|
||||
const navigateToCatalog = di.inject(navigateToCatalogInjectable);
|
||||
const navigateToWelcome = di.inject(navigateToWelcomeInjectable);
|
||||
const navigateToAddCluster = di.inject(navigateToAddClusterInjectable);
|
||||
|
||||
const autoUpdateDisabled = !isAutoUpdateEnabled();
|
||||
|
||||
logger.info(`[MENU]: autoUpdateDisabled=${autoUpdateDisabled}`);
|
||||
@@ -72,7 +74,7 @@ const applicationMenuItemsInjectable = getInjectable({
|
||||
{
|
||||
label: "Check for updates",
|
||||
click() {
|
||||
checkForUpdates().then(() => windowManager.ensureMainWindow());
|
||||
checkForUpdates().then(() => showApplicationWindow());
|
||||
},
|
||||
},
|
||||
]),
|
||||
@@ -105,7 +107,7 @@ const applicationMenuItemsInjectable = getInjectable({
|
||||
accelerator: "Cmd+Q",
|
||||
id: "quit",
|
||||
click() {
|
||||
exitApp();
|
||||
stopServicesAndExitApp();
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -154,7 +156,7 @@ const applicationMenuItemsInjectable = getInjectable({
|
||||
accelerator: "Alt+F4",
|
||||
id: "quit",
|
||||
click() {
|
||||
exitApp();
|
||||
stopServicesAndExitApp();
|
||||
},
|
||||
},
|
||||
]),
|
||||
@@ -231,7 +233,7 @@ const applicationMenuItemsInjectable = getInjectable({
|
||||
accelerator: "CmdOrCtrl+R",
|
||||
id: "reload",
|
||||
click() {
|
||||
windowManager.reload();
|
||||
reloadApplicationWindow();
|
||||
},
|
||||
},
|
||||
{ role: "toggleDevTools" },
|
||||
@@ -285,7 +287,7 @@ const applicationMenuItemsInjectable = getInjectable({
|
||||
label: "Check for updates",
|
||||
click() {
|
||||
checkForUpdates().then(() =>
|
||||
windowManager.ensureMainWindow(),
|
||||
showApplicationWindow(),
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { autorun } from "mobx";
|
||||
import { buildMenu } from "./menu";
|
||||
import applicationMenuItemsInjectable from "./application-menu-items.injectable";
|
||||
import { getStartableStoppable } from "../../common/utils/get-startable-stoppable";
|
||||
|
||||
const applicationMenuInjectable = getInjectable({
|
||||
id: "application-menu",
|
||||
|
||||
instantiate: (di) => {
|
||||
const applicationMenuItems = di.inject(applicationMenuItemsInjectable);
|
||||
|
||||
return getStartableStoppable("build-of-application-menu", () =>
|
||||
autorun(() => buildMenu(applicationMenuItems.get()), {
|
||||
delay: 100,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default applicationMenuInjectable;
|
||||
@@ -16,7 +16,7 @@ describe("electron-menu-items", () => {
|
||||
let electronMenuItems: IComputedValue<MenuRegistration[]>;
|
||||
let extensionsStub: ObservableMap<string, LensMainExtension>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
extensionsStub = new ObservableMap();
|
||||
@@ -26,8 +26,6 @@ describe("electron-menu-items", () => {
|
||||
() => computed(() => [...extensionsStub.values()]),
|
||||
);
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
electronMenuItems = di.inject(electronMenuItemsInjectable);
|
||||
});
|
||||
|
||||
|
||||
+11
-18
@@ -2,24 +2,19 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { app, dialog, Menu } from "electron";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { autorun } from "mobx";
|
||||
import { app, Menu } from "electron";
|
||||
import { appName, isWindows, productName } from "../../common/vars";
|
||||
import packageJson from "../../../package.json";
|
||||
import type { MenuItemOpts } from "./application-menu-items.injectable";
|
||||
import type { ShowMessagePopup } from "../electron-app/features/show-message-popup.injectable";
|
||||
|
||||
export type MenuTopId = "mac" | "file" | "edit" | "view" | "help";
|
||||
|
||||
export function initMenu(
|
||||
applicationMenuItems: IComputedValue<MenuItemOpts[]>,
|
||||
) {
|
||||
return autorun(() => buildMenu(applicationMenuItems.get()), {
|
||||
delay: 100,
|
||||
});
|
||||
interface Dependencies {
|
||||
showMessagePopup: ShowMessagePopup;
|
||||
}
|
||||
|
||||
export function showAbout() {
|
||||
export const showAbout = ({ showMessagePopup }: Dependencies) => async () => {
|
||||
const appInfo = [
|
||||
`${appName}: ${app.getVersion()}`,
|
||||
`Electron: ${process.versions.electron}`,
|
||||
@@ -28,14 +23,12 @@ export function showAbout() {
|
||||
packageJson.copyright,
|
||||
];
|
||||
|
||||
dialog.showMessageBoxSync({
|
||||
title: `${isWindows ? " ".repeat(2) : ""}${appName}`,
|
||||
type: "info",
|
||||
buttons: ["Close"],
|
||||
message: productName,
|
||||
detail: appInfo.join("\r\n"),
|
||||
});
|
||||
}
|
||||
await showMessagePopup(
|
||||
`${isWindows ? " ".repeat(2) : ""}${appName}`,
|
||||
productName,
|
||||
appInfo.join("\r\n"),
|
||||
);
|
||||
};
|
||||
|
||||
export function buildMenu(applicationMenuItems: MenuItemOpts[]) {
|
||||
Menu.setApplicationMenu(
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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 { showAbout } from "./menu";
|
||||
import showMessagePopupInjectable from "../electron-app/features/show-message-popup.injectable";
|
||||
|
||||
const showAboutInjectable = getInjectable({
|
||||
id: "show-about",
|
||||
|
||||
instantiate: (di) =>
|
||||
showAbout({ showMessagePopup: di.inject(showMessagePopupInjectable) }),
|
||||
});
|
||||
|
||||
export default showAboutInjectable;
|
||||
@@ -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 applicationMenuInjectable from "./application-menu.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
|
||||
const startApplicationMenuInjectable = getInjectable({
|
||||
id: "start-application-menu",
|
||||
|
||||
instantiate: (di) => {
|
||||
const applicationMenu = di.inject(
|
||||
applicationMenuInjectable,
|
||||
);
|
||||
|
||||
return {
|
||||
run: async () => {
|
||||
await applicationMenu.start();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default startApplicationMenuInjectable;
|
||||
@@ -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 applicationMenuInjectable from "./application-menu.injectable";
|
||||
import { beforeQuitOfBackEndInjectionToken } from "../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
|
||||
|
||||
const stopApplicationMenuInjectable = getInjectable({
|
||||
id: "stop-application-menu",
|
||||
|
||||
instantiate: (di) => {
|
||||
const applicationMenu = di.inject(
|
||||
applicationMenuInjectable,
|
||||
);
|
||||
|
||||
return {
|
||||
run: async () => {
|
||||
await applicationMenu.stop();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||
});
|
||||
|
||||
export default stopApplicationMenuInjectable;
|
||||
Loaded 100 of 302 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user