Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1883595: Fix chart merging and show chart provider in catalog #6761

Merged
merged 2 commits into from Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions frontend/packages/console-shared/src/utils/__tests__/utils.spec.ts
@@ -1,4 +1,5 @@
import { getRandomChars } from '@console/shared/src/utils/utils';
import { toTitleCase } from '../utils';

describe('Utils', () => {
describe('getRandomCharacters', () => {
Expand All @@ -11,4 +12,14 @@ describe('Utils', () => {
expect(randomOutput).toHaveLength(6);
});
});

describe('toTitleCase', () => {
it('Converts string to titleCase correctly', () => {
expect(toTitleCase('ibm-b2bi-prod')).toBe('Ibm B2bi Prod');
expect(toTitleCase('IBM-b2BI-prod')).toBe('IBM B2BI Prod');
expect(toTitleCase('ibm-b2bi-prod--ibm-helm-catalog')).toBe(
'Ibm B2bi Prod Ibm Helm Catalog',
);
});
});
});
8 changes: 8 additions & 0 deletions frontend/packages/console-shared/src/utils/utils.ts
Expand Up @@ -63,3 +63,11 @@ const recursiveGetSchemaAtPath = (
export const getSchemaAtPath = (schema: JSONSchema6, path: string): JSONSchema6 => {
return recursiveGetSchemaAtPath(schema, toPath(path));
};

// Converts a string to title case `some-title` -> `Some Title`
export const toTitleCase = (title: string): string => {
return title
.split('-')
.map((w) => (w ? w[0].toUpperCase() + w.substr(1) : ''))
.join(' ');
};
Expand Up @@ -35,6 +35,7 @@ export type HelmInstallUpgradeFormData = {
releaseName: string;
chartURL?: string;
chartName: string;
chartRepoName: string;
chartVersion: string;
chartReadme: string;
appVersion: string;
Expand All @@ -54,6 +55,7 @@ const HelmInstallUpgradePage: React.FunctionComponent<HelmInstallUpgradePageProp
const initialChartURL = decodeURIComponent(searchParams.get('chartURL'));
const initialReleaseName = match.params.releaseName || '';
const helmChartName = searchParams.get('chartName');
const helmChartRepoName = searchParams.get('chartRepoName');
const helmActionOrigin = searchParams.get('actionOrigin') as HelmActionOrigins;

const [chartData, setChartData] = React.useState<HelmChart>(null);
Expand Down Expand Up @@ -124,6 +126,7 @@ const HelmInstallUpgradePage: React.FunctionComponent<HelmInstallUpgradePageProp
releaseName: initialReleaseName || helmChartName || '',
chartURL: initialChartURL,
chartName,
chartRepoName: helmChartRepoName || '',
appVersion,
chartVersion,
chartReadme,
Expand Down
@@ -1,5 +1,5 @@
import { K8sResourceKind } from '@console/internal/module/k8s';
import { HelmRelease, HelmChartMetaData } from '../helm-types';
import { HelmRelease, HelmChartMetaData, HelmChartEntries } from '../helm-types';

/* eslint-disable @typescript-eslint/camelcase */
export const mockHelmReleases: HelmRelease[] = [
Expand Down Expand Up @@ -126,7 +126,7 @@ export const mockHelmReleases: HelmRelease[] = [
},
];

export const mockHelmChartData: HelmChartMetaData[] = [
export const mockIBMHelmChartData: HelmChartMetaData[] = [
{
appVersion: '3.12',
apiVersion: 'v1',
Expand All @@ -136,6 +136,7 @@ export const mockHelmChartData: HelmChartMetaData[] = [
'https://raw.githubusercontent.com/IBM/charts/master/repo/community/hazelcast-enterprise-1.0.3.tgz',
],
version: '1.0.3',
repoName: 'ibm-helm-repo',
},
{
apiVersion: 'v1',
Expand All @@ -145,6 +146,7 @@ export const mockHelmChartData: HelmChartMetaData[] = [
'https://raw.githubusercontent.com/IBM/charts/master/repo/community/hazelcast-enterprise-1.0.2.tgz',
],
version: '1.0.2',
repoName: 'ibm-helm-repo',
},
{
appVersion: '3.10.5',
Expand All @@ -155,9 +157,44 @@ export const mockHelmChartData: HelmChartMetaData[] = [
'https://raw.githubusercontent.com/IBM/charts/master/repo/community/hazelcast-enterprise-1.0.1.tgz',
],
version: '1.0.1',
repoName: 'ibm-helm-repo',
},
];

export const mockRedhatHelmChartData: HelmChartMetaData[] = [
{
apiVersion: 'v1',
description: 'abc',
name: 'hazelcast-enterprise',
urls: [
'https://raw.githubusercontent.com/redhat-helm-charts/master/repo/stable/hazelcast-enterprise-1.0.2.tgz',
],
version: '1.0.2',
repoName: 'redhat-helm-repo',
},
{
appVersion: '3.10.5',
apiVersion: 'v1',
description: 'efg',
name: 'hazelcast-enterprise',
urls: [
'https://raw.githubusercontent.com/redhat-helm-charts/master/repo/stable/hazelcast-enterprise-1.0.1.tgz',
],
version: '1.0.1',
repoName: 'redhat-helm-repo',
},
];

export const mockHelmChartData: HelmChartMetaData[] = [
...mockIBMHelmChartData,
...mockRedhatHelmChartData,
];

export const mockChartEntries: HelmChartEntries = {
'hazelcast-enterprise--ibm-helm-repo': mockIBMHelmChartData,
'hazelcast-enterprise--redhat-helm-repo': mockRedhatHelmChartData,
};

export const mockReleaseResources: {
[key: string]: { data: K8sResourceKind };
} = {
Expand Down
Expand Up @@ -7,13 +7,16 @@ import {
getChartVersions,
flattenReleaseResources,
getChartReadme,
getChartEntriesByName,
} from '../helm-utils';
import { HelmReleaseStatus } from '../helm-types';
import {
mockHelmReleases,
mockHelmChartData,
mockReleaseResources,
flattenedMockReleaseResources,
mockChartEntries,
mockRedhatHelmChartData,
} from './helm-release-mock-data';

describe('Helm Releases Utils', () => {
Expand Down Expand Up @@ -47,23 +50,56 @@ describe('Helm Releases Utils', () => {
expect(filteredReleases[0].name).toBe('ghost-test');
});

it('should return the helm chart url', () => {
it('should return the helm chart url from ibm repo', () => {
const chartVersion = '1.0.2';
const chartURL = getChartURL(mockHelmChartData, chartVersion);
const chartRepoName = 'ibm-helm-repo';
const chartURL = getChartURL(mockHelmChartData, chartVersion, chartRepoName);
expect(chartURL).toBe(
'https://raw.githubusercontent.com/IBM/charts/master/repo/community/hazelcast-enterprise-1.0.2.tgz',
);
});

it('should return the helm chart url from redhat repo', () => {
const chartVersion = '1.0.1';
const chartRepoName = 'redhat-helm-repo';
const chartURL = getChartURL(mockHelmChartData, chartVersion, chartRepoName);
expect(chartURL).toBe(
'https://raw.githubusercontent.com/redhat-helm-charts/master/repo/stable/hazelcast-enterprise-1.0.1.tgz',
);
});

it('should return the chart versions, concatenated with the App Version, available for the helm chart', () => {
const chartVersions = getChartVersions(mockHelmChartData);
expect(chartVersions).toEqual({
'1.0.1': '1.0.1 / App Version 3.10.5',
'1.0.2': '1.0.2',
'1.0.3': '1.0.3 / App Version 3.12',
'1.0.1--ibm-helm-repo': '1.0.1 / App Version 3.10.5 (Provided by Ibm Helm Repo)',
'1.0.2--ibm-helm-repo': '1.0.2 (Provided by Ibm Helm Repo)',
'1.0.3--ibm-helm-repo': '1.0.3 / App Version 3.12 (Provided by Ibm Helm Repo)',
'1.0.1--redhat-helm-repo': '1.0.1 / App Version 3.10.5 (Provided by Redhat Helm Repo)',
'1.0.2--redhat-helm-repo': '1.0.2 (Provided by Redhat Helm Repo)',
});
});

it('should return chart entries by name from specific repo if repo name provided', () => {
const chartEntries = getChartEntriesByName(
mockChartEntries,
'hazelcast-enterprise',
'redhat-helm-repo',
);
expect(chartEntries).toEqual(mockRedhatHelmChartData);
});

it('should return chart entries by name from all repos if repo name not provided', () => {
const chartEntries = getChartEntriesByName(mockChartEntries, 'hazelcast-enterprise');
expect(chartEntries).toEqual(mockHelmChartData);
});

it('should return empty array if wrong chart name or repo name provided', () => {
expect(
getChartEntriesByName(mockChartEntries, 'hazelcast-enterprise', 'stable-helm-repo'),
).toEqual([]);
expect(getChartEntriesByName(mockChartEntries, 'hazelcast-enterprise-prod')).toEqual([]);
});

it('should omit resources with no data and flatten them', () => {
expect(flattenReleaseResources(mockReleaseResources)).toEqual(flattenedMockReleaseResources);
});
Expand Down
Expand Up @@ -15,6 +15,7 @@ import {
getChartValuesYAML,
getChartReadme,
concatVersions,
getChartEntriesByName,
} from '../helm-utils';

export type HelmChartVersionDropdownProps = {
Expand All @@ -33,7 +34,7 @@ const HelmChartVersionDropdown: React.FunctionComponent<HelmChartVersionDropdown
}) => {
const {
setFieldValue,
values: { yamlData, formData, appVersion },
values: { chartRepoName, yamlData, formData, appVersion },
setFieldTouched,
} = useFormikContext<FormikValues>();
const [helmChartVersions, setHelmChartVersions] = React.useState({});
Expand Down Expand Up @@ -94,17 +95,20 @@ const HelmChartVersionDropdown: React.FunctionComponent<HelmChartVersionDropdown
if (ignore) return;
}
if (ignore) return;
setHelmChartEntries(json?.entries?.[chartName]);
setHelmChartVersions(getChartVersions(json?.entries?.[chartName]));
const chartEntries = getChartEntriesByName(json?.entries, chartName, chartRepoName);
setHelmChartEntries(chartEntries);
setHelmChartVersions(getChartVersions(chartEntries));
};
fetchChartVersions();
return () => {
ignore = true;
};
}, [chartName]);
}, [chartName, chartRepoName]);

const onChartVersionChange = (value: string) => {
const chartURL = getChartURL(helmChartEntries, value);
const [version, repoName] = value.split('--');

const chartURL = getChartURL(helmChartEntries, version, repoName);

setFieldValue('chartVersion', value);
setFieldValue('chartURL', chartURL);
Expand Down Expand Up @@ -148,6 +152,12 @@ const HelmChartVersionDropdown: React.FunctionComponent<HelmChartVersionDropdown
? 'Select the Chart Version.'
: 'Select the version to upgrade to.';

const title =
_.isEmpty(helmChartVersions) && !chartVersion
? 'No versions available'
: helmChartVersions[`${chartVersion}`] ||
concatVersions(chartVersion, appVersion, chartRepoName);

return (
<GridItem span={6}>
<DropdownField
Expand All @@ -156,11 +166,7 @@ const HelmChartVersionDropdown: React.FunctionComponent<HelmChartVersionDropdown
items={helmChartVersions}
helpText={helpText}
disabled={_.isEmpty(helmChartVersions) || _.keys(helmChartVersions).length === 1}
title={
_.isEmpty(helmChartVersions) && !chartVersion
? 'No versions available'
: helmChartVersions[chartVersion] || concatVersions(chartVersion, appVersion)
}
title={title}
onChange={handleChartVersionChange}
required
fullWidth
Expand Down
Expand Up @@ -41,6 +41,7 @@ export interface HelmChartMetaData {
type?: string;
urls: string[];
kubeVersion?: string;
repoName?: string;
}

export type HelmChartEntries = {
Expand Down
65 changes: 59 additions & 6 deletions frontend/packages/dev-console/src/components/helm/helm-utils.ts
Expand Up @@ -3,6 +3,8 @@ import * as _ from 'lodash';
import { safeDump } from 'js-yaml';
import { coFetchJSON } from '@console/internal/co-fetch';
import { K8sResourceKind } from '@console/internal/module/k8s';
import { RowFilter } from '@console/internal/components/filter-toolbar';
import { toTitleCase } from '@console/shared';
import {
HelmRelease,
HelmChart,
Expand All @@ -11,8 +13,8 @@ import {
HelmActionType,
HelmActionConfigType,
HelmActionOrigins,
HelmChartEntries,
} from './helm-types';
import { RowFilter } from '@console/internal/components/filter-toolbar';

export const HelmReleaseStatusLabels = {
[HelmReleaseStatus.Deployed]: 'Deployed',
Expand Down Expand Up @@ -77,20 +79,71 @@ export const fetchHelmReleases = (
return coFetchJSON(fetchString);
};

export const getChartURL = (helmChartData: HelmChartMetaData[], chartVersion: string): string => {
const chartData: HelmChartMetaData = _.find(helmChartData, (obj) => obj.version === chartVersion);
export const getChartURL = (
helmChartData: HelmChartMetaData[],
chartVersion: string,
chartRepoName: string,
): string => {
const chartData: HelmChartMetaData = _.find(
helmChartData,
(obj) => obj.version === chartVersion && obj.repoName === chartRepoName,
);
return chartData?.urls[0];
};

export const concatVersions = (chartVersion: string, appVersion: string): string => {
return appVersion ? `${chartVersion} / App Version ${appVersion}` : chartVersion;
export const getChartEntriesByName = (
chartEntries: HelmChartEntries,
chartName: string,
chartRepoName?: string,
): HelmChartMetaData[] => {
if (chartName && chartRepoName) {
return (
chartEntries?.[`${chartName}--${chartRepoName}`]?.map((e) => ({
...e,
repoName: chartRepoName,
})) ?? []
);
}
const entries = _.reduce(
chartEntries,
(acc, charts, key) => {
const repoName = key.split('--').pop();
charts.forEach((chart: HelmChartMetaData) => {
if (chart.name === chartName) {
acc.push({ ...chart, repoName });
}
});
return acc;
},
[],
);
return entries;
};

export const concatVersions = (
chartVersion: string,
appVersion: string,
chartRepoName?: string,
): string => {
let title = chartVersion.split('--')[0];
if (appVersion) {
title += ` / App Version ${appVersion}`;
}
if (chartRepoName) {
title += ` (Provided by ${toTitleCase(chartRepoName)})`;
}
return title;
};

export const getChartVersions = (chartEntries: HelmChartMetaData[]) => {
const chartVersions = _.reduce(
chartEntries,
(obj, chart) => {
obj[chart.version] = concatVersions(chart.version, chart.appVersion);
obj[`${chart.version}--${chart.repoName}`] = concatVersions(
chart.version,
chart.appVersion,
chart.repoName,
);
return obj;
},
{},
Expand Down