mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
chore: Remove leading '+' on some directory names
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
7dff8e31a6
commit
4379edf0c7
903 files changed
+438
-438
No files matched your search
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
import releasesInjectable from "../releases.injectable";
|
||||
import type { RequestCreateHelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api/request-create.injectable";
|
||||
import requestCreateHelmReleaseInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/request-create.injectable";
|
||||
|
||||
const createReleaseInjectable = getInjectable({
|
||||
id: "create-release",
|
||||
|
||||
instantiate: (di): RequestCreateHelmRelease => {
|
||||
const releases = di.inject(releasesInjectable);
|
||||
const callForCreateRelease = di.inject(requestCreateHelmReleaseInjectable);
|
||||
|
||||
return async (payload) => {
|
||||
const release = await callForCreateRelease(payload);
|
||||
|
||||
releases.invalidate();
|
||||
|
||||
return release;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default createReleaseInjectable;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import requestDeleteHelmReleaseInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/request-delete.injectable";
|
||||
import releasesInjectable from "../releases.injectable";
|
||||
|
||||
const deleteReleaseInjectable = getInjectable({
|
||||
id: "delete-release",
|
||||
|
||||
instantiate: (di) => {
|
||||
const releases = di.inject(releasesInjectable);
|
||||
const requestDeleteHelmRelease = di.inject(requestDeleteHelmReleaseInjectable);
|
||||
|
||||
return async (release: HelmRelease) => {
|
||||
await requestDeleteHelmRelease(release.getName(), release.getNs());
|
||||
|
||||
releases.invalidate();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default deleteReleaseInjectable;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.ReleaseRollbackDialog {
|
||||
.WizardStep {
|
||||
text-align: center;
|
||||
|
||||
.step-content {
|
||||
padding: var(--wizard-spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./dialog.scss";
|
||||
|
||||
import React from "react";
|
||||
import type { IObservableValue } from "mobx";
|
||||
import { computed, observable, runInAction } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import type { DialogProps } from "../../dialog";
|
||||
import { Dialog } from "../../dialog";
|
||||
import { Wizard, WizardStep } from "../../wizard";
|
||||
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import { Select } from "../../select";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import releaseRollbackDialogStateInjectable from "./state.injectable";
|
||||
import type { RollbackRelease } from "../rollback-release/rollback-release.injectable";
|
||||
import rollbackReleaseInjectable from "../rollback-release/rollback-release.injectable";
|
||||
import type { HelmReleaseRevision, RequestHelmReleaseHistory } from "../../../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable";
|
||||
import requestHelmReleaseHistoryInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable";
|
||||
import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable";
|
||||
import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable";
|
||||
|
||||
export interface ReleaseRollbackDialogProps extends DialogProps {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
rollbackRelease: RollbackRelease;
|
||||
state: IObservableValue<HelmRelease | undefined>;
|
||||
requestHelmReleaseHistory: RequestHelmReleaseHistory;
|
||||
showCheckedErrorNotification: ShowCheckedErrorNotification;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedReleaseRollbackDialog extends React.Component<ReleaseRollbackDialogProps & Dependencies> {
|
||||
readonly isLoading = observable.box(false);
|
||||
readonly revision = observable.box<HelmReleaseRevision | undefined>();
|
||||
readonly revisions = observable.array<HelmReleaseRevision>();
|
||||
readonly revisionOptions = computed(() => (
|
||||
this.revisions.map(revision => ({
|
||||
value: revision,
|
||||
label: `${revision.revision} - ${revision.chart} - ${revision.app_version}, updated: ${new Date(revision.updated).toLocaleString()}`,
|
||||
}))
|
||||
));
|
||||
|
||||
close = () => {
|
||||
this.props.state.set(undefined);
|
||||
};
|
||||
|
||||
onOpen = async (release: HelmRelease) => {
|
||||
this.isLoading.set(true);
|
||||
|
||||
const releases = await this.props.requestHelmReleaseHistory(release.getName(), release.getNs());
|
||||
|
||||
runInAction(() => {
|
||||
this.revisions.replace(orderBy(releases, "revision", "desc"));
|
||||
this.revision.set(this.revisions[0]);
|
||||
this.isLoading.set(false);
|
||||
});
|
||||
};
|
||||
|
||||
rollback = async (release: HelmRelease) => {
|
||||
const revision = this.revision.get();
|
||||
|
||||
if (!revision) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.props.rollbackRelease(release.getName(), release.getNs(), revision.revision);
|
||||
this.close();
|
||||
} catch (err) {
|
||||
this.props.showCheckedErrorNotification(err, "Unknown error occured while rolling back release");
|
||||
}
|
||||
};
|
||||
|
||||
renderRevisionContent() {
|
||||
const revision = this.revision.get();
|
||||
|
||||
if (!revision) {
|
||||
return <p>No revisions to rollback.</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gaps align-center">
|
||||
<b>Revision</b>
|
||||
<Select
|
||||
id="revision-input"
|
||||
themeName="light"
|
||||
value={revision}
|
||||
options={this.revisionOptions.get()}
|
||||
onChange={option => this.revision.set(option?.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderContent(release: HelmRelease) {
|
||||
return (
|
||||
<Wizard
|
||||
header={(
|
||||
<h5>
|
||||
Rollback
|
||||
<b>{release.getName()}</b>
|
||||
</h5>
|
||||
)}
|
||||
done={this.close}
|
||||
>
|
||||
<WizardStep
|
||||
scrollable={false}
|
||||
nextLabel="Rollback"
|
||||
next={() => this.rollback(release)}
|
||||
loading={this.isLoading.get()}
|
||||
>
|
||||
{this.renderRevisionContent()}
|
||||
</WizardStep>
|
||||
</Wizard>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { state, ...dialogProps } = this.props;
|
||||
const release = state.get();
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
{...dialogProps}
|
||||
className="ReleaseRollbackDialog"
|
||||
isOpen={Boolean(release)}
|
||||
onOpen={release ? (() => this.onOpen(release)) : undefined}
|
||||
close={this.close}
|
||||
>
|
||||
{release && this.renderContent(release)}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const ReleaseRollbackDialog = withInjectables<Dependencies, ReleaseRollbackDialogProps>(NonInjectedReleaseRollbackDialog, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
rollbackRelease: di.inject(rollbackReleaseInjectable),
|
||||
state: di.inject(releaseRollbackDialogStateInjectable),
|
||||
requestHelmReleaseHistory: di.inject(requestHelmReleaseHistoryInjectable),
|
||||
showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import releaseRollbackDialogStateInjectable from "./state.injectable";
|
||||
|
||||
export type OpenHelmReleaseRollbackDialog = (release: HelmRelease) => void;
|
||||
|
||||
const openHelmReleaseRollbackDialogInjectable = getInjectable({
|
||||
id: "open-helm-release-dialog",
|
||||
instantiate: (di): OpenHelmReleaseRollbackDialog => {
|
||||
const state = di.inject(releaseRollbackDialogStateInjectable);
|
||||
|
||||
return release => state.set(release);
|
||||
},
|
||||
});
|
||||
|
||||
export default openHelmReleaseRollbackDialogInjectable;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
|
||||
const releaseRollbackDialogStateInjectable = getInjectable({
|
||||
id: "release-rollback-dialog-state",
|
||||
instantiate: () => observable.box<HelmRelease | undefined>(undefined),
|
||||
});
|
||||
|
||||
export default releaseRollbackDialogStateInjectable;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { HelmReleases } from "./releases";
|
||||
import helmReleasesRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const helmReleasesRouteComponentInjectable = getInjectable({
|
||||
id: "helm-releases-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(helmReleasesRouteInjectable),
|
||||
Component: HelmReleases,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default helmReleasesRouteComponentInjectable;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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 { computed } from "mobx";
|
||||
import helmReleasesRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable";
|
||||
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
|
||||
|
||||
const helmReleasesRouteParametersInjectable = getInjectable({
|
||||
id: "helm-releases-route-parameters",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(helmReleasesRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
|
||||
return {
|
||||
namespace: computed(() => pathParameters.get().namespace),
|
||||
name: computed(() => pathParameters.get().name),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default helmReleasesRouteParametersInjectable;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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 { computed } from "mobx";
|
||||
|
||||
import helmReleasesRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable";
|
||||
import { helmSidebarItemId } from "../helm/helm-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToHelmReleasesInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
||||
|
||||
const helmReleasesSidebarItemsInjectable = getInjectable({
|
||||
id: "helm-releases-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(helmReleasesRouteInjectable);
|
||||
const navigateToHelmReleases = di.inject(navigateToHelmReleasesInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "releases",
|
||||
parentId: helmSidebarItemId,
|
||||
title: "Releases",
|
||||
onClick: navigateToHelmReleases,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 20,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default helmReleasesSidebarItemsInjectable;
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./releases";
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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 { computed } from "mobx";
|
||||
import { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token";
|
||||
import { ReleaseDetails } from "./release-details";
|
||||
import targetHelmReleaseInjectable from "./target-helm-release.injectable";
|
||||
|
||||
const releaseDetailsClusterFrameChildComponentInjectable = getInjectable({
|
||||
id: "release-details-cluster-frame-child-component",
|
||||
|
||||
instantiate: (di) => {
|
||||
const targetRelease = di.inject(targetHelmReleaseInjectable);
|
||||
|
||||
return {
|
||||
id: "release-details",
|
||||
Component: ReleaseDetails,
|
||||
shouldRender: computed(() => !!targetRelease.get()),
|
||||
};
|
||||
},
|
||||
injectionToken: clusterFrameChildComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default releaseDetailsClusterFrameChildComponentInjectable;
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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 { Link } from "react-router-dom";
|
||||
import { DrawerItem, DrawerTitle } from "../../drawer";
|
||||
import { stopPropagation } from "@k8slens/utilities";
|
||||
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";
|
||||
import releaseDetailsModelInjectable from "./release-details-model/release-details-model.injectable";
|
||||
import { Button } from "@k8slens/button";
|
||||
import { kebabCase } from "lodash/fp";
|
||||
import { Badge } from "../../badge";
|
||||
import { SubTitle } from "../../layout/sub-title";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../../table";
|
||||
import { Checkbox } from "../../checkbox";
|
||||
import { MonacoEditor } from "../../monaco-editor";
|
||||
import { Spinner } from "../../spinner";
|
||||
import type { TargetHelmRelease } from "./target-helm-release.injectable";
|
||||
|
||||
interface ReleaseDetailsContentProps {
|
||||
targetRelease: TargetHelmRelease;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
model: ReleaseDetailsModel;
|
||||
}
|
||||
|
||||
const NonInjectedReleaseDetailsContent = observer(({ model }: Dependencies & ReleaseDetailsContentProps) => {
|
||||
const loadingError = model.loadingError.get();
|
||||
|
||||
if (loadingError) {
|
||||
return (
|
||||
<div data-testid="helm-release-detail-error">
|
||||
Failed to load release:
|
||||
{" "}
|
||||
{loadingError}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<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"
|
||||
/>
|
||||
</div>
|
||||
</DrawerItem>
|
||||
|
||||
<DrawerItem name="Updated">
|
||||
{`${model.release.getUpdated()} ago (${model.release.updated})`}
|
||||
</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="Status"
|
||||
className="status"
|
||||
labelsOnly>
|
||||
<Badge
|
||||
label={model.release.getStatus()}
|
||||
className={kebabCase(model.release.getStatus())}
|
||||
/>
|
||||
</DrawerItem>
|
||||
|
||||
<ReleaseValues
|
||||
releaseId={model.id}
|
||||
configuration={model.configuration}
|
||||
onlyUserSuppliedValuesAreShown={
|
||||
model.onlyUserSuppliedValuesAreShown
|
||||
}
|
||||
/>
|
||||
|
||||
<DrawerTitle>Notes</DrawerTitle>
|
||||
|
||||
{model.notes && <div className="notes">{model.notes}</div>}
|
||||
|
||||
<DrawerTitle>Resources</DrawerTitle>
|
||||
|
||||
{model.groupedResources.length > 0 && (
|
||||
<div className="resources">
|
||||
{model.groupedResources.map((group) => (
|
||||
<ResourceGroup key={group.kind} group={group} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const ReleaseDetailsContent = withInjectables<Dependencies, ReleaseDetailsContentProps>(NonInjectedReleaseDetailsContent, {
|
||||
getPlaceholder: () => <Spinner center data-testid="helm-release-detail-content-spinner" />,
|
||||
|
||||
getProps: async (di, props) => ({
|
||||
model: await di.inject(releaseDetailsModelInjectable, props.targetRelease),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
|
||||
const ResourceGroup = ({
|
||||
group: { kind, isNamespaced, resources },
|
||||
}: {
|
||||
group: MinimalResourceGroup;
|
||||
}) => (
|
||||
<>
|
||||
<SubTitle title={kind} />
|
||||
|
||||
<Table scrollable={false}>
|
||||
<TableHead sticky={false}>
|
||||
<TableCell className="name">Name</TableCell>
|
||||
|
||||
{isNamespaced && <TableCell className="namespace">Namespace</TableCell>}
|
||||
</TableHead>
|
||||
|
||||
{resources.map(
|
||||
({ detailsUrl, name, namespace, uid }) => (
|
||||
<TableRow key={uid}>
|
||||
<TableCell className="name">
|
||||
{detailsUrl ? <Link to={detailsUrl}>{name}</Link> : name}
|
||||
</TableCell>
|
||||
|
||||
{isNamespaced && (
|
||||
<TableCell className="namespace">{namespace}</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
),
|
||||
)}
|
||||
</Table>
|
||||
</>
|
||||
);
|
||||
|
||||
interface ReleaseValuesProps {
|
||||
releaseId: string;
|
||||
configuration: ConfigurationInput;
|
||||
onlyUserSuppliedValuesAreShown: OnlyUserSuppliedValuesAreShownToggle;
|
||||
}
|
||||
|
||||
const ReleaseValues = observer(({ releaseId, configuration, onlyUserSuppliedValuesAreShown }: ReleaseValuesProps) => {
|
||||
const configurationIsLoading = configuration.isLoading.get();
|
||||
|
||||
return (
|
||||
<div className="values">
|
||||
<DrawerTitle>Values</DrawerTitle>
|
||||
|
||||
<div className="flex column gaps">
|
||||
<Checkbox
|
||||
label="User-supplied values only"
|
||||
value={onlyUserSuppliedValuesAreShown.value.get()}
|
||||
onChange={onlyUserSuppliedValuesAreShown.toggle}
|
||||
disabled={configurationIsLoading}
|
||||
data-testid="user-supplied-values-only-checkbox"
|
||||
/>
|
||||
|
||||
<MonacoEditor
|
||||
id={`helm-release-configuration-${releaseId}`}
|
||||
style={{ minHeight: 300 }}
|
||||
value={configuration.nonSavedValue.get()}
|
||||
onChange={configuration.onChange}
|
||||
/>
|
||||
|
||||
<Button
|
||||
primary
|
||||
label="Save"
|
||||
waiting={configuration.isSaving.get()}
|
||||
disabled={configurationIsLoading}
|
||||
onClick={configuration.save}
|
||||
data-testid="helm-release-configuration-save-button"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
+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,
|
||||
}),
|
||||
});
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./release-details.scss";
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { Drawer } from "../../drawer";
|
||||
import { cssNames } from "@k8slens/utilities";
|
||||
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,
|
||||
}),
|
||||
});
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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 apiManagerInjectable from "../../../../../common/k8s-api/api-manager/manager.injectable";
|
||||
import getDetailsUrlInjectable from "../../../kube-detail-params/get-details-url.injectable";
|
||||
|
||||
export type GetResourceDetailsUrl = (
|
||||
kind: string,
|
||||
apiVersion: string,
|
||||
namespace: string | undefined,
|
||||
name: string
|
||||
) => string;
|
||||
|
||||
const getResourceDetailsUrlInjectable = getInjectable({
|
||||
id: "get-resource-details-url",
|
||||
|
||||
instantiate: (di): GetResourceDetailsUrl => {
|
||||
const apiManager = di.inject(apiManagerInjectable);
|
||||
const getDetailsUrl = di.inject(getDetailsUrlInjectable);
|
||||
|
||||
const getKubeApi = (kind: string, apiVersion: string) =>
|
||||
apiManager.getApi(
|
||||
(api) => api.kind === kind && api.apiVersionWithGroup == apiVersion,
|
||||
);
|
||||
|
||||
return (kind, apiVersion, namespace, name) => {
|
||||
const kubeApi = getKubeApi(kind, apiVersion);
|
||||
|
||||
if (!kubeApi) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const resourceUrl = kubeApi.getUrl({
|
||||
name,
|
||||
namespace,
|
||||
});
|
||||
|
||||
return getDetailsUrl(resourceUrl);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default getResourceDetailsUrlInjectable;
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import type { IComputedValue, IObservableValue } from "mobx";
|
||||
import { runInAction, action, observable, computed } from "mobx";
|
||||
import type { TargetHelmRelease } from "../target-helm-release.injectable";
|
||||
import type { RequestDetailedHelmRelease, DetailedHelmRelease } from "./request-detailed-helm-release.injectable";
|
||||
import requestDetailedHelmReleaseInjectable from "./request-detailed-helm-release.injectable";
|
||||
import type { LensTheme } from "../../../../themes/lens-theme";
|
||||
import type { RequestHelmReleaseConfiguration } from "../../../../../common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable";
|
||||
import requestHelmReleaseConfigurationInjectable from "../../../../../common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { groupBy, map } from "lodash/fp";
|
||||
import type { KubeJsonApiData } from "../../../../../common/k8s-api/kube-json-api";
|
||||
import type { GetResourceDetailsUrl } from "./get-resource-details-url.injectable";
|
||||
import getResourceDetailsUrlInjectable from "./get-resource-details-url.injectable";
|
||||
import type { RequestHelmReleaseUpdate } from "../../../../../common/k8s-api/endpoints/helm-releases.api/request-update.injectable";
|
||||
import updateReleaseInjectable from "../../update-release/update-release.injectable";
|
||||
import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable";
|
||||
import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable";
|
||||
import type { ShowNotification } from "../../../notifications";
|
||||
import showSuccessNotificationInjectable from "../../../notifications/show-success-notification.injectable";
|
||||
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 activeThemeInjectable from "../../../../themes/active.injectable";
|
||||
import type { ToHelmRelease } from "../../to-helm-release.injectable";
|
||||
import toHelmReleaseInjectable from "../../to-helm-release.injectable";
|
||||
|
||||
const releaseDetailsModelInjectable = getInjectable({
|
||||
id: "release-details-model",
|
||||
|
||||
instantiate: async (di, targetRelease: TargetHelmRelease) => {
|
||||
const model = new ReleaseDetailsModel({
|
||||
requestDetailedHelmRelease: di.inject(requestDetailedHelmReleaseInjectable),
|
||||
targetRelease,
|
||||
activeTheme: di.inject(activeThemeInjectable),
|
||||
requestHelmReleaseConfiguration: di.inject(requestHelmReleaseConfigurationInjectable),
|
||||
getResourceDetailsUrl: di.inject(getResourceDetailsUrlInjectable),
|
||||
updateRelease: di.inject(updateReleaseInjectable),
|
||||
showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable),
|
||||
showSuccessNotification: di.inject(showSuccessNotificationInjectable),
|
||||
createUpgradeChartTab: di.inject(createUpgradeChartTabInjectable),
|
||||
navigateToHelmReleases: di.inject(navigateToHelmReleasesInjectable),
|
||||
toHelmRelease: di.inject(toHelmReleaseInjectable),
|
||||
});
|
||||
|
||||
await model.load();
|
||||
|
||||
return model;
|
||||
},
|
||||
|
||||
lifecycle: lifecycleEnum.keyedSingleton({
|
||||
getInstanceKey: (di, release: TargetHelmRelease) => `${release.namespace}/${release.name}`,
|
||||
}),
|
||||
});
|
||||
|
||||
export default releaseDetailsModelInjectable;
|
||||
|
||||
export interface OnlyUserSuppliedValuesAreShownToggle {
|
||||
readonly value: IObservableValue<boolean>;
|
||||
toggle: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface ConfigurationInput {
|
||||
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>;
|
||||
requestDetailedHelmRelease: RequestDetailedHelmRelease;
|
||||
requestHelmReleaseConfiguration: RequestHelmReleaseConfiguration;
|
||||
getResourceDetailsUrl: GetResourceDetailsUrl;
|
||||
updateRelease: RequestHelmReleaseUpdate;
|
||||
showCheckedErrorNotification: ShowCheckedErrorNotification;
|
||||
showSuccessNotification: ShowNotification;
|
||||
createUpgradeChartTab: (release: HelmRelease) => string;
|
||||
navigateToHelmReleases: NavigateToHelmReleases;
|
||||
toHelmRelease: ToHelmRelease;
|
||||
}
|
||||
|
||||
export class ReleaseDetailsModel {
|
||||
readonly id = `${this.dependencies.targetRelease.namespace}/${this.dependencies.targetRelease.name}`;
|
||||
|
||||
constructor(protected readonly dependencies: Dependencies) {}
|
||||
|
||||
private readonly detailedRelease = observable.box<DetailedHelmRelease | undefined>();
|
||||
|
||||
readonly loadingError = observable.box<string>();
|
||||
|
||||
readonly configuration: ConfigurationInput = {
|
||||
nonSavedValue: observable.box(""),
|
||||
isLoading: observable.box(false),
|
||||
isSaving: observable.box(false),
|
||||
|
||||
onChange: action((value: string) => {
|
||||
this.configuration.nonSavedValue.set(value);
|
||||
}),
|
||||
|
||||
save: async () => {
|
||||
runInAction(() => {
|
||||
this.configuration.isSaving.set(true);
|
||||
});
|
||||
|
||||
const name = this.release.getName();
|
||||
const namespace = this.release.getNs();
|
||||
|
||||
const data = {
|
||||
chart: this.release.getChart(),
|
||||
repo: await this.release.getRepo(),
|
||||
version: this.release.getVersion(),
|
||||
values: this.configuration.nonSavedValue.get(),
|
||||
};
|
||||
|
||||
const result = await this.dependencies.updateRelease(name, namespace, data);
|
||||
|
||||
runInAction(() => {
|
||||
this.configuration.isSaving.set(false);
|
||||
});
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
this.dependencies.showCheckedErrorNotification(
|
||||
result.error,
|
||||
"Unknown error occured while updating release",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.dependencies.showSuccessNotification(
|
||||
<p>
|
||||
Release
|
||||
{" "}
|
||||
<b>{name}</b>
|
||||
{" successfully updated!"}
|
||||
</p>,
|
||||
);
|
||||
|
||||
await this.loadConfiguration();
|
||||
},
|
||||
};
|
||||
|
||||
readonly onlyUserSuppliedValuesAreShown: OnlyUserSuppliedValuesAreShownToggle = {
|
||||
value: observable.box(false),
|
||||
|
||||
toggle: action(async () => {
|
||||
const value = this.onlyUserSuppliedValuesAreShown.value;
|
||||
|
||||
value.set(!value.get());
|
||||
|
||||
await this.loadConfiguration();
|
||||
}),
|
||||
};
|
||||
|
||||
load = async () => {
|
||||
const { name, namespace } = this.dependencies.targetRelease;
|
||||
|
||||
const result = await this.dependencies.requestDetailedHelmRelease(
|
||||
name,
|
||||
namespace,
|
||||
);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
runInAction(() => {
|
||||
this.loadingError.set(result.error);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
this.detailedRelease.set(result.response);
|
||||
});
|
||||
|
||||
await this.loadConfiguration();
|
||||
};
|
||||
|
||||
private loadConfiguration = async () => {
|
||||
runInAction(() => {
|
||||
this.configuration.isLoading.set(true);
|
||||
});
|
||||
|
||||
const { name, namespace } = this.release;
|
||||
|
||||
const configuration =
|
||||
await this.dependencies.requestHelmReleaseConfiguration(
|
||||
name,
|
||||
namespace,
|
||||
!this.onlyUserSuppliedValuesAreShown.value.get(),
|
||||
);
|
||||
|
||||
runInAction(() => {
|
||||
this.configuration.isLoading.set(false);
|
||||
this.configuration.nonSavedValue.set(configuration);
|
||||
});
|
||||
};
|
||||
|
||||
@computed get release() {
|
||||
const detailedRelease = this.detailedRelease.get();
|
||||
|
||||
assert(detailedRelease, "Tried to access release before load");
|
||||
|
||||
return this.dependencies.toHelmRelease(detailedRelease.release);
|
||||
}
|
||||
|
||||
@computed private get details() {
|
||||
const detailedRelease = this.detailedRelease.get();
|
||||
|
||||
assert(detailedRelease, "Tried to access details before load");
|
||||
|
||||
return detailedRelease.details;
|
||||
}
|
||||
|
||||
@computed get notes() {
|
||||
return this.details?.info.notes ?? "";
|
||||
}
|
||||
|
||||
@computed get groupedResources(): MinimalResourceGroup[] {
|
||||
return pipeline(
|
||||
this.details?.resources ?? [],
|
||||
groupBy((resource) => resource.kind),
|
||||
(grouped) => Object.entries(grouped),
|
||||
|
||||
map(([kind, resources]) => ({
|
||||
kind,
|
||||
|
||||
resources: resources.map(
|
||||
toMinimalResourceFor(this.dependencies.getResourceDetailsUrl, kind),
|
||||
),
|
||||
|
||||
isNamespaced: resources.some(
|
||||
(resource) => !!resource.metadata.namespace,
|
||||
),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
@computed get activeTheme() {
|
||||
return this.dependencies.activeTheme.get().type;
|
||||
}
|
||||
|
||||
close = () => {
|
||||
this.dependencies.navigateToHelmReleases();
|
||||
};
|
||||
|
||||
startUpgradeProcess = () => {
|
||||
this.dependencies.createUpgradeChartTab(this.release);
|
||||
|
||||
this.dependencies.navigateToHelmReleases();
|
||||
};
|
||||
}
|
||||
|
||||
export interface MinimalResourceGroup {
|
||||
kind: string;
|
||||
isNamespaced: boolean;
|
||||
resources: MinimalResource[];
|
||||
}
|
||||
|
||||
export interface MinimalResource {
|
||||
uid: string | undefined;
|
||||
name: string;
|
||||
namespace: string | undefined;
|
||||
detailsUrl: string | undefined;
|
||||
}
|
||||
|
||||
const toMinimalResourceFor =
|
||||
(getResourceDetailsUrl: GetResourceDetailsUrl, kind: string) =>
|
||||
(resource: KubeJsonApiData): MinimalResource => {
|
||||
const { name, namespace, uid } = resource.metadata;
|
||||
|
||||
return {
|
||||
uid,
|
||||
name,
|
||||
namespace,
|
||||
detailsUrl: getResourceDetailsUrl(
|
||||
kind,
|
||||
resource.apiVersion,
|
||||
namespace,
|
||||
name,
|
||||
),
|
||||
};
|
||||
};
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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 { HelmReleaseDto } from "../../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import requestHelmReleasesInjectable from "../../../../../common/k8s-api/endpoints/helm-releases.api/request-releases.injectable";
|
||||
import type { HelmReleaseDetails } from "../../../../../common/k8s-api/endpoints/helm-releases.api/request-details.injectable";
|
||||
import requestHelmReleaseDetailsInjectable from "../../../../../common/k8s-api/endpoints/helm-releases.api/request-details.injectable";
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
|
||||
export interface DetailedHelmRelease {
|
||||
release: HelmReleaseDto;
|
||||
details?: HelmReleaseDetails;
|
||||
}
|
||||
|
||||
export type RequestDetailedHelmRelease = (
|
||||
name: string,
|
||||
namespace: string
|
||||
) => AsyncResult<DetailedHelmRelease>;
|
||||
|
||||
const requestDetailedHelmReleaseInjectable = getInjectable({
|
||||
id: "request-detailed-helm-release",
|
||||
|
||||
instantiate: (di): RequestDetailedHelmRelease => {
|
||||
const requestHelmReleases = di.inject(requestHelmReleasesInjectable);
|
||||
const requestHelmReleaseDetails = di.inject(requestHelmReleaseDetailsInjectable);
|
||||
|
||||
return async (name, namespace) => {
|
||||
const [releases, details] = await Promise.all([
|
||||
requestHelmReleases(namespace),
|
||||
requestHelmReleaseDetails(name, namespace),
|
||||
]);
|
||||
|
||||
const release = releases.find(
|
||||
(rel) => rel.name === name && rel.namespace === namespace,
|
||||
);
|
||||
|
||||
if (!release) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `Release ${name} didn't exist in ${namespace} namespace.`,
|
||||
};
|
||||
}
|
||||
|
||||
return { callWasSuccessful: true, response: { release, details }};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default requestDetailedHelmReleaseInjectable;
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
@import "../release.mixins";
|
||||
|
||||
.ReleaseDetails {
|
||||
.DrawerItem {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading-error {
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status {
|
||||
@include release-status-bgs;
|
||||
}
|
||||
|
||||
.chart {
|
||||
.upgrade {
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.resources {
|
||||
.SubTitle {
|
||||
text-transform: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.Table {
|
||||
margin-bottom: $margin * 4;
|
||||
border: 1px solid var(--drawerSubtitleBackground);
|
||||
border-radius: $radius;
|
||||
overflow: auto;
|
||||
|
||||
.TableHead {
|
||||
border-bottom: none;
|
||||
|
||||
.TableCell {
|
||||
background-color: var(--drawerSubtitleBackground);
|
||||
word-break: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.TableCell {
|
||||
text-overflow: unset;
|
||||
word-break: break-word;
|
||||
|
||||
&.name {
|
||||
flex-grow: 3;
|
||||
}
|
||||
|
||||
&.namespace {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&.age {
|
||||
flex-basis: 100px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notes {
|
||||
white-space: pre-line;
|
||||
font-family: var(--font-monospace);
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.values + .Button {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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 { IComputedValue } from "mobx";
|
||||
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<
|
||||
TargetHelmRelease | undefined
|
||||
>;
|
||||
}
|
||||
|
||||
const NonInjectedReleaseDetails = observer(
|
||||
({ targetRelease }: Dependencies) => {
|
||||
const release = targetRelease.get();
|
||||
|
||||
return release ? <ReleaseDetailsDrawer targetRelease={release} /> : null;
|
||||
},
|
||||
);
|
||||
|
||||
export const ReleaseDetails = withInjectables<Dependencies>(
|
||||
NonInjectedReleaseDetails,
|
||||
{
|
||||
getProps: (di) => ({
|
||||
targetRelease: di.inject(targetHelmReleaseInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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 { computed } from "mobx";
|
||||
import helmReleasesRouteParametersInjectable from "../helm-releases-route-parameters.injectable";
|
||||
|
||||
export interface TargetHelmRelease { name: string; namespace: string }
|
||||
|
||||
const targetHelmReleaseInjectable = getInjectable({
|
||||
id: "target-helm-release",
|
||||
|
||||
instantiate: (di) => {
|
||||
const routeParameters = di.inject(helmReleasesRouteParametersInjectable);
|
||||
|
||||
return computed((): TargetHelmRelease | undefined => {
|
||||
const name = routeParameters.name.get();
|
||||
const namespace = routeParameters.namespace.get();
|
||||
|
||||
return name && namespace ? { name, namespace } : undefined;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default targetHelmReleaseInjectable;
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 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 { HelmRelease } from "../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import { cssNames } from "@k8slens/utilities";
|
||||
import type { MenuActionsProps } from "../menu/menu-actions";
|
||||
import { MenuActions } from "../menu/menu-actions";
|
||||
import { MenuItem } from "../menu";
|
||||
import { Icon } from "../icon";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import createUpgradeChartTabInjectable from "../dock/upgrade-chart/create-upgrade-chart-tab.injectable";
|
||||
import deleteReleaseInjectable from "./delete-release/delete-release.injectable";
|
||||
import type { OpenHelmReleaseRollbackDialog } from "./dialog/open.injectable";
|
||||
import openHelmReleaseRollbackDialogInjectable from "./dialog/open.injectable";
|
||||
|
||||
export interface HelmReleaseMenuProps extends MenuActionsProps {
|
||||
release: HelmRelease;
|
||||
hideDetails?(): void;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
deleteRelease: (release: HelmRelease) => Promise<any>;
|
||||
createUpgradeChartTab: (release: HelmRelease) => void;
|
||||
openRollbackDialog: OpenHelmReleaseRollbackDialog;
|
||||
}
|
||||
|
||||
class NonInjectedHelmReleaseMenu extends React.Component<HelmReleaseMenuProps & Dependencies> {
|
||||
remove = () => {
|
||||
return this.props.deleteRelease(this.props.release);
|
||||
};
|
||||
|
||||
upgrade = () => {
|
||||
const { release, hideDetails } = this.props;
|
||||
|
||||
this.props.createUpgradeChartTab(release);
|
||||
hideDetails?.();
|
||||
};
|
||||
|
||||
rollback = () => {
|
||||
this.props.openRollbackDialog(this.props.release);
|
||||
};
|
||||
|
||||
renderContent() {
|
||||
const { release, toolbar } = this.props;
|
||||
|
||||
if (!release) return null;
|
||||
const hasRollback = release && release.getRevision() > 1;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasRollback && (
|
||||
<MenuItem onClick={this.rollback}>
|
||||
<Icon
|
||||
material="history"
|
||||
interactive={toolbar}
|
||||
tooltip="Rollback"
|
||||
/>
|
||||
<span className="title">Rollback</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={this.upgrade}
|
||||
data-testid={`upgrade-chart-menu-item-for-${release.getId()}`}
|
||||
>
|
||||
<Icon
|
||||
material="refresh"
|
||||
interactive={toolbar}
|
||||
tooltip="Upgrade"
|
||||
/>
|
||||
<span className="title">Upgrade</span>
|
||||
</MenuItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, release, ...menuProps } = this.props;
|
||||
|
||||
return (
|
||||
<MenuActions
|
||||
id={`menu-actions-for-release-menu-for-${release.getId()}`}
|
||||
triggerIcon={{
|
||||
material: "more_vert",
|
||||
"data-testid": `menu-actions-icon-for-release-menu-for-${release.getId()}`,
|
||||
}}
|
||||
{...menuProps}
|
||||
className={cssNames("HelmReleaseMenu", className)}
|
||||
removeAction={this.remove}
|
||||
removeConfirmationMessage={() => (
|
||||
<p>
|
||||
Remove Helm Release
|
||||
{" "}
|
||||
<b>{release.name}</b>
|
||||
?
|
||||
</p>
|
||||
)}
|
||||
>
|
||||
{this.renderContent()}
|
||||
</MenuActions>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const HelmReleaseMenu = withInjectables<Dependencies, HelmReleaseMenuProps>(NonInjectedHelmReleaseMenu, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
deleteRelease: di.inject(deleteReleaseInjectable),
|
||||
createUpgradeChartTab: di.inject(createUpgradeChartTabInjectable),
|
||||
openRollbackDialog: di.inject(openHelmReleaseRollbackDialogInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed, onBecomeObserved, onBecomeUnobserved } from "mobx";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import secretStoreInjectable from "../config-secrets/store.injectable";
|
||||
|
||||
const releaseSecretsInjectable = getInjectable({
|
||||
id: "release-secrets",
|
||||
|
||||
instantiate: (di) => {
|
||||
const subscribeStores = di.inject(subscribeStoresInjectable);
|
||||
const secretStore = di.inject(secretStoreInjectable);
|
||||
|
||||
const releaseSecrets = computed(() =>
|
||||
secretStore.contextItems.filter((secret) =>
|
||||
secret.type.startsWith("helm.sh/release"),
|
||||
),
|
||||
);
|
||||
|
||||
let unsubscribe: () => void;
|
||||
|
||||
onBecomeObserved(releaseSecrets, () => {
|
||||
unsubscribe = subscribeStores([secretStore]);
|
||||
});
|
||||
|
||||
onBecomeUnobserved(releaseSecrets, () => {
|
||||
unsubscribe?.();
|
||||
});
|
||||
|
||||
return releaseSecrets;
|
||||
},
|
||||
});
|
||||
|
||||
export default releaseSecretsInjectable;
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
$release-status-color-list: (
|
||||
deployed: var(--colorSuccess),
|
||||
failed: var(--colorError),
|
||||
deleting: var(--colorWarning),
|
||||
pendingInstall: var(--colorInfo),
|
||||
pendingUpgrade: var(--colorInfo),
|
||||
pendingRollback: var(--colorInfo),
|
||||
);
|
||||
|
||||
@mixin release-status-bgs {
|
||||
@each $status, $color in $release-status-color-list {
|
||||
&.#{$status} {
|
||||
color: white;
|
||||
background: $color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin release-status-colors {
|
||||
@each $status, $color in $release-status-color-list {
|
||||
&.#{$status} {
|
||||
color: $color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { asyncComputed } from "@ogre-tools/injectable-react";
|
||||
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
|
||||
import releaseSecretsInjectable from "./release-secrets.injectable";
|
||||
import requestHelmReleasesInjectable from "../../../common/k8s-api/endpoints/helm-releases.api/request-releases.injectable";
|
||||
import toHelmReleaseInjectable from "./to-helm-release.injectable";
|
||||
|
||||
const releasesInjectable = getInjectable({
|
||||
id: "releases",
|
||||
|
||||
instantiate: (di) => {
|
||||
const clusterContext = di.inject(clusterFrameContextForNamespacedResourcesInjectable);
|
||||
const releaseSecrets = di.inject(releaseSecretsInjectable);
|
||||
const requestHelmReleases = di.inject(requestHelmReleasesInjectable);
|
||||
const toHelmRelease = di.inject(toHelmReleaseInjectable);
|
||||
|
||||
return asyncComputed({
|
||||
getValueFromObservedPromise: async () => {
|
||||
void releaseSecrets.get();
|
||||
|
||||
const releaseArrays = await (
|
||||
clusterContext.hasSelectedAll
|
||||
? requestHelmReleases()
|
||||
: Promise.all(clusterContext.contextNamespaces.map((namespace) => requestHelmReleases(namespace)))
|
||||
);
|
||||
|
||||
return releaseArrays.flat().map(toHelmRelease);
|
||||
},
|
||||
|
||||
valueWhenPending: [],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export default releasesInjectable;
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
@import "release.mixins";
|
||||
|
||||
.HelmReleases {
|
||||
.TableCell {
|
||||
&.status {
|
||||
@include release-status-colors;
|
||||
}
|
||||
|
||||
&.version {
|
||||
> .Icon {
|
||||
margin-left: $margin;
|
||||
|
||||
&.new-version {
|
||||
color: var(--colorInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "../item-object-list/item-list-layout.scss";
|
||||
import "./releases.scss";
|
||||
|
||||
import React, { Component } from "react";
|
||||
import type { HelmRelease } from "../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { ItemListStore } from "../item-object-list";
|
||||
import { ItemListLayout } from "../item-object-list";
|
||||
import { NamespaceSelectFilter } from "../namespaces/namespace-select-filter";
|
||||
import { kebabCase } from "lodash/fp";
|
||||
import { HelmReleaseMenu } from "./release-menu";
|
||||
import { ReleaseRollbackDialog } from "./dialog/dialog";
|
||||
import removableReleasesInjectable from "./removable-releases.injectable";
|
||||
import type { RemovableHelmRelease } from "./removable-releases";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import releasesInjectable from "./releases.injectable";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import helmReleasesRouteParametersInjectable from "./helm-releases-route-parameters.injectable";
|
||||
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 { NamespaceSelectBadge } from "../namespaces/namespace-select-badge";
|
||||
|
||||
enum columnId {
|
||||
name = "name",
|
||||
namespace = "namespace",
|
||||
revision = "revision",
|
||||
chart = "chart",
|
||||
version = "version",
|
||||
appVersion = "app-version",
|
||||
status = "status",
|
||||
updated = "update",
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
releases: IComputedValue<RemovableHelmRelease[]>;
|
||||
releasesArePending: IComputedValue<boolean>;
|
||||
namespace: IComputedValue<string>;
|
||||
navigateToHelmReleases: NavigateToHelmReleases;
|
||||
}
|
||||
|
||||
class NonInjectedHelmReleases extends Component<Dependencies> {
|
||||
onDetails = (item: HelmRelease) => {
|
||||
this.showDetails(item);
|
||||
};
|
||||
|
||||
showDetails = (item: HelmRelease) => {
|
||||
this.props.navigateToHelmReleases({
|
||||
name: item.getName(),
|
||||
namespace: item.getNs(),
|
||||
});
|
||||
};
|
||||
|
||||
hideDetails = () => {
|
||||
this.props.navigateToHelmReleases();
|
||||
};
|
||||
|
||||
renderRemoveDialogMessage(selectedItems: HelmRelease[]) {
|
||||
const releaseNames = selectedItems.map(item => item.getName()).join(", ");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
Remove
|
||||
{" "}
|
||||
<b>{releaseNames}</b>
|
||||
?
|
||||
</>
|
||||
<p className="warning">
|
||||
Note: StatefulSet Volumes won't be deleted automatically
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const releases = this.props.releases;
|
||||
const releasesArePending = this.props.releasesArePending;
|
||||
|
||||
// TODO: Implement ItemListLayout without stateful stores
|
||||
const legacyReleaseStore: ItemListStore<RemovableHelmRelease, false> = {
|
||||
get isLoaded() {
|
||||
return !releasesArePending.get();
|
||||
},
|
||||
|
||||
failedLoading: false,
|
||||
|
||||
getTotalCount: () => releases.get().length,
|
||||
|
||||
toggleSelection: (release) => release.toggle(),
|
||||
|
||||
isSelectedAll: (releases) => (
|
||||
releases.length > 0
|
||||
&& releases.every((release) => release.isSelected)
|
||||
),
|
||||
|
||||
toggleSelectionAll: (releases) => {
|
||||
let selected = false;
|
||||
|
||||
if (!legacyReleaseStore.isSelectedAll(releases)) {
|
||||
selected = true;
|
||||
}
|
||||
|
||||
for (const release of releases) {
|
||||
if (release.isSelected !== selected) {
|
||||
release.toggle();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
isSelected: (release) => release.isSelected,
|
||||
|
||||
removeSelectedItems: async () => {
|
||||
await Promise.all(
|
||||
releases.get()
|
||||
.filter((release) => release.isSelected)
|
||||
.map(release => release.delete()),
|
||||
);
|
||||
},
|
||||
|
||||
pickOnlySelected: (releases) => releases.filter(release => release.isSelected),
|
||||
};
|
||||
|
||||
return (
|
||||
<SiblingsInTabLayout>
|
||||
<ItemListLayout<RemovableHelmRelease, false>
|
||||
store={legacyReleaseStore}
|
||||
getItems={() => releases.get()}
|
||||
preloadStores={false}
|
||||
isConfigurable
|
||||
tableId="helm_releases"
|
||||
className="HelmReleases"
|
||||
customizeTableRowProps={(item) => ({ testId: `helm-release-row-for-${item.getId()}` })}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: release => release.getName(),
|
||||
[columnId.namespace]: release => release.getNs(),
|
||||
[columnId.revision]: release => release.getRevision(),
|
||||
[columnId.chart]: release => release.getChart(),
|
||||
[columnId.status]: release => release.getStatus(),
|
||||
[columnId.updated]: release => release.getUpdated(false, false),
|
||||
}}
|
||||
searchFilters={[
|
||||
release => release.getName(),
|
||||
release => release.getNs(),
|
||||
release => release.getChart(),
|
||||
release => release.getStatus(),
|
||||
release => release.getVersion(),
|
||||
]}
|
||||
customizeHeader={({ filters, searchProps, ...headerPlaceholders }) => ({
|
||||
filters: (
|
||||
<>
|
||||
{filters}
|
||||
<NamespaceSelectFilter id="namespace-select-filter" />
|
||||
</>
|
||||
),
|
||||
searchProps: {
|
||||
...searchProps,
|
||||
placeholder: "Search Releases...",
|
||||
},
|
||||
...headerPlaceholders,
|
||||
})}
|
||||
renderHeaderTitle="Releases"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Chart", className: "chart", sortBy: columnId.chart, id: columnId.chart },
|
||||
{ title: "Revision", className: "revision", sortBy: columnId.revision, id: columnId.revision },
|
||||
{ title: "Version", className: "version", id: columnId.version },
|
||||
{ title: "App Version", className: "app-version", id: columnId.appVersion },
|
||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||
{ title: "Updated", className: "updated", sortBy: columnId.updated, id: columnId.updated },
|
||||
]}
|
||||
renderTableContents={release => [
|
||||
release.getName(),
|
||||
<NamespaceSelectBadge
|
||||
key="namespace"
|
||||
namespace={release.getNs()}
|
||||
/>,
|
||||
release.getChart(),
|
||||
release.getRevision(),
|
||||
release.getVersion(),
|
||||
release.appVersion,
|
||||
{ title: release.getStatus(), className: kebabCase(release.getStatus()) },
|
||||
release.getUpdated(),
|
||||
]}
|
||||
renderItemMenu={release => (
|
||||
<HelmReleaseMenu
|
||||
release={release}
|
||||
removeConfirmationMessage={this.renderRemoveDialogMessage([release])}
|
||||
/>
|
||||
)}
|
||||
customizeRemoveDialog={selectedItems => ({
|
||||
message: this.renderRemoveDialogMessage(selectedItems),
|
||||
})}
|
||||
onDetails={this.onDetails}
|
||||
spinnerTestId="helm-releases-spinner"
|
||||
/>
|
||||
|
||||
<ReleaseRollbackDialog/>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const HelmReleases = withInjectables<Dependencies>(NonInjectedHelmReleases, {
|
||||
getProps: (di) => ({
|
||||
releases: di.inject(removableReleasesInjectable),
|
||||
releasesArePending: di.inject(releasesInjectable).pending,
|
||||
navigateToHelmReleases: di.inject(navigateToHelmReleasesInjectable),
|
||||
...di.inject(helmReleasesRouteParametersInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
import releasesInjectable from "./releases.injectable";
|
||||
import deleteReleaseInjectable from "./delete-release/delete-release.injectable";
|
||||
import { removableReleases } from "./removable-releases";
|
||||
|
||||
const removableReleasesInjectable = getInjectable({
|
||||
id: "removable-releases",
|
||||
|
||||
instantiate: (di) =>
|
||||
removableReleases({
|
||||
releases: di.inject(releasesInjectable),
|
||||
deleteRelease: di.inject(deleteReleaseInjectable),
|
||||
releaseSelectionStatus: observable.map<string, boolean>(),
|
||||
}),
|
||||
});
|
||||
|
||||
export default removableReleasesInjectable;
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
|
||||
import type { ObservableMap } from "mobx";
|
||||
import { computed } from "mobx";
|
||||
import type { HelmRelease } from "../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
|
||||
interface Dependencies {
|
||||
releases: IAsyncComputed<HelmRelease[]>;
|
||||
releaseSelectionStatus: ObservableMap<string, boolean>;
|
||||
deleteRelease: (release: HelmRelease) => Promise<any>;
|
||||
}
|
||||
|
||||
export interface RemovableHelmRelease extends HelmRelease {
|
||||
toggle: () => void;
|
||||
isSelected: boolean;
|
||||
delete: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const removableReleases = ({
|
||||
releases,
|
||||
releaseSelectionStatus,
|
||||
deleteRelease,
|
||||
}: Dependencies) => {
|
||||
const isSelected = (release: HelmRelease) =>
|
||||
releaseSelectionStatus.get(release.getId()) || false;
|
||||
|
||||
return computed(() =>
|
||||
releases.value.get().map(
|
||||
(release): RemovableHelmRelease => ({
|
||||
...release,
|
||||
|
||||
toggle: () => {
|
||||
releaseSelectionStatus.set(release.getId(), !isSelected(release));
|
||||
},
|
||||
|
||||
get isSelected() {
|
||||
return isSelected(release);
|
||||
},
|
||||
|
||||
delete: async () => {
|
||||
await deleteRelease(release);
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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 requestHelmReleaseRollbackInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/request-rollback.injectable";
|
||||
import releasesInjectable from "../releases.injectable";
|
||||
|
||||
export type RollbackRelease = (name: string, namespace: string, revision: number) => Promise<void>;
|
||||
|
||||
const rollbackReleaseInjectable = getInjectable({
|
||||
id: "rollback-release",
|
||||
|
||||
instantiate: (di): RollbackRelease => {
|
||||
const releases = di.inject(releasesInjectable);
|
||||
const requestHelmReleaseRollback = di.inject(requestHelmReleaseRollbackInjectable);
|
||||
|
||||
return async (name, namespace, revision) => {
|
||||
await requestHelmReleaseRollback(name, namespace, revision);
|
||||
|
||||
releases.invalidate();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default rollbackReleaseInjectable;
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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 { capitalize } from "lodash";
|
||||
import { when } from "mobx";
|
||||
import helmChartVersionsInjectable from "../helm-charts/helm-charts/versions.injectable";
|
||||
import type { HelmRelease, HelmReleaseDto } from "../../../common/k8s-api/endpoints/helm-releases.api";
|
||||
import { getMillisecondsFromUnixEpoch } from "../../../common/utils/date/get-current-date-time";
|
||||
import { formatDuration } from "@k8slens/utilities";
|
||||
|
||||
export type ToHelmRelease = (release: HelmReleaseDto) => HelmRelease;
|
||||
|
||||
const toHelmReleaseInjectable = getInjectable({
|
||||
id: "to-helm-release",
|
||||
instantiate: (di): ToHelmRelease => {
|
||||
const helmChartVersions = (release: HelmRelease) => di.inject(helmChartVersionsInjectable, release);
|
||||
|
||||
return (release) => ({
|
||||
...release,
|
||||
|
||||
getId() {
|
||||
return `${this.namespace}/${this.name}`;
|
||||
},
|
||||
|
||||
getName() {
|
||||
return this.name;
|
||||
},
|
||||
|
||||
getNs() {
|
||||
return this.namespace;
|
||||
},
|
||||
|
||||
getChart(withVersion = false) {
|
||||
let chart = this.chart;
|
||||
|
||||
if (!withVersion && this.getVersion() != "") {
|
||||
const search = new RegExp(`-${this.getVersion()}`);
|
||||
|
||||
chart = chart.replace(search, "");
|
||||
}
|
||||
|
||||
return chart;
|
||||
},
|
||||
|
||||
getRevision() {
|
||||
return parseInt(this.revision, 10);
|
||||
},
|
||||
|
||||
getStatus() {
|
||||
return capitalize(this.status);
|
||||
},
|
||||
|
||||
getVersion() {
|
||||
const versions = this.chart.match(/(?<=-)(v?\d+)[^-].*$/);
|
||||
|
||||
return versions?.[0] ?? "";
|
||||
},
|
||||
|
||||
getUpdated(humanize = true, compact = true) {
|
||||
const updated = this.updated.replace(/\s\w*$/, ""); // 2019-11-26 10:58:09 +0300 MSK -> 2019-11-26 10:58:09 +0300 to pass into Date()
|
||||
const updatedDate = new Date(updated).getTime();
|
||||
const diff = getMillisecondsFromUnixEpoch() - updatedDate;
|
||||
|
||||
if (humanize) {
|
||||
return formatDuration(diff, compact);
|
||||
}
|
||||
|
||||
return diff;
|
||||
},
|
||||
|
||||
// Helm does not store from what repository the release is installed,
|
||||
// so we have to try to guess it by searching charts
|
||||
async getRepo() {
|
||||
const versionsComputed = helmChartVersions(this);
|
||||
const version = this.getVersion();
|
||||
|
||||
await when(() => !versionsComputed.pending.get());
|
||||
|
||||
return versionsComputed.value
|
||||
.get()
|
||||
.find((chartVersion) => chartVersion.version === version)?.repo
|
||||
?? "";
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default toHelmReleaseInjectable;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import releasesInjectable from "../releases.injectable";
|
||||
import type { RequestHelmReleaseUpdate } from "../../../../common/k8s-api/endpoints/helm-releases.api/request-update.injectable";
|
||||
import requestHelmReleaseUpdateInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/request-update.injectable";
|
||||
|
||||
const updateReleaseInjectable = getInjectable({
|
||||
id: "update-release",
|
||||
|
||||
instantiate: (di): RequestHelmReleaseUpdate => {
|
||||
const releases = di.inject(releasesInjectable);
|
||||
const requestHelmReleaseUpdate = di.inject(requestHelmReleaseUpdateInjectable);
|
||||
|
||||
return async (
|
||||
name,
|
||||
namespace,
|
||||
payload,
|
||||
) => {
|
||||
const result = await requestHelmReleaseUpdate(name, namespace, payload);
|
||||
|
||||
releases.invalidate();
|
||||
|
||||
return result;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default updateReleaseInjectable;
|
||||
Reference in new issue
Block a user