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

Fix: deprecated helm chart filtering (#2158)

* Refactor of excludeDeprecated helm service method

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Pick first helm chart from the list on load

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Removing helm filtering in UI

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Cleaning up

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Cleaning up type definitions

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Adding sorting charts by version

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Adding tests for methods that manipute chart listing

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Cleaning up tests a bit

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Adding semver coercion before comparing versions

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
Alex Andreev 2021-02-24 10:20:06 +03:00 committed by GitHub
parent 103467d31b
commit 6876d774a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 246 additions and 31 deletions

View File

@ -0,0 +1,108 @@
import { HelmRepo, HelmRepoManager } from "../helm-repo-manager";
export class HelmChartManager {
private cache: any = {};
private repo: HelmRepo;
constructor(repo: HelmRepo){
this.cache = HelmRepoManager.cache;
this.repo = repo;
}
public async charts(): Promise<any> {
switch (this.repo.name) {
case "stable":
return Promise.resolve({
"apm-server": [
{
apiVersion: "3.0.0",
name: "apm-server",
version: "2.1.7",
repo: "stable",
digest: "test"
},
{
apiVersion: "3.0.0",
name: "apm-server",
version: "2.1.6",
repo: "stable",
digest: "test"
}
],
"redis": [
{
apiVersion: "3.0.0",
name: "apm-server",
version: "1.0.0",
repo: "stable",
digest: "test"
},
{
apiVersion: "3.0.0",
name: "apm-server",
version: "0.0.9",
repo: "stable",
digest: "test"
}
]
});
case "experiment":
return Promise.resolve({
"fairwind": [
{
apiVersion: "3.0.0",
name: "fairwind",
version: "0.0.1",
repo: "experiment",
digest: "test"
},
{
apiVersion: "3.0.0",
name: "fairwind",
version: "0.0.2",
repo: "experiment",
digest: "test",
deprecated: true
}
]
});
case "bitnami":
return Promise.resolve({
"hotdog": [
{
apiVersion: "3.0.0",
name: "hotdog",
version: "1.0.1",
repo: "bitnami",
digest: "test"
},
{
apiVersion: "3.0.0",
name: "hotdog",
version: "1.0.2",
repo: "bitnami",
digest: "test",
}
],
"pretzel": [
{
apiVersion: "3.0.0",
name: "pretzel",
version: "1.0",
repo: "bitnami",
digest: "test",
},
{
apiVersion: "3.0.0",
name: "pretzel",
version: "1.0.1",
repo: "bitnami",
digest: "test"
}
]
});
default:
return Promise.resolve({});
}
}
}

View File

@ -0,0 +1,104 @@
import { helmService } from "../helm-service";
import { repoManager } from "../helm-repo-manager";
jest.spyOn(repoManager, "init").mockImplementation();
jest.mock("../helm-chart-manager");
describe("Helm Service tests", () => {
test("list charts without deprecated ones", async () => {
jest.spyOn(repoManager, "repositories").mockImplementation(async () => {
return [
{ name: "stable", url: "stableurl" },
{ name: "experiment", url: "experimenturl" }
];
});
const charts = await helmService.listCharts();
expect(charts).toEqual({
stable: {
"apm-server": [
{
apiVersion: "3.0.0",
name: "apm-server",
version: "2.1.7",
repo: "stable",
digest: "test"
},
{
apiVersion: "3.0.0",
name: "apm-server",
version: "2.1.6",
repo: "stable",
digest: "test"
}
],
"redis": [
{
apiVersion: "3.0.0",
name: "apm-server",
version: "1.0.0",
repo: "stable",
digest: "test"
},
{
apiVersion: "3.0.0",
name: "apm-server",
version: "0.0.9",
repo: "stable",
digest: "test"
}
]
},
experiment: {}
});
});
test("list charts sorted by version in descending order", async () => {
jest.spyOn(repoManager, "repositories").mockImplementation(async () => {
return [
{ name: "bitnami", url: "bitnamiurl" }
];
});
const charts = await helmService.listCharts();
expect(charts).toEqual({
bitnami: {
"hotdog": [
{
apiVersion: "3.0.0",
name: "hotdog",
version: "1.0.2",
repo: "bitnami",
digest: "test",
},
{
apiVersion: "3.0.0",
name: "hotdog",
version: "1.0.1",
repo: "bitnami",
digest: "test"
},
],
"pretzel": [
{
apiVersion: "3.0.0",
name: "pretzel",
version: "1.0.1",
repo: "bitnami",
digest: "test",
},
{
apiVersion: "3.0.0",
name: "pretzel",
version: "1.0",
repo: "bitnami",
digest: "test"
}
]
}
});
});
});

View File

@ -4,9 +4,10 @@ import { HelmRepo, HelmRepoManager } from "./helm-repo-manager";
import logger from "../logger"; import logger from "../logger";
import { promiseExec } from "../promise-exec"; import { promiseExec } from "../promise-exec";
import { helmCli } from "./helm-cli"; import { helmCli } from "./helm-cli";
import type { RepoHelmChartList } from "../../renderer/api/endpoints/helm-charts.api";
type CachedYaml = { type CachedYaml = {
entries: any; // todo: types entries: RepoHelmChartList
}; };
export class HelmChartManager { export class HelmChartManager {
@ -24,15 +25,15 @@ export class HelmChartManager {
return charts[name]; return charts[name];
} }
public async charts(): Promise<any> { public async charts(): Promise<RepoHelmChartList> {
try { try {
const cachedYaml = await this.cachedYaml(); const cachedYaml = await this.cachedYaml();
return cachedYaml["entries"]; return cachedYaml["entries"];
} catch(error) { } catch(error) {
logger.error(error); logger.error("HELM-CHART-MANAGER]: failed to list charts", { error });
return []; return {};
} }
} }

View File

@ -1,8 +1,10 @@
import semver from "semver";
import { Cluster } from "../cluster"; import { Cluster } from "../cluster";
import logger from "../logger"; import logger from "../logger";
import { repoManager } from "./helm-repo-manager"; import { repoManager } from "./helm-repo-manager";
import { HelmChartManager } from "./helm-chart-manager"; import { HelmChartManager } from "./helm-chart-manager";
import { releaseManager } from "./helm-release-manager"; import { releaseManager } from "./helm-release-manager";
import { HelmChartList, RepoHelmChartList } from "../../renderer/api/endpoints/helm-charts.api";
class HelmService { class HelmService {
public async installChart(cluster: Cluster, data: { chart: string; values: {}; name: string; namespace: string; version: string }) { public async installChart(cluster: Cluster, data: { chart: string; values: {}; name: string; namespace: string; version: string }) {
@ -10,7 +12,7 @@ class HelmService {
} }
public async listCharts() { public async listCharts() {
const charts: any = {}; const charts: HelmChartList = {};
await repoManager.init(); await repoManager.init();
const repositories = await repoManager.repositories(); const repositories = await repoManager.repositories();
@ -18,14 +20,10 @@ class HelmService {
for (const repo of repositories) { for (const repo of repositories) {
charts[repo.name] = {}; charts[repo.name] = {};
const manager = new HelmChartManager(repo); const manager = new HelmChartManager(repo);
let entries = await manager.charts(); const sortedCharts = this.sortChartsByVersion(await manager.charts());
const enabledCharts = this.excludeDeprecatedChartGroups(sortedCharts);
entries = this.excludeDeprecated(entries); charts[repo.name] = enabledCharts;
for (const key in entries) {
entries[key] = entries[key][0];
}
charts[repo.name] = entries;
} }
return charts; return charts;
@ -96,20 +94,30 @@ class HelmService {
return { message: output }; return { message: output };
} }
protected excludeDeprecated(entries: any) { private excludeDeprecatedChartGroups(chartGroups: RepoHelmChartList) {
for (const key in entries) { const groups = new Map(Object.entries(chartGroups));
entries[key] = entries[key].filter((entry: any) => {
if (Array.isArray(entry)) {
return entry[0]["deprecated"] != true;
}
return entry["deprecated"] != true; for (const [chartName, charts] of groups) {
if (charts[0].deprecated) {
groups.delete(chartName);
}
}
return Object.fromEntries(groups);
}
private sortChartsByVersion(chartGroups: RepoHelmChartList) {
for (const key in chartGroups) {
chartGroups[key] = chartGroups[key].sort((first, second) => {
const firstVersion = semver.coerce(first.version || 0);
const secondVersion = semver.coerce(second.version || 0);
return semver.compare(secondVersion, firstVersion);
}); });
} }
return entries; return chartGroups;
} }
} }
export const helmService = new HelmService(); export const helmService = new HelmService();

View File

@ -3,11 +3,8 @@ import { apiBase } from "../index";
import { stringify } from "querystring"; import { stringify } from "querystring";
import { autobind } from "../../utils"; import { autobind } from "../../utils";
interface IHelmChartList { export type RepoHelmChartList = Record<string, HelmChart[]>;
[repo: string]: { export type HelmChartList = Record<string, RepoHelmChartList>;
[name: string]: HelmChart;
};
}
export interface IHelmChartDetails { export interface IHelmChartDetails {
readme: string; readme: string;
@ -22,12 +19,12 @@ const endpoint = compile(`/v2/charts/:repo?/:name?`) as (params?: {
export const helmChartsApi = { export const helmChartsApi = {
list() { list() {
return apiBase return apiBase
.get<IHelmChartList>(endpoint()) .get<HelmChartList>(endpoint())
.then(data => { .then(data => {
return Object return Object
.values(data) .values(data)
.reduce((allCharts, repoCharts) => allCharts.concat(Object.values(repoCharts)), []) .reduce((allCharts, repoCharts) => allCharts.concat(Object.values(repoCharts)), [])
.map(HelmChart.create); .map(([chart]) => HelmChart.create(chart));
}); });
}, },

View File

@ -72,9 +72,6 @@ export class HelmCharts extends Component<Props> {
(chart: HelmChart) => chart.getAppVersion(), (chart: HelmChart) => chart.getAppVersion(),
(chart: HelmChart) => chart.getKeywords(), (chart: HelmChart) => chart.getKeywords(),
]} ]}
filterItems={[
(items: HelmChart[]) => items.filter(item => !item.deprecated)
]}
customizeHeader={() => ( customizeHeader={() => (
<SearchInputUrl placeholder="Search Helm Charts" /> <SearchInputUrl placeholder="Search Helm Charts" />
)} )}