mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into wrap-pod-logs
This commit is contained in:
commit
66dbcede1c
140 files changed
+15617
-12817
No files matched your search
@@ -80,7 +80,7 @@ describe("CatalogEntityRegistry", () => {
|
||||
let catalogCategoryRegistry: CatalogCategoryRegistry;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting();
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
entityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
catalogCategoryRegistry = di.inject(catalogCategoryRegistryInjectable);
|
||||
|
||||
@@ -43,7 +43,7 @@ describe("Custom Category Columns", () => {
|
||||
let di: DiContainer;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting();
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(hotbarStoreInjectable, () => ({}));
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ describe("Custom Category Views", () => {
|
||||
let di: DiContainer;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting();
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
});
|
||||
|
||||
it("should order items correctly over all extensions", () => {
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import extensionInstallationStateStoreInjectable
|
||||
from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
|
||||
|
||||
const installFromInputInjectable = getInjectable({
|
||||
id: "install-from-input",
|
||||
id: "install-extension-from-input",
|
||||
|
||||
instantiate: (di) =>
|
||||
installFromInput({
|
||||
|
||||
+64
-76
@@ -8,8 +8,8 @@ import "./release-details.scss";
|
||||
import React from "react";
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import { Drawer, DrawerItem, DrawerTitle } from "../../drawer";
|
||||
import { cssNames, stopPropagation } from "../../../utils";
|
||||
import { DrawerItem, DrawerTitle } from "../../drawer";
|
||||
import { stopPropagation } from "../../../utils";
|
||||
import { observer } from "mobx-react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { ConfigurationInput, MinimalResourceGroup, OnlyUserSuppliedValuesAreShownToggle, ReleaseDetailsModel } from "./release-details-model/release-details-model.injectable";
|
||||
@@ -20,7 +20,6 @@ import { Badge } from "../../badge";
|
||||
import { SubTitle } from "../../layout/sub-title";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../../table";
|
||||
import { ReactiveDuration } from "../../duration/reactive-duration";
|
||||
import { HelmReleaseMenu } from "../release-menu";
|
||||
import { Checkbox } from "../../checkbox";
|
||||
import { MonacoEditor } from "../../monaco-editor";
|
||||
import { Spinner } from "../../spinner";
|
||||
@@ -35,97 +34,86 @@ interface Dependencies {
|
||||
}
|
||||
|
||||
const NonInjectedReleaseDetailsContent = observer(({ model }: Dependencies & ReleaseDetailsContentProps) => {
|
||||
const isLoading = model.isLoading.get();
|
||||
const loadingError = model.loadingError.get();
|
||||
|
||||
if (loadingError) {
|
||||
return (
|
||||
<div data-testid="helm-release-detail-error">
|
||||
Failed to load release:
|
||||
{" "}
|
||||
{loadingError}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
className={cssNames("ReleaseDetails", model.activeTheme)}
|
||||
usePortal={true}
|
||||
open={true}
|
||||
title={isLoading ? "" : model.release.getName()}
|
||||
onClose={model.close}
|
||||
testIdForClose="close-helm-release-detail"
|
||||
toolbar={
|
||||
!isLoading && (
|
||||
<HelmReleaseMenu
|
||||
release={model.release}
|
||||
toolbar
|
||||
hideDetails={model.close}
|
||||
<div>
|
||||
<DrawerItem name="Chart" className="chart">
|
||||
<div className="flex gaps align-center">
|
||||
<span>{model.release.chart}</span>
|
||||
|
||||
<Button
|
||||
primary
|
||||
label="Upgrade"
|
||||
className="box right upgrade"
|
||||
onClick={model.startUpgradeProcess}
|
||||
data-testid="helm-release-upgrade-button"
|
||||
/>
|
||||
)
|
||||
}
|
||||
data-testid={`helm-release-details-for-${model.id}`}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Spinner center data-testid="helm-release-detail-content-spinner" />
|
||||
) : (
|
||||
<div>
|
||||
<DrawerItem name="Chart" className="chart">
|
||||
<div className="flex gaps align-center">
|
||||
<span>{model.release.chart}</span>
|
||||
</div>
|
||||
</DrawerItem>
|
||||
|
||||
<Button
|
||||
primary
|
||||
label="Upgrade"
|
||||
className="box right upgrade"
|
||||
onClick={model.startUpgradeProcess}
|
||||
data-testid="helm-release-upgrade-button"
|
||||
/>
|
||||
</div>
|
||||
</DrawerItem>
|
||||
<DrawerItem name="Updated">
|
||||
{`${model.release.getUpdated()} ago (${model.release.updated})`}
|
||||
</DrawerItem>
|
||||
|
||||
<DrawerItem name="Updated">
|
||||
{model.release.getUpdated()}
|
||||
{` ago (${model.release.updated})`}
|
||||
</DrawerItem>
|
||||
<DrawerItem name="Namespace">{model.release.getNs()}</DrawerItem>
|
||||
|
||||
<DrawerItem name="Namespace">{model.release.getNs()}</DrawerItem>
|
||||
<DrawerItem name="Version" onClick={stopPropagation}>
|
||||
<div className="version flex gaps align-center">
|
||||
<span>{model.release.getVersion()}</span>
|
||||
</div>
|
||||
</DrawerItem>
|
||||
|
||||
<DrawerItem name="Version" onClick={stopPropagation}>
|
||||
<div className="version flex gaps align-center">
|
||||
<span>{model.release.getVersion()}</span>
|
||||
</div>
|
||||
</DrawerItem>
|
||||
<DrawerItem
|
||||
name="Status"
|
||||
className="status"
|
||||
labelsOnly>
|
||||
<Badge
|
||||
label={model.release.getStatus()}
|
||||
className={kebabCase(model.release.getStatus())}
|
||||
/>
|
||||
</DrawerItem>
|
||||
|
||||
<DrawerItem
|
||||
name="Status"
|
||||
className="status"
|
||||
labelsOnly>
|
||||
<Badge
|
||||
label={model.release.getStatus()}
|
||||
className={kebabCase(model.release.getStatus())}
|
||||
/>
|
||||
</DrawerItem>
|
||||
<ReleaseValues
|
||||
configuration={model.configuration}
|
||||
onlyUserSuppliedValuesAreShown={
|
||||
model.onlyUserSuppliedValuesAreShown
|
||||
}
|
||||
/>
|
||||
|
||||
<ReleaseValues
|
||||
configuration={model.configuration}
|
||||
onlyUserSuppliedValuesAreShown={
|
||||
model.onlyUserSuppliedValuesAreShown
|
||||
}
|
||||
/>
|
||||
<DrawerTitle>Notes</DrawerTitle>
|
||||
|
||||
<DrawerTitle>Notes</DrawerTitle>
|
||||
{model.notes && <div className="notes">{model.notes}</div>}
|
||||
|
||||
{model.notes && <div className="notes">{model.notes}</div>}
|
||||
<DrawerTitle>Resources</DrawerTitle>
|
||||
|
||||
<DrawerTitle>Resources</DrawerTitle>
|
||||
|
||||
{model.groupedResources.length > 0 && (
|
||||
<div className="resources">
|
||||
{model.groupedResources.map((group) => (
|
||||
<ResourceGroup key={group.kind} group={group} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{model.groupedResources.length > 0 && (
|
||||
<div className="resources">
|
||||
{model.groupedResources.map((group) => (
|
||||
<ResourceGroup key={group.kind} group={group} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const ReleaseDetailsContent = withInjectables<Dependencies, ReleaseDetailsContentProps>(NonInjectedReleaseDetailsContent, {
|
||||
getProps: (di, props) => ({
|
||||
model: di.inject(releaseDetailsModelInjectable, props.targetRelease),
|
||||
getPlaceholder: () => <Spinner center data-testid="helm-release-detail-content-spinner" />,
|
||||
|
||||
getProps: async (di, props) => ({
|
||||
model: await di.inject(releaseDetailsModelInjectable, props.targetRelease),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./release-details.scss";
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { TargetHelmRelease } from "./target-helm-release.injectable";
|
||||
import navigateToHelmReleasesInjectable from "../../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
||||
import type { ReleaseDetailsModel } from "./release-details-model/release-details-model.injectable";
|
||||
import releaseDetailsModelInjectable from "./release-details-model/release-details-model.injectable";
|
||||
import { HelmReleaseMenu } from "../release-menu";
|
||||
|
||||
interface ReleaseDetailsDrawerProps {
|
||||
targetRelease: TargetHelmRelease;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
model: ReleaseDetailsModel;
|
||||
navigateToHelmReleases: () => void;
|
||||
}
|
||||
|
||||
const NonInjectedReleaseDetailsDrawerToolbar = observer(({
|
||||
model,
|
||||
navigateToHelmReleases,
|
||||
}: Dependencies & ReleaseDetailsDrawerProps) => (
|
||||
model.loadingError.get()
|
||||
? null
|
||||
: (
|
||||
<HelmReleaseMenu
|
||||
release={model.release}
|
||||
toolbar
|
||||
hideDetails={navigateToHelmReleases}
|
||||
/>
|
||||
)
|
||||
));
|
||||
|
||||
export const ReleaseDetailsDrawerToolbar = withInjectables<
|
||||
Dependencies,
|
||||
ReleaseDetailsDrawerProps
|
||||
>(NonInjectedReleaseDetailsDrawerToolbar, {
|
||||
getPlaceholder: () => <></>,
|
||||
|
||||
getProps: async (di, props) => ({
|
||||
model: await di.inject(releaseDetailsModelInjectable, props.targetRelease),
|
||||
navigateToHelmReleases: di.inject(navigateToHelmReleasesInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./release-details.scss";
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { Drawer } from "../../drawer";
|
||||
import { cssNames } from "../../../utils";
|
||||
import { observer } from "mobx-react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { TargetHelmRelease } from "./target-helm-release.injectable";
|
||||
import type { ActiveThemeType } from "../../../themes/active-type.injectable";
|
||||
import activeThemeTypeInjectable from "../../../themes/active-type.injectable";
|
||||
import { ReleaseDetailsContent } from "./release-details-content";
|
||||
import navigateToHelmReleasesInjectable from "../../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
||||
import { ReleaseDetailsDrawerToolbar } from "./release-details-drawer-toolbar";
|
||||
|
||||
interface ReleaseDetailsDrawerProps {
|
||||
targetRelease: TargetHelmRelease;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
activeThemeType: ActiveThemeType;
|
||||
closeDrawer: () => void;
|
||||
}
|
||||
|
||||
const NonInjectedReleaseDetailsDrawer = observer(({
|
||||
activeThemeType,
|
||||
closeDrawer,
|
||||
targetRelease,
|
||||
}: Dependencies & ReleaseDetailsDrawerProps) => (
|
||||
<Drawer
|
||||
className={cssNames("ReleaseDetails", activeThemeType.get())}
|
||||
usePortal={true}
|
||||
open={true}
|
||||
title={targetRelease.name}
|
||||
onClose={closeDrawer}
|
||||
testIdForClose="close-helm-release-detail"
|
||||
toolbar={<ReleaseDetailsDrawerToolbar targetRelease={targetRelease} />}
|
||||
data-testid={`helm-release-details-for-${targetRelease.namespace}/${targetRelease.name}`}
|
||||
>
|
||||
<ReleaseDetailsContent targetRelease={targetRelease} />
|
||||
</Drawer>
|
||||
));
|
||||
|
||||
export const ReleaseDetailsDrawer = withInjectables<
|
||||
Dependencies,
|
||||
ReleaseDetailsDrawerProps
|
||||
>(NonInjectedReleaseDetailsDrawer, {
|
||||
getProps: (di, props) => ({
|
||||
activeThemeType: di.inject(activeThemeTypeInjectable),
|
||||
closeDrawer: di.inject(navigateToHelmReleasesInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
+7
-3
@@ -7,6 +7,7 @@ import type { HelmReleaseDto } from "../../../../../../common/k8s-api/endpoints/
|
||||
import callForHelmReleasesInjectable from "../../../call-for-helm-releases/call-for-helm-releases.injectable";
|
||||
import type { HelmReleaseDetails } from "./call-for-helm-release-details/call-for-helm-release-details.injectable";
|
||||
import callForHelmReleaseDetailsInjectable from "./call-for-helm-release-details/call-for-helm-release-details.injectable";
|
||||
import type { AsyncResult } from "../../../../../../common/utils/async-result";
|
||||
|
||||
export interface DetailedHelmRelease {
|
||||
release: HelmReleaseDto;
|
||||
@@ -16,7 +17,7 @@ export interface DetailedHelmRelease {
|
||||
export type CallForHelmRelease = (
|
||||
name: string,
|
||||
namespace: string
|
||||
) => Promise<DetailedHelmRelease | undefined>;
|
||||
) => Promise<AsyncResult<DetailedHelmRelease | undefined>>;
|
||||
|
||||
const callForHelmReleaseInjectable = getInjectable({
|
||||
id: "call-for-helm-release",
|
||||
@@ -36,10 +37,13 @@ const callForHelmReleaseInjectable = getInjectable({
|
||||
);
|
||||
|
||||
if (!release) {
|
||||
return undefined;
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `Release ${name} didn't exist in ${namespace} namespace.`,
|
||||
};
|
||||
}
|
||||
|
||||
return { release, details };
|
||||
return { callWasSuccessful: true, response: { release, details }};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
+25
-32
@@ -26,16 +26,14 @@ import showSuccessNotificationInjectable from "../../../notifications/show-succe
|
||||
import React from "react";
|
||||
import createUpgradeChartTabInjectable from "../../../dock/upgrade-chart/create-upgrade-chart-tab.injectable";
|
||||
import type { HelmRelease } from "../../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import type { NavigateToHelmReleases } from "../../../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
||||
import navigateToHelmReleasesInjectable from "../../../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
||||
import assert from "assert";
|
||||
import withOrphanPromiseInjectable from "../../../../../common/utils/with-orphan-promise/with-orphan-promise.injectable";
|
||||
import activeThemeInjectable from "../../../../themes/active.injectable";
|
||||
|
||||
const releaseDetailsModelInjectable = getInjectable({
|
||||
id: "release-details-model",
|
||||
|
||||
instantiate: (di, targetRelease: TargetHelmRelease) => {
|
||||
instantiate: async (di, targetRelease: TargetHelmRelease) => {
|
||||
const callForHelmRelease = di.inject(callForHelmReleaseInjectable);
|
||||
const callForHelmReleaseConfiguration = di.inject(callForHelmReleaseConfigurationInjectable);
|
||||
const activeTheme = di.inject(activeThemeInjectable);
|
||||
@@ -45,7 +43,6 @@ const releaseDetailsModelInjectable = getInjectable({
|
||||
const showSuccessNotification = di.inject(showSuccessNotificationInjectable);
|
||||
const createUpgradeChartTab = di.inject(createUpgradeChartTabInjectable);
|
||||
const navigateToHelmReleases = di.inject(navigateToHelmReleasesInjectable);
|
||||
const withOrphanPromise = di.inject(withOrphanPromiseInjectable);
|
||||
|
||||
const model = new ReleaseDetailsModel({
|
||||
callForHelmRelease,
|
||||
@@ -60,56 +57,52 @@ const releaseDetailsModelInjectable = getInjectable({
|
||||
navigateToHelmReleases,
|
||||
});
|
||||
|
||||
const load = withOrphanPromise(model.load);
|
||||
|
||||
// TODO: Reorganize Drawer to allow setting of header-bar in children to make "getPlaceholder" from injectable usable.
|
||||
load();
|
||||
await model.load();
|
||||
|
||||
return model;
|
||||
},
|
||||
|
||||
lifecycle: lifecycleEnum.keyedSingleton({
|
||||
getInstanceKey: (di, release: TargetHelmRelease) =>
|
||||
`${release.namespace}/${release.name}`,
|
||||
getInstanceKey: (di, release: TargetHelmRelease) => `${release.namespace}/${release.name}`,
|
||||
}),
|
||||
});
|
||||
|
||||
export default releaseDetailsModelInjectable;
|
||||
|
||||
export interface OnlyUserSuppliedValuesAreShownToggle {
|
||||
value: IObservableValue<boolean>;
|
||||
readonly value: IObservableValue<boolean>;
|
||||
toggle: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface ConfigurationInput {
|
||||
nonSavedValue: IObservableValue<string>;
|
||||
isLoading: IObservableValue<boolean>;
|
||||
isSaving: IObservableValue<boolean>;
|
||||
readonly nonSavedValue: IObservableValue<string>;
|
||||
readonly isLoading: IObservableValue<boolean>;
|
||||
readonly isSaving: IObservableValue<boolean>;
|
||||
onChange: (value: string) => void;
|
||||
save: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
readonly targetRelease: TargetHelmRelease;
|
||||
readonly activeTheme: IComputedValue<LensTheme>;
|
||||
callForHelmRelease: CallForHelmRelease;
|
||||
targetRelease: TargetHelmRelease;
|
||||
activeTheme: IComputedValue<LensTheme>;
|
||||
callForHelmReleaseConfiguration: CallForHelmReleaseConfiguration;
|
||||
getResourceDetailsUrl: GetResourceDetailsUrl;
|
||||
updateRelease: CallForHelmReleaseUpdate;
|
||||
showCheckedErrorNotification: ShowCheckedErrorNotification;
|
||||
showSuccessNotification: ShowNotification;
|
||||
createUpgradeChartTab: (release: HelmRelease) => string;
|
||||
navigateToHelmReleases: NavigateToHelmReleases;
|
||||
navigateToHelmReleases: () => void;
|
||||
}
|
||||
|
||||
export class ReleaseDetailsModel {
|
||||
id = `${this.dependencies.targetRelease.namespace}/${this.dependencies.targetRelease.name}`;
|
||||
readonly id = `${this.dependencies.targetRelease.namespace}/${this.dependencies.targetRelease.name}`;
|
||||
|
||||
constructor(private dependencies: Dependencies) {}
|
||||
constructor(private readonly dependencies: Dependencies) {}
|
||||
|
||||
private detailedRelease = observable.box<DetailedHelmRelease | undefined>();
|
||||
private readonly detailedRelease = observable.box<DetailedHelmRelease | undefined>();
|
||||
|
||||
readonly isLoading = observable.box(false);
|
||||
readonly loadingError = observable.box<string>();
|
||||
|
||||
readonly configuration: ConfigurationInput = {
|
||||
nonSavedValue: observable.box(""),
|
||||
@@ -176,26 +169,26 @@ export class ReleaseDetailsModel {
|
||||
};
|
||||
|
||||
load = async () => {
|
||||
runInAction(() => {
|
||||
this.isLoading.set(true);
|
||||
});
|
||||
|
||||
const { name, namespace } = this.dependencies.targetRelease;
|
||||
|
||||
const detailedRelease = await this.dependencies.callForHelmRelease(
|
||||
const result = await this.dependencies.callForHelmRelease(
|
||||
name,
|
||||
namespace,
|
||||
);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
runInAction(() => {
|
||||
this.loadingError.set(result.error);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
this.detailedRelease.set(detailedRelease);
|
||||
this.detailedRelease.set(result.response);
|
||||
});
|
||||
|
||||
await this.loadConfiguration();
|
||||
|
||||
runInAction(() => {
|
||||
this.isLoading.set(false);
|
||||
});
|
||||
};
|
||||
|
||||
private loadConfiguration = async () => {
|
||||
@@ -269,7 +262,7 @@ export class ReleaseDetailsModel {
|
||||
startUpgradeProcess = () => {
|
||||
this.dependencies.createUpgradeChartTab(this.release);
|
||||
|
||||
this.close();
|
||||
this.dependencies.navigateToHelmReleases();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -49,16 +49,19 @@
|
||||
.TableCell {
|
||||
text-overflow: unset;
|
||||
word-break: break-word;
|
||||
min-width: 100px;
|
||||
|
||||
&.name {
|
||||
flex-basis: auto;
|
||||
flex-grow: 0;
|
||||
width: 230px;
|
||||
flex-grow: 3;
|
||||
}
|
||||
|
||||
&.volume {
|
||||
flex-basis: 30%;
|
||||
&.namespace {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&.age {
|
||||
flex-basis: 100px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import { observer } from "mobx-react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { ReleaseDetailsContent } from "./release-details-content";
|
||||
import type { TargetHelmRelease } from "./target-helm-release.injectable";
|
||||
import targetHelmReleaseInjectable from "./target-helm-release.injectable";
|
||||
import { ReleaseDetailsDrawer } from "./release-details-drawer";
|
||||
|
||||
interface Dependencies {
|
||||
targetRelease: IComputedValue<
|
||||
@@ -25,7 +25,7 @@ const NonInjectedReleaseDetails = observer(
|
||||
({ targetRelease }: Dependencies) => {
|
||||
const release = targetRelease.get();
|
||||
|
||||
return release ? <ReleaseDetailsContent targetRelease={release} /> : null;
|
||||
return release ? <ReleaseDetailsDrawer targetRelease={release} /> : null;
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ const dummyDeployment = new Deployment({
|
||||
},
|
||||
},
|
||||
terminationMessagePath: "dummy",
|
||||
terminationMessagePolicy: "dummy",
|
||||
imagePullPolicy: "dummy",
|
||||
terminationMessagePolicy: "File",
|
||||
imagePullPolicy: "Always",
|
||||
}],
|
||||
restartPolicy: "dummy",
|
||||
terminationGracePeriodSeconds: 10,
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<ContainerEnv /> renders both env and configMapRef envFrom 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem ContainerEnvironment"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Environment
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<div
|
||||
class="variable"
|
||||
>
|
||||
<span
|
||||
class="var-name"
|
||||
>
|
||||
foobar
|
||||
</span>
|
||||
:
|
||||
https://localhost:12345
|
||||
</div>
|
||||
<div
|
||||
class="variable"
|
||||
>
|
||||
<span
|
||||
class="var-name"
|
||||
>
|
||||
configFoo
|
||||
</span>
|
||||
:
|
||||
configBar
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`<ContainerEnv /> renders env 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem ContainerEnvironment"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Environment
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<div
|
||||
class="variable"
|
||||
>
|
||||
<span
|
||||
class="var-name"
|
||||
>
|
||||
foobar
|
||||
</span>
|
||||
:
|
||||
https://localhost:12345
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`<ContainerEnv /> renders env 2`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem ContainerEnvironment"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Environment
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<div
|
||||
class="variable"
|
||||
>
|
||||
<span
|
||||
class="var-name"
|
||||
>
|
||||
foobar
|
||||
</span>
|
||||
:
|
||||
https://localhost:12345
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`<ContainerEnv /> renders envFrom when given a configMapRef 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem ContainerEnvironment"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Environment
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<div
|
||||
class="variable"
|
||||
>
|
||||
<span
|
||||
class="var-name"
|
||||
>
|
||||
configFoo
|
||||
</span>
|
||||
:
|
||||
configBar
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`<ContainerEnv /> renders envFrom when given a secretRef 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="DrawerItem ContainerEnvironment"
|
||||
>
|
||||
<span
|
||||
class="name"
|
||||
>
|
||||
Environment
|
||||
</span>
|
||||
<span
|
||||
class="value"
|
||||
>
|
||||
<div
|
||||
class="variable"
|
||||
>
|
||||
<span
|
||||
class="var-name"
|
||||
>
|
||||
bar
|
||||
</span>
|
||||
:
|
||||
secretKeyRef(my-secret.bar)
|
||||
|
||||
<i
|
||||
class="Icon secret-button material interactive focusable"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
data-icon-name="visibility"
|
||||
>
|
||||
visibility
|
||||
</span>
|
||||
</i>
|
||||
<div>
|
||||
Show
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import type { ConfigMapStore } from "../../+config-maps/store";
|
||||
import configMapStoreInjectable from "../../+config-maps/store.injectable";
|
||||
import type { SecretStore } from "../../+config-secrets/store";
|
||||
import secretStoreInjectable from "../../+config-secrets/store.injectable";
|
||||
import type { Container } from "../../../../common/k8s-api/endpoints";
|
||||
import { Secret, ConfigMap, Pod, SecretType } from "../../../../common/k8s-api/endpoints";
|
||||
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
|
||||
import type { DiRender } from "../../test-utils/renderFor";
|
||||
import { renderFor } from "../../test-utils/renderFor";
|
||||
import { ContainerEnvironment } from "../pod-container-env";
|
||||
|
||||
describe("<ContainerEnv />", () => {
|
||||
let render: DiRender;
|
||||
let secretStore: jest.Mocked<Pick<SecretStore, "load" | "getByName">>;
|
||||
let configMapStore: jest.Mocked<Pick<ConfigMapStore, "load" | "getByName">>;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
secretStore = ({
|
||||
load: jest.fn().mockImplementation(async () => {
|
||||
return {} as Secret;
|
||||
}),
|
||||
getByName: jest.fn(),
|
||||
});
|
||||
configMapStore = ({
|
||||
load: jest.fn().mockImplementation(async () => {
|
||||
return {} as ConfigMap;
|
||||
}),
|
||||
getByName: jest.fn(),
|
||||
});
|
||||
|
||||
di.override(secretStoreInjectable, () => secretStore as jest.Mocked<SecretStore>);
|
||||
di.override(configMapStoreInjectable, () => configMapStore as jest.Mocked<ConfigMapStore>);
|
||||
|
||||
render = renderFor(di);
|
||||
});
|
||||
|
||||
it("renders env", () => {
|
||||
const container: Container = {
|
||||
image: "my-image",
|
||||
name: "my-first-container",
|
||||
env: [{
|
||||
name: "foobar",
|
||||
value: "https://localhost:12345",
|
||||
}],
|
||||
};
|
||||
const pod = new Pod({
|
||||
apiVersion: "v1",
|
||||
kind: "Pod",
|
||||
metadata: {
|
||||
name: "my-pod",
|
||||
namespace: "default",
|
||||
resourceVersion: "1",
|
||||
selfLink: "/api/v1/pods/default/my-pod",
|
||||
uid: "1234",
|
||||
},
|
||||
spec: {
|
||||
containers: [container],
|
||||
},
|
||||
});
|
||||
const result = render(<ContainerEnvironment container={container} namespace={pod.getNs()} />);
|
||||
|
||||
expect(result.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders envFrom when given a configMapRef", () => {
|
||||
configMapStore.getByName.mockImplementation((name, namespace) => {
|
||||
expect(name).toBe("my-config-map");
|
||||
expect(namespace).toBe("default");
|
||||
|
||||
return new ConfigMap({
|
||||
apiVersion: "v1",
|
||||
kind: "ConfigMap",
|
||||
metadata: {
|
||||
name: "my-config-map",
|
||||
namespace: "default",
|
||||
resourceVersion: "2",
|
||||
selfLink: "/api/v1/configmaps/default/my-config-map",
|
||||
uid: "456",
|
||||
},
|
||||
data: {
|
||||
configFoo: "configBar",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const container: Container = {
|
||||
image: "my-image",
|
||||
name: "my-first-container",
|
||||
envFrom: [{
|
||||
configMapRef: {
|
||||
name: "my-config-map",
|
||||
},
|
||||
}],
|
||||
};
|
||||
const pod = new Pod({
|
||||
apiVersion: "v1",
|
||||
kind: "Pod",
|
||||
metadata: {
|
||||
name: "my-pod",
|
||||
namespace: "default",
|
||||
resourceVersion: "1",
|
||||
selfLink: "/api/v1/pods/default/my-pod",
|
||||
uid: "1234",
|
||||
},
|
||||
spec: {
|
||||
containers: [container],
|
||||
},
|
||||
});
|
||||
const result = render(<ContainerEnvironment container={container} namespace={pod.getNs()} />);
|
||||
|
||||
expect(result.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders envFrom when given a secretRef", () => {
|
||||
secretStore.getByName.mockImplementation((name, namespace) => {
|
||||
expect(name).toBe("my-secret");
|
||||
expect(namespace).toBe("default");
|
||||
|
||||
return new Secret({
|
||||
apiVersion: "v1",
|
||||
kind: "Secret",
|
||||
metadata: {
|
||||
name: "my-secret",
|
||||
namespace: "default",
|
||||
resourceVersion: "3",
|
||||
selfLink: "/api/v1/secrets/default/my-secret",
|
||||
uid: "237",
|
||||
},
|
||||
type: SecretType.BasicAuth,
|
||||
data: {
|
||||
bar: "bat",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const container: Container = {
|
||||
image: "my-image",
|
||||
name: "my-first-container",
|
||||
envFrom: [{
|
||||
secretRef: {
|
||||
name: "my-secret",
|
||||
},
|
||||
}],
|
||||
};
|
||||
const pod = new Pod({
|
||||
apiVersion: "v1",
|
||||
kind: "Pod",
|
||||
metadata: {
|
||||
name: "my-pod",
|
||||
namespace: "default",
|
||||
resourceVersion: "1",
|
||||
selfLink: "/api/v1/pods/default/my-pod",
|
||||
uid: "1234",
|
||||
},
|
||||
spec: {
|
||||
containers: [container],
|
||||
},
|
||||
});
|
||||
const result = render(<ContainerEnvironment container={container} namespace={pod.getNs()} />);
|
||||
|
||||
expect(result.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders env", () => {
|
||||
const container: Container = {
|
||||
image: "my-image",
|
||||
name: "my-first-container",
|
||||
env: [{
|
||||
name: "foobar",
|
||||
value: "https://localhost:12345",
|
||||
}],
|
||||
};
|
||||
const pod = new Pod({
|
||||
apiVersion: "v1",
|
||||
kind: "Pod",
|
||||
metadata: {
|
||||
name: "my-pod",
|
||||
namespace: "default",
|
||||
resourceVersion: "1",
|
||||
selfLink: "/api/v1/pods/default/my-pod",
|
||||
uid: "1234",
|
||||
},
|
||||
spec: {
|
||||
containers: [container],
|
||||
},
|
||||
});
|
||||
const result = render(<ContainerEnvironment container={container} namespace={pod.getNs()} />);
|
||||
|
||||
expect(result.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders both env and configMapRef envFrom", () => {
|
||||
configMapStore.getByName.mockImplementation((name, namespace) => {
|
||||
expect(name).toBe("my-config-map");
|
||||
expect(namespace).toBe("default");
|
||||
|
||||
return new ConfigMap({
|
||||
apiVersion: "v1",
|
||||
kind: "ConfigMap",
|
||||
metadata: {
|
||||
name: "my-config-map",
|
||||
namespace: "default",
|
||||
resourceVersion: "2",
|
||||
selfLink: "/api/v1/configmaps/default/my-config-map",
|
||||
uid: "456",
|
||||
},
|
||||
data: {
|
||||
configFoo: "configBar",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const container: Container = {
|
||||
image: "my-image",
|
||||
name: "my-first-container",
|
||||
envFrom: [{
|
||||
configMapRef: {
|
||||
name: "my-config-map",
|
||||
},
|
||||
}],
|
||||
env: [{
|
||||
name: "foobar",
|
||||
value: "https://localhost:12345",
|
||||
}],
|
||||
};
|
||||
const pod = new Pod({
|
||||
apiVersion: "v1",
|
||||
kind: "Pod",
|
||||
metadata: {
|
||||
name: "my-pod",
|
||||
namespace: "default",
|
||||
resourceVersion: "1",
|
||||
selfLink: "/api/v1/pods/default/my-pod",
|
||||
uid: "1234",
|
||||
},
|
||||
spec: {
|
||||
containers: [container],
|
||||
},
|
||||
});
|
||||
const result = render(<ContainerEnvironment container={container} namespace={pod.getNs()} />);
|
||||
|
||||
expect(result.baseElement).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -7,26 +7,39 @@ import "./pod-container-env.scss";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { PodContainer, Secret } from "../../../common/k8s-api/endpoints";
|
||||
import type { Container, EnvVarKeySelector, Secret } from "../../../common/k8s-api/endpoints";
|
||||
import { DrawerItem } from "../drawer";
|
||||
import { autorun } from "mobx";
|
||||
import { secretStore } from "../+config-secrets/legacy-store";
|
||||
import { configMapStore } from "../+config-maps/legacy-store";
|
||||
import { Icon } from "../icon";
|
||||
import { base64, cssNames, iter } from "../../utils";
|
||||
import { base64, cssNames, object } from "../../utils";
|
||||
import _ from "lodash";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { ConfigMapStore } from "../+config-maps/store";
|
||||
import type { SecretStore } from "../+config-secrets/store";
|
||||
import configMapStoreInjectable from "../+config-maps/store.injectable";
|
||||
import secretStoreInjectable from "../+config-secrets/store.injectable";
|
||||
|
||||
export interface ContainerEnvironmentProps {
|
||||
container: PodContainer;
|
||||
container: Container;
|
||||
namespace: string;
|
||||
}
|
||||
|
||||
export const ContainerEnvironment = observer((props: ContainerEnvironmentProps) => {
|
||||
const { container: { env, envFrom = [] }, namespace } = props;
|
||||
interface Dependencies {
|
||||
configMapStore: ConfigMapStore;
|
||||
secretStore: SecretStore;
|
||||
}
|
||||
|
||||
const NonInjectedContainerEnvironment = observer((props: Dependencies & ContainerEnvironmentProps) => {
|
||||
const {
|
||||
container: { env, envFrom = [] },
|
||||
namespace,
|
||||
configMapStore,
|
||||
secretStore,
|
||||
} = props;
|
||||
|
||||
useEffect( () => autorun(() => {
|
||||
for (const { valueFrom } of env ?? []) {
|
||||
if (valueFrom?.configMapKeyRef) {
|
||||
if (valueFrom?.configMapKeyRef?.name) {
|
||||
configMapStore.load({ name: valueFrom.configMapKeyRef.name, namespace });
|
||||
}
|
||||
}
|
||||
@@ -51,100 +64,97 @@ export const ContainerEnvironment = observer((props: ContainerEnvironmentProps)
|
||||
|
||||
if (value) {
|
||||
secretValue = value;
|
||||
}
|
||||
|
||||
if (valueFrom) {
|
||||
} else if (valueFrom) {
|
||||
const { fieldRef, secretKeyRef, configMapKeyRef } = valueFrom;
|
||||
|
||||
if (fieldRef) {
|
||||
const { apiVersion, fieldPath } = fieldRef;
|
||||
|
||||
secretValue = `fieldRef(${apiVersion}:${fieldPath})`;
|
||||
}
|
||||
|
||||
if (secretKeyRef) {
|
||||
} else if (secretKeyRef?.name) {
|
||||
secretValue = (
|
||||
<SecretKey
|
||||
reference={secretKeyRef}
|
||||
namespace={namespace}
|
||||
secretStore={secretStore}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (configMapKeyRef) {
|
||||
} else if (configMapKeyRef?.name) {
|
||||
const { name, key } = configMapKeyRef;
|
||||
const configMap = configMapStore.getByName(name, namespace);
|
||||
|
||||
secretValue = configMap ?
|
||||
configMap.data[key] :
|
||||
`configMapKeyRef(${name}${key})`;
|
||||
secretValue = configMap
|
||||
? configMap.data[key]
|
||||
: `configMapKeyRef(${name}${key})`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="variable" key={name}>
|
||||
<span className="var-name">{name}</span>
|
||||
:
|
||||
{` : `}
|
||||
{secretValue}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const renderEnvFrom = () => {
|
||||
return Array.from(iter.filterFlatMap(envFrom, vars => {
|
||||
if (vars.configMapRef?.name) {
|
||||
return renderEnvFromConfigMap(vars.configMapRef.name);
|
||||
}
|
||||
const renderEnvFrom = () => (
|
||||
envFrom
|
||||
.flatMap(({ configMapRef, secretRef, prefix }) => {
|
||||
if (configMapRef?.name) {
|
||||
return renderEnvFromConfigMap(configMapRef.name, prefix);
|
||||
}
|
||||
|
||||
if (vars.secretRef?.name) {
|
||||
return renderEnvFromSecret(vars.secretRef.name);
|
||||
}
|
||||
if (secretRef?.name) {
|
||||
return renderEnvFromSecret(secretRef.name, prefix);
|
||||
}
|
||||
|
||||
return null;
|
||||
}));
|
||||
};
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
const renderEnvFromConfigMap = (configMapName: string) => {
|
||||
const renderEnvFromConfigMap = (configMapName: string, prefix: string | undefined) => {
|
||||
const configMap = configMapStore.getByName(configMapName, namespace);
|
||||
|
||||
if (!configMap) return null;
|
||||
|
||||
return Object.entries(configMap.data).map(([name, value]) => (
|
||||
<div className="variable" key={name}>
|
||||
<span className="var-name">{name}</span>
|
||||
:
|
||||
{value}
|
||||
</div>
|
||||
));
|
||||
return object.entries(configMap.data)
|
||||
.map(([name, value]) => (
|
||||
<div className="variable" key={name}>
|
||||
<span className="var-name">
|
||||
{prefix}
|
||||
{name}
|
||||
</span>
|
||||
{` : `}
|
||||
{value}
|
||||
</div>
|
||||
));
|
||||
};
|
||||
|
||||
const renderEnvFromSecret = (secretName: string) => {
|
||||
const renderEnvFromSecret = (secretName: string, prefix: string | undefined) => {
|
||||
const secret = secretStore.getByName(secretName, namespace);
|
||||
|
||||
if (!secret) return null;
|
||||
|
||||
return Object.keys(secret.data).map(key => {
|
||||
const secretKeyRef = {
|
||||
name: secret.getName(),
|
||||
key,
|
||||
};
|
||||
|
||||
const value = (
|
||||
<SecretKey
|
||||
reference={secretKeyRef}
|
||||
namespace={namespace}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
return Object.keys(secret.data)
|
||||
.map(key => (
|
||||
<div className="variable" key={key}>
|
||||
<span className="var-name">{key}</span>
|
||||
:
|
||||
{value}
|
||||
<span className="var-name">
|
||||
{prefix}
|
||||
{key}
|
||||
</span>
|
||||
{` : `}
|
||||
<SecretKey
|
||||
reference={{
|
||||
name: secret.getName(),
|
||||
key,
|
||||
}}
|
||||
namespace={namespace}
|
||||
secretStore={secretStore}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -155,19 +165,34 @@ export const ContainerEnvironment = observer((props: ContainerEnvironmentProps)
|
||||
);
|
||||
});
|
||||
|
||||
export interface SecretKeyProps {
|
||||
reference: {
|
||||
name: string;
|
||||
key: string;
|
||||
};
|
||||
export const ContainerEnvironment = withInjectables<Dependencies, ContainerEnvironmentProps>(NonInjectedContainerEnvironment, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
configMapStore: di.inject(configMapStoreInjectable),
|
||||
secretStore: di.inject(secretStoreInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
interface SecretKeyProps {
|
||||
reference: EnvVarKeySelector;
|
||||
namespace: string;
|
||||
secretStore: SecretStore;
|
||||
}
|
||||
|
||||
const SecretKey = (props: SecretKeyProps) => {
|
||||
const { reference: { name, key }, namespace } = props;
|
||||
const {
|
||||
reference: { name, key },
|
||||
namespace,
|
||||
secretStore,
|
||||
} = props;
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [secret, setSecret] = useState<Secret>();
|
||||
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const showKey = async (evt: React.MouseEvent) => {
|
||||
evt.preventDefault();
|
||||
setLoading(true);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import "./pod-details-container.scss";
|
||||
|
||||
import React from "react";
|
||||
import type { PodContainer, PodContainerStatus, Pod } from "../../../common/k8s-api/endpoints";
|
||||
import type { Container, PodContainerStatus, Pod } from "../../../common/k8s-api/endpoints";
|
||||
import { DrawerItem } from "../drawer";
|
||||
import { cssNames, isDefined } from "../../utils";
|
||||
import { StatusBrick } from "../status-brick";
|
||||
@@ -27,7 +27,7 @@ import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-activ
|
||||
|
||||
export interface PodDetailsContainerProps {
|
||||
pod: Pod;
|
||||
container: PodContainer;
|
||||
container: Container;
|
||||
metrics?: Partial<Record<string, MetricData>>;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,12 +43,12 @@ const dummyReplicaSet = new ReplicaSet({
|
||||
containers: [{
|
||||
name: "dummy",
|
||||
image: "dummy",
|
||||
imagePullPolicy: "dummy",
|
||||
imagePullPolicy: "Always",
|
||||
}],
|
||||
initContainers: [{
|
||||
name: "dummy",
|
||||
image: "dummy",
|
||||
imagePullPolicy: "dummy",
|
||||
imagePullPolicy: "Always",
|
||||
}],
|
||||
priority: 1,
|
||||
serviceAccountName: "dummy",
|
||||
|
||||
@@ -23,8 +23,8 @@ const spec: PodSpec = {
|
||||
},
|
||||
},
|
||||
terminationMessagePath: "test",
|
||||
terminationMessagePolicy: "test",
|
||||
imagePullPolicy: "test",
|
||||
terminationMessagePolicy: "File",
|
||||
imagePullPolicy: "Always",
|
||||
}],
|
||||
restartPolicy: "restart",
|
||||
terminationGracePeriodSeconds: 1200,
|
||||
|
||||
@@ -22,7 +22,7 @@ export const dockerPod = new Pod({
|
||||
{
|
||||
name: "docker-exporter",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
],
|
||||
serviceAccountName: "dummy",
|
||||
@@ -66,24 +66,24 @@ export const deploymentPod1 = new Pod({
|
||||
{
|
||||
name: "init-node-exporter",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
{
|
||||
name: "init-node-exporter-1",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
],
|
||||
containers: [
|
||||
{
|
||||
name: "node-exporter",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
{
|
||||
name: "node-exporter-1",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
],
|
||||
serviceAccountName: "dummy",
|
||||
@@ -127,24 +127,24 @@ export const deploymentPod2 = new Pod({
|
||||
{
|
||||
name: "init-node-exporter",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
{
|
||||
name: "init-node-exporter-1",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
],
|
||||
containers: [
|
||||
{
|
||||
name: "node-exporter",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
{
|
||||
name: "node-exporter-1",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
],
|
||||
serviceAccountName: "dummy",
|
||||
@@ -188,12 +188,12 @@ export const deploymentPod3 = new Pod({
|
||||
{
|
||||
name: "node-exporter",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
{
|
||||
name: "node-exporter-1",
|
||||
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
|
||||
imagePullPolicy: "pull",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
],
|
||||
serviceAccountName: "dummy",
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { PodContainer, Pod } from "../../../../common/k8s-api/endpoints";
|
||||
import type { Container, Pod } from "../../../../common/k8s-api/endpoints";
|
||||
import type { TabId } from "../dock/store";
|
||||
import createLogsTabInjectable from "./create-logs-tab.injectable";
|
||||
|
||||
export interface PodLogsTabData {
|
||||
selectedPod: Pod;
|
||||
selectedContainer: PodContainer;
|
||||
selectedContainer: Container;
|
||||
}
|
||||
|
||||
const createPodLogsTabInjectable = getInjectable({
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Badge } from "../../badge";
|
||||
import type { SelectOption } from "../../select";
|
||||
import { Select } from "../../select";
|
||||
import type { LogTabViewModel } from "./logs-view-model";
|
||||
import type { PodContainer, Pod } from "../../../../common/k8s-api/endpoints";
|
||||
import type { Container, Pod } from "../../../../common/k8s-api/endpoints";
|
||||
import type { SingleValue } from "react-select";
|
||||
|
||||
export interface LogResourceSelectorProps {
|
||||
@@ -40,7 +40,7 @@ export const LogResourceSelector = observer(({ model }: LogResourceSelectorProps
|
||||
}));
|
||||
const allContainers = pod.getAllContainers();
|
||||
const container = allContainers.find(container => container.name === selectedContainer) ?? null;
|
||||
const onContainerChange = (option: SingleValue<SelectOption<PodContainer>>) => {
|
||||
const onContainerChange = (option: SingleValue<SelectOption<Container>>) => {
|
||||
if (!option) {
|
||||
return;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ export const LogResourceSelector = observer(({ model }: LogResourceSelectorProps
|
||||
menuClass="pod-selector-menu"
|
||||
/>
|
||||
<span>Container</span>
|
||||
<Select<PodContainer, SelectOption<PodContainer>, false>
|
||||
<Select<Container, SelectOption<Container>, false>
|
||||
id="container-selector-input"
|
||||
options={containerSelectOptions}
|
||||
value={container}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ConfirmDialog } from "../confirm-dialog";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import { computed } from "mobx";
|
||||
import { computed, runInAction } from "mobx";
|
||||
import clusterInjectable from "./dependencies/cluster.injectable";
|
||||
import type { DiRender } from "../test-utils/renderFor";
|
||||
import { renderFor } from "../test-utils/renderFor";
|
||||
@@ -34,11 +34,13 @@ describe("kube-object-menu", () => {
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.register(
|
||||
someMenuItemInjectable,
|
||||
someOtherMenuItemInjectable,
|
||||
someAnotherMenuItemInjectable,
|
||||
);
|
||||
runInAction(() => {
|
||||
di.register(
|
||||
someMenuItemInjectable,
|
||||
someOtherMenuItemInjectable,
|
||||
someAnotherMenuItemInjectable,
|
||||
);
|
||||
});
|
||||
|
||||
render = renderFor(di);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
import type { Injectable } from "@ogre-tools/injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { computed } from "mobx";
|
||||
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
@@ -38,6 +39,7 @@ const toItemInjectableFor = (extension: LensRendererExtension, getRandomId: () =
|
||||
const id = `${getRandomId()}-status-bar-item-for-extension-${extension.sanitizedExtensionId}`;
|
||||
let component: React.ComponentType;
|
||||
let position: "left" | "right";
|
||||
const visible: IComputedValue<boolean> | undefined = registration?.visible;
|
||||
|
||||
if (registration?.item) {
|
||||
const { item } = registration;
|
||||
@@ -74,7 +76,7 @@ const toItemInjectableFor = (extension: LensRendererExtension, getRandomId: () =
|
||||
instantiate: () => ({
|
||||
component,
|
||||
position,
|
||||
visible: computed(() => true),
|
||||
visible: visible ?? computed(() => true),
|
||||
}),
|
||||
|
||||
injectionToken: statusBarItemInjectionToken,
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { IComputedValue } from "mobx";
|
||||
|
||||
/**
|
||||
* The props for StatusBar item component
|
||||
*/
|
||||
@@ -38,4 +40,9 @@ export interface StatusBarRegistration {
|
||||
* The newer API, allows for registering a component instead of a ReactNode
|
||||
*/
|
||||
components?: StatusBarComponents;
|
||||
|
||||
/**
|
||||
* If specified, controls item visibility
|
||||
*/
|
||||
visible?: IComputedValue<boolean>;
|
||||
}
|
||||
@@ -143,7 +143,9 @@ export const getApplicationBuilder = () => {
|
||||
doGeneralOverrides: true,
|
||||
});
|
||||
|
||||
mainDi.register(mainExtensionsStateInjectable);
|
||||
runInAction(() => {
|
||||
mainDi.register(mainExtensionsStateInjectable);
|
||||
});
|
||||
|
||||
const overrideChannelsForWindow = overrideChannels(mainDi);
|
||||
|
||||
@@ -184,7 +186,9 @@ export const getApplicationBuilder = () => {
|
||||
|
||||
overrideChannelsForWindow(windowDi, windowId);
|
||||
|
||||
windowDi.register(rendererExtensionsStateInjectable);
|
||||
runInAction(() => {
|
||||
windowDi.register(rendererExtensionsStateInjectable);
|
||||
});
|
||||
|
||||
windowDi.override(
|
||||
currentlyInClusterFrameInjectable,
|
||||
@@ -552,9 +556,11 @@ export const getApplicationBuilder = () => {
|
||||
getExtensionFakeForMain(mainDi, extension.id, extension.name, extension.mainOptions || {}),
|
||||
);
|
||||
|
||||
mainExtensionInstances.forEach(
|
||||
enableExtensionFor(mainDi, mainExtensionsStateInjectable),
|
||||
);
|
||||
runInAction(() => {
|
||||
mainExtensionInstances.forEach(
|
||||
enableExtensionFor(mainDi, mainExtensionsStateInjectable),
|
||||
);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
+15
-12
@@ -6,24 +6,27 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
|
||||
import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import { runInAction } from "mobx";
|
||||
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
|
||||
export const getDi = () => {
|
||||
const di = createContainer("renderer");
|
||||
|
||||
registerMobX(di);
|
||||
|
||||
autoRegister({
|
||||
di,
|
||||
requireContexts: [
|
||||
require.context("./", true, /\.injectable\.(ts|tsx)$/),
|
||||
require.context("../common", true, /\.injectable\.(ts|tsx)$/),
|
||||
require.context("../extensions", true, /\.injectable\.(ts|tsx)$/),
|
||||
require.context("../features", true, /.*\/(renderer|common)\/.*\.injectable\.(ts|tsx)$/),
|
||||
],
|
||||
});
|
||||
|
||||
setLegacyGlobalDiForExtensionApi(di, Environments.renderer);
|
||||
|
||||
runInAction(() => {
|
||||
registerMobX(di);
|
||||
|
||||
autoRegister({
|
||||
di,
|
||||
requireContexts: [
|
||||
require.context("./", true, /\.injectable\.(ts|tsx)$/),
|
||||
require.context("../common", true, /\.injectable\.(ts|tsx)$/),
|
||||
require.context("../extensions", true, /\.injectable\.(ts|tsx)$/),
|
||||
require.context("../features", true, /.*\/(renderer|common)\/.*\.injectable\.(ts|tsx)$/),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
return di;
|
||||
};
|
||||
@@ -33,7 +33,7 @@ import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.inject
|
||||
import broadcastMessageInjectable from "../common/ipc/broadcast-message.injectable";
|
||||
import apiManagerInjectable from "../common/k8s-api/api-manager/manager.injectable";
|
||||
import setupOnApiErrorListenersInjectable from "./api/setup-on-api-errors.injectable";
|
||||
import { observable, computed } from "mobx";
|
||||
import { observable, computed, runInAction } from "mobx";
|
||||
import defaultShellInjectable from "./components/+preferences/default-shell.injectable";
|
||||
import appVersionInjectable from "../common/vars/app-version.injectable";
|
||||
import requestAnimationFrameInjectable from "./components/animate/request-animation-frame.injectable";
|
||||
@@ -68,26 +68,30 @@ import getEntitySettingCommandsInjectable from "./components/command-palette/reg
|
||||
import storageSaveDelayInjectable from "./utils/create-storage/storage-save-delay.injectable";
|
||||
import type { GlobalOverride } from "../common/test-utils/get-global-override";
|
||||
|
||||
export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => {
|
||||
const {
|
||||
doGeneralOverrides = false,
|
||||
} = opts;
|
||||
export const getDiForUnitTesting = (
|
||||
opts: { doGeneralOverrides?: boolean } = {},
|
||||
) => {
|
||||
const { doGeneralOverrides = false } = opts;
|
||||
|
||||
const di = createContainer("renderer");
|
||||
|
||||
registerMobX(di);
|
||||
di.preventSideEffects();
|
||||
|
||||
setLegacyGlobalDiForExtensionApi(di, Environments.renderer);
|
||||
|
||||
const injectables: Injectable<any, any, any>[] = (global as any).rendererInjectablePaths.map(
|
||||
const injectables: Injectable<any, any, any>[] = (
|
||||
global as any
|
||||
).rendererInjectablePaths.map(
|
||||
(filePath: string) => require(filePath).default,
|
||||
);
|
||||
|
||||
chunk(100)(injectables).forEach(chunkInjectables => {
|
||||
di.register(...chunkInjectables);
|
||||
});
|
||||
runInAction(() => {
|
||||
registerMobX(di);
|
||||
|
||||
di.preventSideEffects();
|
||||
chunk(100)(injectables).forEach((chunkInjectables) => {
|
||||
di.register(...chunkInjectables);
|
||||
});
|
||||
});
|
||||
|
||||
if (doGeneralOverrides) {
|
||||
const globalOverrides: GlobalOverride[] = (global as any).rendererGlobalOverridePaths.map(
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 { IComputedValue } from "mobx";
|
||||
import { computed } from "mobx";
|
||||
import type { LensThemeType } from "./store";
|
||||
import themeStoreInjectable from "./store.injectable";
|
||||
|
||||
export type ActiveThemeType = IComputedValue<LensThemeType>;
|
||||
|
||||
const activeThemeTypeInjectable = getInjectable({
|
||||
id: "active-theme-type",
|
||||
|
||||
instantiate: (di) => {
|
||||
const store = di.inject(themeStoreInjectable);
|
||||
|
||||
return computed(() => store.activeTheme.type);
|
||||
},
|
||||
});
|
||||
|
||||
export default activeThemeTypeInjectable;
|
||||
@@ -17,10 +17,10 @@ import type { ReadonlyDeep } from "type-fest/source/readonly-deep";
|
||||
import assert from "assert";
|
||||
|
||||
export type ThemeId = string;
|
||||
|
||||
export type LensThemeType = "dark" | "light";
|
||||
export interface LensTheme {
|
||||
name: string;
|
||||
type: "dark" | "light";
|
||||
type: LensThemeType;
|
||||
colors: Record<string, string>;
|
||||
description: string;
|
||||
author: string;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { runInAction } from "mobx";
|
||||
import type { CreateStorage } from "./create-storage";
|
||||
import createStorageInjectable from "./create-storage.injectable";
|
||||
|
||||
@@ -19,8 +20,10 @@ export const controlWhenStoragesAreReady = (di: DiContainer) => {
|
||||
return storage;
|
||||
};
|
||||
|
||||
// TODO: Remove when typing is added to the library
|
||||
(di as any).decorateFunction(createStorageInjectable, decorated);
|
||||
runInAction(() => {
|
||||
// TODO: Remove when typing is added to the library
|
||||
(di as any).decorateFunction(createStorageInjectable, decorated);
|
||||
});
|
||||
|
||||
return async () => {
|
||||
await Promise.all(storagesAreReady);
|
||||
|
||||
Reference in new issue
Block a user