1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Switch to non-reactive way of gettting possible helm release versions

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-08-12 11:25:56 -04:00
parent 5798f6722e
commit 18db8d544d
9 changed files with 122 additions and 91 deletions

View File

@ -3,11 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { SemVer } from "semver";
import semver, { coerce } from "semver";
import * as iter from "./iter";
import type { RawHelmChart } from "../k8s-api/endpoints/helm-charts.api";
import logger from "../logger";
export enum Ordering {
LESS = -1,
@ -49,52 +45,31 @@ export function sortCompare<T>(left: T, right: T): Ordering {
return Ordering.GREATER;
}
interface ChartVersion {
version: string;
__version?: SemVer | null;
}
export function sortCompareChartVersions(left: ChartVersion, right: ChartVersion): Ordering {
if (left.__version && right.__version) {
return semver.compare(right.__version, left.__version);
}
if (!left.__version && right.__version) {
return Ordering.GREATER;
}
if (left.__version && !right.__version) {
return Ordering.LESS;
}
return sortCompare(left.version, right.version);
}
export function sortCharts(charts: RawHelmChart[]) {
interface ExtendedHelmChart extends RawHelmChart {
__version?: SemVer | null;
}
const chartsWithVersion = Array.from(
iter.map(
charts,
chart => {
const __version = coerce(chart.version, { loose: true });
if (!__version) {
logger.warn(`[HELM-SERVICE]: Version from helm chart is not loosely coercable to semver.`, { name: chart.name, version: chart.version, repo: chart.repo });
}
(chart as ExtendedHelmChart).__version = __version;
return chart as ExtendedHelmChart;
},
),
);
return chartsWithVersion
.sort(sortCompareChartVersions)
.map(chart => (delete chart.__version, chart as RawHelmChart));
/**
* This function sorts of list of items that have what should be a semver version formated string
* as the field `version` but if it is not loosely coercable to semver falls back to sorting them
* alphanumerically
*/
export function sortBySemverVersion<T extends { version: string }>(versioneds: T[]): T[] {
return versioneds
.map(versioned => ({
__version: coerce(versioned.version, { loose: true }),
raw: versioned,
}))
.sort((left, right) => {
if (left.__version && right.__version) {
return semver.compare(right.__version, left.__version);
}
if (!left.__version && right.__version) {
return Ordering.GREATER;
}
if (left.__version && !right.__version) {
return Ordering.LESS;
}
return sortCompare(left.raw.version, right.raw.version);
})
.map(({ raw }) => raw);
}

View File

@ -8,7 +8,7 @@ import listHelmChartsInjectable from "../helm-service/list-helm-charts.injectabl
import getActiveHelmRepositoriesInjectable from "../repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
import type { AsyncResult } from "../../../common/utils/async-result";
import type { HelmRepo } from "../../../common/helm/helm-repo";
import { sortCharts } from "../../../common/utils";
import { sortBySemverVersion } from "../../../common/utils";
import helmChartManagerInjectable from "../helm-chart-manager.injectable";
describe("Helm Service tests", () => {
@ -203,7 +203,7 @@ describe("Helm Service tests", () => {
const charts = new Map([
["stable", {
"invalid-semver": sortCharts([
"invalid-semver": sortBySemverVersion([
{
apiVersion: "3.0.0",
name: "weird-versioning",
@ -237,7 +237,7 @@ const charts = new Map([
created: "now",
},
]),
"apm-server": sortCharts([
"apm-server": sortBySemverVersion([
{
apiVersion: "3.0.0",
name: "apm-server",
@ -255,7 +255,7 @@ const charts = new Map([
created: "now",
},
]),
"redis": sortCharts([
"redis": sortBySemverVersion([
{
apiVersion: "3.0.0",
name: "apm-server",
@ -275,7 +275,7 @@ const charts = new Map([
]),
}],
["experiment", {
"fairwind": sortCharts([
"fairwind": sortBySemverVersion([
{
apiVersion: "3.0.0",
name: "fairwind",
@ -296,7 +296,7 @@ const charts = new Map([
]),
}],
["bitnami", {
"hotdog": sortCharts([
"hotdog": sortBySemverVersion([
{
apiVersion: "3.0.0",
name: "hotdog",
@ -314,7 +314,7 @@ const charts = new Map([
created: "now",
},
]),
"pretzel": sortCharts([
"pretzel": sortBySemverVersion([
{
apiVersion: "3.0.0",
name: "pretzel",

View File

@ -5,7 +5,7 @@
import fs from "fs";
import * as yaml from "js-yaml";
import { iter, put, sortCharts } from "../../common/utils";
import { iter, put, sortBySemverVersion } from "../../common/utils";
import { execHelm } from "./exec";
import type { SetRequired } from "type-fest";
import { assert } from "console";
@ -118,7 +118,7 @@ function normalizeHelmCharts(repoName: string, entries: RepoHelmChartList): Repo
Object.entries(entries),
([name, charts]) => [
name,
sortCharts(
sortBySemverVersion(
charts.map(chart => ({
...chart,
created: Date.parse(chart.created).toString(),

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { HelmChart } from "../../../../common/k8s-api/endpoints/helm-charts.api";
import requestHelmChartVersionsInjectable from "../../../../common/k8s-api/endpoints/helm-charts.api/request-versions.injectable";
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
import { sortBySemverVersion } from "../../../utils";
import type { HelmChartVersion } from "./versions";
/**
* @param release The release to try and figure out what chart versions match for
* @param charts The list of possible helm charts that `release` could be of
*/
export type RequestVersionsOfHelmChart = (release: HelmRelease, charts: HelmChart[]) => Promise<HelmChartVersion[]>;
const requestVersionsOfHelmChartInjectable = getInjectable({
id: "request-versions-of-helm-chart-for-helm-release-from-list-of-charts",
instantiate: (di): RequestVersionsOfHelmChart => {
const requestHelmChartVersions = di.inject(requestHelmChartVersionsInjectable);
return async (release, charts) => {
const rawVersions = await Promise.all((
charts
.filter(chart => chart.getName() === release.getChart())
.map(chart => chart.getRepository())
.map(repo => requestHelmChartVersions(repo, release.getChart()))
));
return sortBySemverVersion(rawVersions.flat());
};
},
});
export default requestVersionsOfHelmChartInjectable;

View File

@ -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 requestHelmChartsInjectable from "../../../../common/k8s-api/endpoints/helm-charts.api/request-charts.injectable";
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
import requestVersionsOfHelmChartInjectable from "./request-versions-of-chart-for-release.injectable";
import type { HelmChartVersion } from "./versions";
export type RequestVersionsOfHelmChartFor = (release: HelmRelease) => Promise<HelmChartVersion[]>;
const requestVersionsOfHelmChartForInjectable = getInjectable({
id: "request-versions-of-helm-chart-for",
instantiate: (di): RequestVersionsOfHelmChartFor => {
const requestHelmCharts = di.inject(requestHelmChartsInjectable);
const requestVersionsOfHelmChart = di.inject(requestVersionsOfHelmChartInjectable);
return async (release) => {
const charts = await requestHelmCharts();
return requestVersionsOfHelmChart(release, charts);
};
},
});
export default requestVersionsOfHelmChartForInjectable;

View File

@ -5,40 +5,20 @@
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import { asyncComputed } from "@ogre-tools/injectable-react";
import { when } from "mobx";
import { coerce } from "semver";
import requestHelmChartVersionsInjectable from "../../../../common/k8s-api/endpoints/helm-charts.api/request-versions.injectable";
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
import { sortCompareChartVersions } from "../../../utils";
import helmChartsInjectable from "./helm-charts.injectable";
export interface ChartVersion {
repo: string;
version: string;
}
const sortChartVersions = (versions: ChartVersion[]) => (
versions
.map(chartVersion => ({ ...chartVersion, __version: coerce(chartVersion.version, { loose: true }) }))
.sort(sortCompareChartVersions)
.map(({ __version, ...chartVersion }) => chartVersion)
);
import requestVersionsOfHelmChartInjectable from "./request-versions-of-chart-for-release.injectable";
const helmChartVersionsInjectable = getInjectable({
id: "helm-chart-versions-loader",
instantiate: (di, release) => {
const requestHelmChartVersions = di.inject(requestHelmChartVersionsInjectable);
const helmCharts = di.inject(helmChartsInjectable);
const requestVersionsOfHelmChart = di.inject(requestVersionsOfHelmChartInjectable);
return asyncComputed(async () => {
await when(() => !helmCharts.pending.get());
const rawVersions = await Promise.all((
helmCharts.value.get()
.filter(chart => chart.getName() === release.getChart())
.map(chart => chart.getRepository())
.map(repo => requestHelmChartVersions(repo, release.getChart()))
));
return sortChartVersions(rawVersions.flat());
return requestVersionsOfHelmChart(release, helmCharts.value.get());
}, []);
},
lifecycle: lifecycleEnum.keyedSingleton({

View File

@ -0,0 +1,12 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* A type for the possible versions that a helm release was made from
*/
export interface HelmChartVersion {
repo: string;
version: string;
}

View File

@ -7,7 +7,7 @@ import { asyncComputed } from "@ogre-tools/injectable-react";
import type { IComputedValue } from "mobx";
import { action, computed, observable, when } from "mobx";
import type { SingleValue } from "react-select";
import type { ChartVersion } from "../../+helm-charts/helm-charts/versions.injectable";
import type { HelmChartVersion } from "../../+helm-charts/helm-charts/versions";
import helmChartVersionsInjectable from "../../+helm-charts/helm-charts/versions.injectable";
import releasesInjectable from "../../+helm-releases/releases.injectable";
import updateReleaseInjectable from "../../+helm-releases/update-release/update-release.injectable";
@ -20,7 +20,7 @@ import upgradeChartTabStoreInjectable from "./store.injectable";
export interface UpgradeChartModel {
readonly release: HelmRelease;
readonly versionOptions: IComputedValue<SelectOption<ChartVersion>[]>;
readonly versionOptions: IComputedValue<SelectOption<HelmChartVersion>[]>;
readonly configration: {
readonly value: IComputedValue<string>;
set: (value: string) => void;
@ -28,8 +28,8 @@ export interface UpgradeChartModel {
setError: (error: unknown) => void;
};
readonly version: {
readonly value: IComputedValue<ChartVersion | undefined>;
set: (value: SingleValue<SelectOption<ChartVersion>>) => void;
readonly value: IComputedValue<HelmChartVersion | undefined>;
set: (value: SingleValue<SelectOption<HelmChartVersion>>) => void;
};
submit: () => Promise<UpgradeChartSubmitResult>;
}
@ -68,7 +68,7 @@ const upgradeChartModelInjectable = getInjectable({
error: computed(() => configrationEditError.get()),
setError: action((error) => configrationEditError.set(String(error))),
};
const versionValue = observable.box<ChartVersion>(undefined, {
const versionValue = observable.box<HelmChartVersion>(undefined, {
deep: false,
});
const version: UpgradeChartModel["version"] = {

View File

@ -17,9 +17,9 @@ import { EditorPanel } from "../editor-panel";
import type { SelectOption } from "../../select";
import { Select } from "../../select";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { ChartVersion } from "../../+helm-charts/helm-charts/versions.injectable";
import type { UpgradeChartModel } from "./upgrade-chart-model.injectable";
import upgradeChartModelInjectable from "./upgrade-chart-model.injectable";
import type { HelmChartVersion } from "../../+helm-charts/helm-charts/versions";
export interface UpgradeChartProps {
className?: string;
@ -82,7 +82,7 @@ export class NonInjectedUpgradeChart extends React.Component<UpgradeChartProps &
{" "}
<Badge label={release.getVersion()} />
<span>Upgrade version</span>
<Select<ChartVersion, SelectOption<ChartVersion>, false>
<Select<HelmChartVersion, SelectOption<HelmChartVersion>, false>
id="char-version-input"
className="chart-version"
menuPlacement="top"