mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Starting attempt to move kubeconfig sync tests to application builder
- Blocked on catalog sync not being injectable yet Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
d2ff35551c
commit
6f8de6ab80
@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getGlobalOverride } from "../../test-utils/get-global-override";
|
import { getGlobalOverride } from "../test-utils/get-global-override";
|
||||||
import watchInjectable from "./watch.injectable";
|
import watchInjectable from "./watch.injectable";
|
||||||
|
|
||||||
export default getGlobalOverride(watchInjectable, () => () => {
|
export default getGlobalOverride(watchInjectable, () => () => {
|
||||||
@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import { watch } from "chokidar";
|
import { watch } from "chokidar";
|
||||||
import type { Stats } from "fs";
|
import type { Stats } from "fs";
|
||||||
import type TypedEventEmitter from "typed-emitter";
|
import type TypedEventEmitter from "typed-emitter";
|
||||||
import type { SingleOrMany } from "../../utils";
|
import type { SingleOrMany } from "../utils";
|
||||||
|
|
||||||
export interface AlwaysStatWatcherEvents {
|
export interface AlwaysStatWatcherEvents {
|
||||||
add: (path: string, stats: Stats) => void;
|
add: (path: string, stats: Stats) => void;
|
||||||
@ -13,7 +13,7 @@ import extensionPackageRootDirectoryInjectable from "../extension-installer/exte
|
|||||||
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
||||||
import loggerInjectable from "../../common/logger.injectable";
|
import loggerInjectable from "../../common/logger.injectable";
|
||||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||||
import watchInjectable from "../../common/fs/watch/watch.injectable";
|
import watchInjectable from "../../common/fs/watch.injectable";
|
||||||
import accessPathInjectable from "../../common/fs/access-path.injectable";
|
import accessPathInjectable from "../../common/fs/access-path.injectable";
|
||||||
import copyInjectable from "../../common/fs/copy.injectable";
|
import copyInjectable from "../../common/fs/copy.injectable";
|
||||||
import ensureDirInjectable from "../../common/fs/ensure-dir.injectable";
|
import ensureDirInjectable from "../../common/fs/ensure-dir.injectable";
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { delay } from "../../renderer/utils";
|
|||||||
import { observable, runInAction, when } from "mobx";
|
import { observable, runInAction, when } from "mobx";
|
||||||
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
||||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||||
import watchInjectable from "../../common/fs/watch/watch.injectable";
|
import watchInjectable from "../../common/fs/watch.injectable";
|
||||||
import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable";
|
import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable";
|
||||||
import removePathInjectable from "../../common/fs/remove.injectable";
|
import removePathInjectable from "../../common/fs/remove.injectable";
|
||||||
import type { JoinPaths } from "../../common/path/join-paths.injectable";
|
import type { JoinPaths } from "../../common/path/join-paths.injectable";
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import { requestInitialExtensionDiscovery } from "../../renderer/ipc";
|
|||||||
import type { ReadJson } from "../../common/fs/read-json-file.injectable";
|
import type { ReadJson } from "../../common/fs/read-json-file.injectable";
|
||||||
import type { Logger } from "../../common/logger";
|
import type { Logger } from "../../common/logger";
|
||||||
import type { PathExists } from "../../common/fs/path-exists.injectable";
|
import type { PathExists } from "../../common/fs/path-exists.injectable";
|
||||||
import type { Watch } from "../../common/fs/watch/watch.injectable";
|
import type { Watch } from "../../common/fs/watch.injectable";
|
||||||
import type { Stats } from "fs";
|
import type { Stats } from "fs";
|
||||||
import type { LStat } from "../../common/fs/lstat.injectable";
|
import type { LStat } from "../../common/fs/lstat.injectable";
|
||||||
import type { ReadDirectory } from "../../common/fs/read-directory.injectable";
|
import type { ReadDirectory } from "../../common/fs/read-directory.injectable";
|
||||||
|
|||||||
@ -0,0 +1,882 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`kubeconfig sync showing reactive catalog renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="ClusterManager"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="topBar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="items"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="preventedDragging"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive disabled focusable"
|
||||||
|
data-testid="home-button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="home"
|
||||||
|
>
|
||||||
|
home
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="size-sm"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="preventedDragging"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive disabled focusable"
|
||||||
|
data-testid="history-back"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_back"
|
||||||
|
>
|
||||||
|
arrow_back
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="size-sm"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="preventedDragging"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive disabled focusable"
|
||||||
|
data-testid="history-forward"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_forward"
|
||||||
|
>
|
||||||
|
arrow_forward
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="separator"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<main>
|
||||||
|
<div
|
||||||
|
id="lens-views"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="flex justify-center Welcome align-center"
|
||||||
|
data-testid="welcome-page"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="welcome-banner-container"
|
||||||
|
style="width: 320px;"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon logo svg focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
|
</i>
|
||||||
|
<div
|
||||||
|
class="flex justify-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="welcome-text-container"
|
||||||
|
style="width: 320px;"
|
||||||
|
>
|
||||||
|
<h2>
|
||||||
|
Welcome to some-product-name!
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
To get you started we have auto-detected your clusters in your
|
||||||
|
|
||||||
|
kubeconfig file and added them to the catalog, your centralized
|
||||||
|
|
||||||
|
view for managing all your cloud-native resources.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
If you have any questions or feedback, please join our
|
||||||
|
<a
|
||||||
|
class="link"
|
||||||
|
href="https://k8slens.dev/slack.html"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Lens Community slack channel
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
<ul
|
||||||
|
class="block"
|
||||||
|
data-testid="welcome-menu-container"
|
||||||
|
style="width: 320px;"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
class="flex grid-12"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon box col-1 material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="view_list"
|
||||||
|
>
|
||||||
|
view_list
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<a
|
||||||
|
class="box col-10"
|
||||||
|
>
|
||||||
|
Browse Clusters in Catalog
|
||||||
|
</a>
|
||||||
|
<i
|
||||||
|
class="Icon box col-1 material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="navigate_next"
|
||||||
|
>
|
||||||
|
navigate_next
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<div
|
||||||
|
class="HotbarMenu flex column"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="HotbarItems flex column gaps"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="z-index: 12; position: absolute;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="HotbarIcon contextMenuAvailable"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Avatar rounded disabled avatar"
|
||||||
|
id="hotbarIcon-hotbar-icon-catalog-entity"
|
||||||
|
style="width: 40px; height: 40px; background: rgb(5, 1, 130);"
|
||||||
|
>
|
||||||
|
Ca
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="1"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="2"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="3"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="4"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="5"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="6"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="7"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="8"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="9"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="10"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="11"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="HotbarSelector"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon Icon previous material interactive focusable"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_left"
|
||||||
|
>
|
||||||
|
arrow_left
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<div
|
||||||
|
class="HotbarIndex"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="badge Badge small clickable"
|
||||||
|
id="hotbarIndex"
|
||||||
|
>
|
||||||
|
1
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive focusable"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_right"
|
||||||
|
>
|
||||||
|
arrow_right
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="StatusBar"
|
||||||
|
data-testid="status-bar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="leftSide"
|
||||||
|
data-testid="status-bar-left"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="rightSide"
|
||||||
|
data-testid="status-bar-right"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`kubeconfig sync showing reactive catalog when navigating to the catalog renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="ClusterManager"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="topBar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="items"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="preventedDragging"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive focusable"
|
||||||
|
data-testid="home-button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="home"
|
||||||
|
>
|
||||||
|
home
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="size-sm"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="preventedDragging"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive disabled focusable"
|
||||||
|
data-testid="history-back"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_back"
|
||||||
|
>
|
||||||
|
arrow_back
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="size-sm"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="preventedDragging"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive disabled focusable"
|
||||||
|
data-testid="history-forward"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_forward"
|
||||||
|
>
|
||||||
|
arrow_forward
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="separator"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<main>
|
||||||
|
<div
|
||||||
|
id="lens-views"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="mainLayout"
|
||||||
|
style="--sidebar-width: 200px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sidebar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-col w-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="catalog"
|
||||||
|
>
|
||||||
|
Catalog
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
aria-multiselectable="false"
|
||||||
|
class="MuiTreeView-root"
|
||||||
|
role="tree"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
aria-selected="true"
|
||||||
|
class="MuiTreeItem-root Mui-selected"
|
||||||
|
data-testid="*-tab"
|
||||||
|
role="treeitem"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-iconContainer"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="MuiTypography-root MuiTreeItem-label MuiTypography-body1"
|
||||||
|
>
|
||||||
|
Browse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
aria-expanded="true"
|
||||||
|
class="MuiTreeItem-root bordered Mui-expanded"
|
||||||
|
role="treeitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-iconContainer"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="expand_more"
|
||||||
|
>
|
||||||
|
expand_more
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiTypography-root MuiTreeItem-label MuiTypography-body1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="parent"
|
||||||
|
>
|
||||||
|
Categories
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
class="MuiCollapse-root MuiTreeItem-group MuiCollapse-entered"
|
||||||
|
role="group"
|
||||||
|
style="min-height: 0px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiCollapse-wrapper"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiCollapse-wrapperInner"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
class="MuiTreeItem-root"
|
||||||
|
data-testid="entity.k8slens.dev/General-tab"
|
||||||
|
role="treeitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-iconContainer"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable small"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="settings"
|
||||||
|
>
|
||||||
|
settings
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiTypography-root MuiTreeItem-label MuiTypography-body1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
General
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="MuiTreeItem-root"
|
||||||
|
data-testid="entity.k8slens.dev/KubernetesCluster-tab"
|
||||||
|
role="treeitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-iconContainer"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon focusable small"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name=""
|
||||||
|
/>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiTypography-root MuiTreeItem-label MuiTypography-body1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Clusters
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="MuiTreeItem-root"
|
||||||
|
data-testid="entity.k8slens.dev/WebLink-tab"
|
||||||
|
role="treeitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiTreeItem-iconContainer"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable small"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="public"
|
||||||
|
>
|
||||||
|
public
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiTypography-root MuiTreeItem-label MuiTypography-body1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Web Links
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ResizingAnchor horizontal trailing"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="contents"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="views"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ItemListLayout flex column Catalog"
|
||||||
|
data-testid="catalog-list-for-browse-all"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="header flex gaps align-center"
|
||||||
|
>
|
||||||
|
<h5
|
||||||
|
class="title"
|
||||||
|
>
|
||||||
|
Browse All
|
||||||
|
</h5>
|
||||||
|
<div
|
||||||
|
class="info-panel box grow"
|
||||||
|
>
|
||||||
|
0 items
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Input SearchInput focused"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="input-area flex gaps align-center"
|
||||||
|
id=""
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
class="input box grow"
|
||||||
|
placeholder="Search..."
|
||||||
|
spellcheck="false"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable small"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="search"
|
||||||
|
>
|
||||||
|
search
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="input-info flex gaps"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="items box grow flex column"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Table flex column Catalog box grow dark selectable scrollable sortable autoSize virtual"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="TableHead sticky nowrap topLine"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="TableCell entityName nowrap sorting"
|
||||||
|
id="name"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell nowrap sorting"
|
||||||
|
id="kind"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Kind
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell sourceCell nowrap sorting"
|
||||||
|
id="source"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Source
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell labelsCell scrollable nowrap"
|
||||||
|
id="labels"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Labels
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell statusCell nowrap sorting"
|
||||||
|
id="status"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Status
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell menu nowrap"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive focusable"
|
||||||
|
id="menu-actions-for-item-object-list-content"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="more_vert"
|
||||||
|
>
|
||||||
|
more_vert
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="NoItems flex box grow"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box center"
|
||||||
|
>
|
||||||
|
Item list is empty
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="AddRemoveButtons flex gaps"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="footer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<div
|
||||||
|
class="HotbarMenu flex column"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="HotbarItems flex column gaps"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="z-index: 12; position: absolute;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="HotbarIcon contextMenuAvailable"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Avatar rounded disabled avatar"
|
||||||
|
id="hotbarIcon-hotbar-icon-catalog-entity"
|
||||||
|
style="width: 40px; height: 40px; background: rgb(5, 1, 130);"
|
||||||
|
>
|
||||||
|
Ca
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="1"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="2"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="3"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="4"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="5"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="6"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="7"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="8"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="9"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="10"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="HotbarCell isDraggingOwner animateDown"
|
||||||
|
index="11"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="HotbarSelector"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon Icon previous material interactive focusable"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_left"
|
||||||
|
>
|
||||||
|
arrow_left
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
<div
|
||||||
|
class="HotbarIndex"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="badge Badge small clickable"
|
||||||
|
id="hotbarIndex"
|
||||||
|
>
|
||||||
|
1
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive focusable"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_right"
|
||||||
|
>
|
||||||
|
arrow_right
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="StatusBar"
|
||||||
|
data-testid="status-bar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="leftSide"
|
||||||
|
data-testid="status-bar-left"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="rightSide"
|
||||||
|
data-testid="status-bar-right"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
80
src/features/kubeconfig-sync/reactive-catalog.test.tsx
Normal file
80
src/features/kubeconfig-sync/reactive-catalog.test.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||||
|
import writeFileInjectable from "../../common/fs/write-file.injectable";
|
||||||
|
import { dumpConfigYaml } from "../../common/kube-helpers";
|
||||||
|
import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable";
|
||||||
|
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
||||||
|
import { flushPromises } from "../../common/test-utils/flush-promises";
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
|
||||||
|
describe("kubeconfig sync showing reactive catalog", () => {
|
||||||
|
let builder: ApplicationBuilder;
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let windowDi: DiContainer;
|
||||||
|
let mainDi: DiContainer;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
builder = getApplicationBuilder();
|
||||||
|
|
||||||
|
// builder.mainDi.override(loggerInjectable, () => console as any);
|
||||||
|
rendered = await builder.render();
|
||||||
|
windowDi = builder.applicationWindow.only.di;
|
||||||
|
mainDi = builder.mainDi;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating to the catalog", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const navigateToCatalog = windowDi.inject(navigateToCatalogInjectable);
|
||||||
|
|
||||||
|
navigateToCatalog();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when a config file is written under ~/.kube", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
const writeFile = mainDi.inject(writeFileInjectable);
|
||||||
|
const joinPaths = mainDi.inject(joinPathsInjectable);
|
||||||
|
const homeDirectoryPath = mainDi.inject(homeDirectoryPathInjectable);
|
||||||
|
|
||||||
|
const configContents = dumpConfigYaml({
|
||||||
|
clusters: [{
|
||||||
|
name: "some-cluster-name",
|
||||||
|
server: "https://1.2.3.4",
|
||||||
|
skipTLSVerify: false,
|
||||||
|
}],
|
||||||
|
users: [{
|
||||||
|
name: "some-user-name",
|
||||||
|
}],
|
||||||
|
contexts: [{
|
||||||
|
cluster: "some-cluster-name",
|
||||||
|
name: "some-context-name",
|
||||||
|
user: "some-user-name",
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
await writeFile(joinPaths(homeDirectoryPath, ".kube", "config"), configContents);
|
||||||
|
await flushPromises();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.only("eventually shows the cluster as a new entity", async () => {
|
||||||
|
await rendered.findByTestId("catalog-entity-row-for-some-cluster-name", undefined, {
|
||||||
|
timeout: 10_000,
|
||||||
|
});
|
||||||
|
}, 100_000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -25,8 +25,8 @@ import type { AsyncFnMock } from "@async-fn/jest";
|
|||||||
import type { Stat } from "../../../common/fs/stat.injectable";
|
import type { Stat } from "../../../common/fs/stat.injectable";
|
||||||
import asyncFn from "@async-fn/jest";
|
import asyncFn from "@async-fn/jest";
|
||||||
import statInjectable from "../../../common/fs/stat.injectable";
|
import statInjectable from "../../../common/fs/stat.injectable";
|
||||||
import type { Watcher } from "../../../common/fs/watch/watch.injectable";
|
import type { Watcher } from "../../../common/fs/watch.injectable";
|
||||||
import watchInjectable from "../../../common/fs/watch/watch.injectable";
|
import watchInjectable from "../../../common/fs/watch.injectable";
|
||||||
import EventEmitter from "events";
|
import EventEmitter from "events";
|
||||||
import type { ReadStream, Stats } from "fs";
|
import type { ReadStream, Stats } from "fs";
|
||||||
import createReadFileStreamInjectable from "../../../common/fs/create-read-file-stream.injectable";
|
import createReadFileStreamInjectable from "../../../common/fs/create-read-file-stream.injectable";
|
||||||
|
|||||||
@ -86,7 +86,7 @@ const computeKubeconfigDiffInjectable = getInjectable({
|
|||||||
|
|
||||||
logger.debug(`Added new cluster from sync`, { filePath, contextName });
|
logger.debug(`Added new cluster from sync`, { filePath, contextName });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(`Failed to create cluster from model: ${error}`, { filePath, contextName });
|
logger.warn(`Failed to create cluster with context="${contextName}" from path="${filePath}"`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* 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 { Stats } from "fs";
|
||||||
|
import watchInjectable from "../../../common/fs/watch.injectable";
|
||||||
|
import loggerInjectable from "../../../common/logger.injectable";
|
||||||
|
|
||||||
|
export interface CreateKubeSyncWatcherOptions {
|
||||||
|
isDirectorySync: boolean;
|
||||||
|
onChange: (filePath: string, stats: Stats) => void;
|
||||||
|
onAdd: (filePath: string, stats: Stats) => void;
|
||||||
|
onRemove: (filePath: string) => void;
|
||||||
|
onError: (error: Error) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KubeSyncWatcher {
|
||||||
|
stop: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreateKubeSyncWatcher = (filePath: string, opts: CreateKubeSyncWatcherOptions) => KubeSyncWatcher;
|
||||||
|
|
||||||
|
const createKubeSyncWatcherInjectable = getInjectable({
|
||||||
|
id: "create-kube-sync-watcher",
|
||||||
|
instantiate: (di): CreateKubeSyncWatcher => {
|
||||||
|
const watch = di.inject(watchInjectable);
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
|
||||||
|
return (filePath, { isDirectorySync, ...handlers }) => {
|
||||||
|
const watcher = watch<true>(filePath, {
|
||||||
|
followSymlinks: true,
|
||||||
|
depth: isDirectorySync ? 0 : 1, // DIRs works with 0 but files need 1 (bug: https://github.com/paulmillr/chokidar/issues/1095)
|
||||||
|
disableGlobbing: true,
|
||||||
|
ignorePermissionErrors: true,
|
||||||
|
usePolling: false,
|
||||||
|
awaitWriteFinish: {
|
||||||
|
pollInterval: 100,
|
||||||
|
stabilityThreshold: 1000,
|
||||||
|
},
|
||||||
|
atomic: 150, // for "atomic writes"
|
||||||
|
alwaysStat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher
|
||||||
|
.on("change", handlers.onChange)
|
||||||
|
.on("add", handlers.onAdd)
|
||||||
|
.on("unlink", handlers.onRemove)
|
||||||
|
.on("error", handlers.onError);
|
||||||
|
|
||||||
|
return {
|
||||||
|
stop: () => {
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
await watcher.close();
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`[KUBE-SYNC-WATCHER]: failed to stop watching "${filePath}": ${error}`);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default createKubeSyncWatcherInjectable;
|
||||||
@ -11,8 +11,8 @@ import { inspect } from "util";
|
|||||||
import type { CatalogEntity } from "../../../common/catalog";
|
import type { CatalogEntity } from "../../../common/catalog";
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import statInjectable from "../../../common/fs/stat.injectable";
|
import statInjectable from "../../../common/fs/stat.injectable";
|
||||||
import type { Watcher } from "../../../common/fs/watch/watch.injectable";
|
import type { KubeSyncWatcher } from "./create-watcher.injectable";
|
||||||
import watchInjectable from "../../../common/fs/watch/watch.injectable";
|
import createKubeSyncWatcherInjectable from "./create-watcher.injectable";
|
||||||
import type { Disposer } from "../../../common/utils";
|
import type { Disposer } from "../../../common/utils";
|
||||||
import { getOrInsertWith, iter } from "../../../common/utils";
|
import { getOrInsertWith, iter } from "../../../common/utils";
|
||||||
import diffChangedKubeconfigInjectable from "./diff-changed-kubeconfig.injectable";
|
import diffChangedKubeconfigInjectable from "./diff-changed-kubeconfig.injectable";
|
||||||
@ -38,8 +38,8 @@ const ignoreGlobs = [
|
|||||||
* Even if you have a cert-file, key-file, and client-cert files that is only
|
* Even if you have a cert-file, key-file, and client-cert files that is only
|
||||||
* 12kb of extra data (at 4096 bytes each) which allows for around 150 entries.
|
* 12kb of extra data (at 4096 bytes each) which allows for around 150 entries.
|
||||||
*/
|
*/
|
||||||
const folderSyncMaxAllowedFileReadSize = 2 * 1024 * 1024; // 2 MiB
|
const dirSyncMaxAllowedFileReadSize = 2 * 1024 * 1024; // 2 MiB
|
||||||
const fileSyncMaxAllowedFileReadSize = 16 * folderSyncMaxAllowedFileReadSize; // 32 MiB
|
const fileSyncMaxAllowedFileReadSize = 16 * dirSyncMaxAllowedFileReadSize; // 32 MiB
|
||||||
|
|
||||||
const watchKubeconfigFileChangesInjectable = getInjectable({
|
const watchKubeconfigFileChangesInjectable = getInjectable({
|
||||||
id: "watch-kubeconfig-file-changes",
|
id: "watch-kubeconfig-file-changes",
|
||||||
@ -47,39 +47,27 @@ const watchKubeconfigFileChangesInjectable = getInjectable({
|
|||||||
const diffChangedKubeconfig = di.inject(diffChangedKubeconfigInjectable);
|
const diffChangedKubeconfig = di.inject(diffChangedKubeconfigInjectable);
|
||||||
const logger = di.inject(kubeconfigSyncLoggerInjectable);
|
const logger = di.inject(kubeconfigSyncLoggerInjectable);
|
||||||
const stat = di.inject(statInjectable);
|
const stat = di.inject(statInjectable);
|
||||||
const watch = di.inject(watchInjectable);
|
const createKubeSyncWatcher = di.inject(createKubeSyncWatcherInjectable);
|
||||||
|
|
||||||
return (filePath) => {
|
return (filePath) => {
|
||||||
const rootSource = observable.map<string, ObservableMap<string, [Cluster, CatalogEntity]>>();
|
const rootSource = observable.map<string, ObservableMap<string, [Cluster, CatalogEntity]>>();
|
||||||
const derivedSource = computed(() => Array.from(iter.flatMap(rootSource.values(), from => iter.map(from.values(), child => child[1]))));
|
const derivedSource = computed(() => Array.from(iter.flatMap(rootSource.values(), from => iter.map(from.values(), child => child[1]))));
|
||||||
|
|
||||||
let watcher: Watcher<true>;
|
let watcher: KubeSyncWatcher;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const stats = await stat(filePath);
|
const stats = await stat(filePath);
|
||||||
const isFolderSync = stats.isDirectory();
|
const isDirectorySync = stats.isDirectory();
|
||||||
const cleanupFns = new Map<string, Disposer>();
|
const cleanupFns = new Map<string, Disposer>();
|
||||||
const maxAllowedFileReadSize = isFolderSync
|
const maxAllowedFileReadSize = isDirectorySync
|
||||||
? folderSyncMaxAllowedFileReadSize
|
? dirSyncMaxAllowedFileReadSize
|
||||||
: fileSyncMaxAllowedFileReadSize;
|
: fileSyncMaxAllowedFileReadSize;
|
||||||
|
|
||||||
watcher = watch<true>(filePath, {
|
watcher = createKubeSyncWatcher(filePath, {
|
||||||
followSymlinks: true,
|
isDirectorySync,
|
||||||
depth: isFolderSync ? 0 : 1, // DIRs works with 0 but files need 1 (bug: https://github.com/paulmillr/chokidar/issues/1095)
|
onChange: (childFilePath, stats): void => {
|
||||||
disableGlobbing: true,
|
console.log("change", childFilePath);
|
||||||
ignorePermissionErrors: true,
|
|
||||||
usePolling: false,
|
|
||||||
awaitWriteFinish: {
|
|
||||||
pollInterval: 100,
|
|
||||||
stabilityThreshold: 1000,
|
|
||||||
},
|
|
||||||
atomic: 150, // for "atomic writes"
|
|
||||||
alwaysStat: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher
|
|
||||||
.on("change", (childFilePath, stats): void => {
|
|
||||||
const cleanup = cleanupFns.get(childFilePath);
|
const cleanup = cleanupFns.get(childFilePath);
|
||||||
|
|
||||||
if (!cleanup) {
|
if (!cleanup) {
|
||||||
@ -94,9 +82,11 @@ const watchKubeconfigFileChangesInjectable = getInjectable({
|
|||||||
stats,
|
stats,
|
||||||
maxAllowedFileReadSize,
|
maxAllowedFileReadSize,
|
||||||
}));
|
}));
|
||||||
})
|
},
|
||||||
.on("add", (childFilePath, stats): void => {
|
onAdd: (childFilePath, stats): void => {
|
||||||
if (isFolderSync) {
|
console.log("add", childFilePath);
|
||||||
|
|
||||||
|
if (isDirectorySync) {
|
||||||
const fileName = path.basename(childFilePath);
|
const fileName = path.basename(childFilePath);
|
||||||
|
|
||||||
for (const ignoreGlob of ignoreGlobs) {
|
for (const ignoreGlob of ignoreGlobs) {
|
||||||
@ -112,20 +102,24 @@ const watchKubeconfigFileChangesInjectable = getInjectable({
|
|||||||
stats,
|
stats,
|
||||||
maxAllowedFileReadSize,
|
maxAllowedFileReadSize,
|
||||||
}));
|
}));
|
||||||
})
|
},
|
||||||
.on("unlink", (childFilePath) => {
|
onRemove: (childFilePath) => {
|
||||||
cleanupFns.get(childFilePath)?.();
|
cleanupFns.get(childFilePath)?.();
|
||||||
cleanupFns.delete(childFilePath);
|
cleanupFns.delete(childFilePath);
|
||||||
rootSource.delete(childFilePath);
|
rootSource.delete(childFilePath);
|
||||||
})
|
},
|
||||||
.on("error", error => logger.error(`watching file/folder failed: ${error}`, { filePath }));
|
onError: (error) => {
|
||||||
|
console.log("error", error);
|
||||||
|
logger.error(`watching file/folder failed: ${error}`, { filePath });
|
||||||
|
},
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(`failed to start watching changes: ${error}`);
|
logger.warn(`failed to start watching changes: ${error}`);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return [derivedSource, () => {
|
return [derivedSource, () => {
|
||||||
watcher?.close();
|
watcher?.stop();
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -307,6 +307,7 @@ class NonInjectedCatalog extends React.Component<Dependencies> {
|
|||||||
getItems={() => catalogEntityStore.entities.get()}
|
getItems={() => catalogEntityStore.entities.get()}
|
||||||
customizeTableRowProps={entity => ({
|
customizeTableRowProps={entity => ({
|
||||||
disabled: !entity.isEnabled(),
|
disabled: !entity.isEnabled(),
|
||||||
|
testId: `catalog-entity-row-for-${entity.getId()}`,
|
||||||
})}
|
})}
|
||||||
{...getCategoryColumns({ activeCategory })}
|
{...getCategoryColumns({ activeCategory })}
|
||||||
onDetails={this.onDetails}
|
onDetails={this.onDetails}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { readFile } from "fs/promises";
|
|||||||
import { hasCorrectExtension } from "./has-correct-extension";
|
import { hasCorrectExtension } from "./has-correct-extension";
|
||||||
import type { RawTemplates } from "./create-resource-templates.injectable";
|
import type { RawTemplates } from "./create-resource-templates.injectable";
|
||||||
import joinPathsInjectable from "../../../../common/path/join-paths.injectable";
|
import joinPathsInjectable from "../../../../common/path/join-paths.injectable";
|
||||||
import watchInjectable from "../../../../common/fs/watch/watch.injectable";
|
import watchInjectable from "../../../../common/fs/watch.injectable";
|
||||||
import getRelativePathInjectable from "../../../../common/path/get-relative-path.injectable";
|
import getRelativePathInjectable from "../../../../common/path/get-relative-path.injectable";
|
||||||
import homeDirectoryPathInjectable from "../../../../common/os/home-directory-path.injectable";
|
import homeDirectoryPathInjectable from "../../../../common/os/home-directory-path.injectable";
|
||||||
import getDirnameOfPathInjectable from "../../../../common/path/get-dirname.injectable";
|
import getDirnameOfPathInjectable from "../../../../common/path/get-dirname.injectable";
|
||||||
|
|||||||
@ -192,7 +192,7 @@ export const getApplicationBuilder = ({ useFakeTime = true }: ApplicationBuilder
|
|||||||
|
|
||||||
const overrideFsWithFakes = getOverrideFsWithFakes();
|
const overrideFsWithFakes = getOverrideFsWithFakes();
|
||||||
|
|
||||||
overrideFsWithFakes(mainDi);
|
overrideFsWithFakes(mainDi, true);
|
||||||
|
|
||||||
// Set up ~/.kube as existing as a folder
|
// Set up ~/.kube as existing as a folder
|
||||||
{
|
{
|
||||||
|
|||||||
@ -10,6 +10,9 @@ import type {
|
|||||||
readJsonSync as readJsonSyncImpl,
|
readJsonSync as readJsonSyncImpl,
|
||||||
writeJsonSync as writeJsonSyncImpl,
|
writeJsonSync as writeJsonSyncImpl,
|
||||||
} from "fs-extra";
|
} from "fs-extra";
|
||||||
|
import createKubeSyncWatcherInjectable from "../main/catalog-sources/kubeconfig-sync/create-watcher.injectable";
|
||||||
|
import { isErrnoException } from "../common/utils";
|
||||||
|
import joinPathsInjectable from "../common/path/join-paths.injectable";
|
||||||
|
|
||||||
export const getOverrideFsWithFakes = () => {
|
export const getOverrideFsWithFakes = () => {
|
||||||
const root = createFsFromVolume(Volume.fromJSON({}));
|
const root = createFsFromVolume(Volume.fromJSON({}));
|
||||||
@ -41,7 +44,7 @@ export const getOverrideFsWithFakes = () => {
|
|||||||
root.mkdirpSync(path, mode);
|
root.mkdirpSync(path, mode);
|
||||||
}) as typeof ensureDirSyncImpl;
|
}) as typeof ensureDirSyncImpl;
|
||||||
|
|
||||||
return (di: DiContainer) => {
|
return (di: DiContainer, overrideWatches = false) => {
|
||||||
di.override(fsInjectable, () => ({
|
di.override(fsInjectable, () => ({
|
||||||
pathExists: async (path) => root.existsSync(path),
|
pathExists: async (path) => root.existsSync(path),
|
||||||
pathExistsSync: root.existsSync,
|
pathExistsSync: root.existsSync,
|
||||||
@ -63,5 +66,76 @@ export const getOverrideFsWithFakes = () => {
|
|||||||
createReadStream: root.createReadStream as any,
|
createReadStream: root.createReadStream as any,
|
||||||
stat: root.promises.stat as any,
|
stat: root.promises.stat as any,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (overrideWatches) {
|
||||||
|
di.override(createKubeSyncWatcherInjectable, (di) => {
|
||||||
|
const joinPaths = di.inject(joinPathsInjectable);
|
||||||
|
|
||||||
|
return ((path, options) => {
|
||||||
|
const watcher = root.watch( path, {
|
||||||
|
recursive: options.isDirectorySync,
|
||||||
|
});
|
||||||
|
const seenPaths = new Set<string>();
|
||||||
|
|
||||||
|
console.log("watching", path);
|
||||||
|
|
||||||
|
watcher.addListener("rename", (eventType, filename: string) => {
|
||||||
|
try {
|
||||||
|
const stats = root.statSync(filename);
|
||||||
|
|
||||||
|
options.onAdd(filename, stats);
|
||||||
|
} catch (error) {
|
||||||
|
if (isErrnoException(error) && error.code === "ENOENT") {
|
||||||
|
options.onRemove(filename);
|
||||||
|
} else {
|
||||||
|
options.onError(error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
watcher.addListener("change", (...args) => {
|
||||||
|
const [,filename] = args;
|
||||||
|
|
||||||
|
if (options.isDirectorySync) {
|
||||||
|
// For testing purposes just emit change events for all files
|
||||||
|
for (const entry of root.readdirSync(filename) as string[]) {
|
||||||
|
const path = joinPaths(filename, entry);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stats = root.statSync(path);
|
||||||
|
|
||||||
|
if (seenPaths.has(path)) {
|
||||||
|
options.onChange(path, stats);
|
||||||
|
} else {
|
||||||
|
seenPaths.add(path);
|
||||||
|
options.onAdd(path, stats);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
options.onError(error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const stats = root.statSync(filename);
|
||||||
|
|
||||||
|
if (seenPaths.has(filename)) {
|
||||||
|
options.onChange(filename, stats);
|
||||||
|
} else {
|
||||||
|
seenPaths.add(filename);
|
||||||
|
options.onAdd(filename, stats);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
options.onError(error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
stop: () => {
|
||||||
|
watcher.close();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user