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

feat: enhance Helm Releases #3341

Merged
merged 8 commits into from
Sep 12, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion public/i18n/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -629,16 +629,18 @@ get-help:
helm-releases:
description: <0>Helm Release</0> tracks the installed charts. For each installed chart, a new release is created.
headers:
app-version: App Version
chart: Chart
chart-description: Chart Description
chart-files: Chart Files
chart-information: Chart Information
chart-name: Chart Name
chart-version: Chart Version
first-deployed: First Deployed
last-deployed: Last Deployed
other-release-versions: Other Release Versions
release: 'Release '
release-data: Release Data
release-manifest: Release Manifest
release-version: Version v{{version}}
revision: Revision
messages:
Expand Down
2 changes: 1 addition & 1 deletion src/components/HelmReleases/ChartContent.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { ReadonlyEditorPanel } from 'shared/components/ReadonlyEditorPanel';
import { ComboboxInput } from 'shared/ResourceForm/inputs';
import { useTranslation } from 'react-i18next';
Expand Down
13 changes: 5 additions & 8 deletions src/components/HelmReleases/HelmReleaseData.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import React from 'react';
import { decodeHelmRelease } from 'components/HelmReleases/decodeHelmRelease';
import { ReadonlyEditorPanel } from 'shared/components/ReadonlyEditorPanel';
import { ReleaseDataPanel } from './ReleaseDataPanel';
import { ChartContent } from './ChartContent';
import { useTranslation } from 'react-i18next';
import jsyaml from 'js-yaml';
import { UI5Panel } from 'shared/components/UI5Panel/UI5Panel';

export function HelmReleaseData({ encodedRelease }) {
export function HelmReleaseData({ releaseSecret, release }) {
const { t } = useTranslation();

const release = decodeHelmRelease(encodedRelease);

if (!release) {
return (
<UI5Panel
Expand All @@ -23,12 +20,12 @@ export function HelmReleaseData({ encodedRelease }) {

return (
<React.Fragment key="helm-release-data">
<ReleaseDataPanel release={release} />
<ReleaseDataPanel release={release} secret={releaseSecret} />
<ChartContent chart={release.chart} />
<ReadonlyEditorPanel
title={t('helm-releases.headers.release-data')}
value={jsyaml.dump(release.config)}
title={t('helm-releases.headers.release-manifest')}
value={jsyaml.dump(release.manifest)}
/>
<ChartContent chart={release.chart} />
</React.Fragment>
);
}
101 changes: 52 additions & 49 deletions src/components/HelmReleases/HelmReleasesDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@ import { ResourceNotFound } from 'shared/components/ResourceNotFound/ResourceNot
import { useGetList } from 'shared/hooks/BackendAPI/useGet';
import { prettifyNameSingular } from 'shared/utils/helpers';
import { Spinner } from 'shared/components/Spinner/Spinner';
import { DynamicPageComponent } from 'shared/components/DynamicPageComponent/DynamicPageComponent';
import { HelmReleaseData } from './HelmReleaseData';
import { HelmReleaseStatus } from './HelmReleaseStatus';
import { OtherReleaseVersions } from './OtherReleaseVersions';
import { findRecentRelease } from './findRecentRelease';
import { ResourceCreate } from 'shared/components/ResourceCreate/ResourceCreate';
import { useUrl } from 'hooks/useUrl';
import YamlUploadDialog from 'resources/Namespaces/YamlUpload/YamlUploadDialog';
import { ResourceDescription } from 'components/HelmReleases';
import HelmReleasesYaml from './HelmReleasesYaml';
import { ErrorBoundary } from 'shared/components/ErrorBoundary/ErrorBoundary';
import { showYamlTab } from './index';
import { Link } from 'shared/components/Link/Link';
import { createPortal } from 'react-dom';
import { ResourceDetails } from 'shared/components/ResourceDetails/ResourceDetails';
import { decodeHelmRelease } from './decodeHelmRelease';
import { EventsList } from 'shared/components/EventsList';
import { filterByResource } from 'hooks/useMessageList';

function HelmReleasesDetails({ releaseName, namespace }) {
const { t } = useTranslation();
const { namespaceUrl } = useUrl();

const { data, loading } = useGetList(s => s.type === 'helm.sh/release.v1')(
const resourceUrl =
namespace === '-all-'
? `/api/v1/secrets?labelSelector=name==${releaseName}`
: `/api/v1/namespaces/${namespace}/secrets?labelSelector=name==${releaseName}`,
: `/api/v1/namespaces/${namespace}/secrets?labelSelector=name==${releaseName}`;

const { data, loading } = useGetList(s => s.type === 'helm.sh/release.v1')(
resourceUrl,
);

if (loading) return <Spinner />;
Expand All @@ -39,14 +39,49 @@ function HelmReleasesDetails({ releaseName, namespace }) {
);
}

const release = decodeHelmRelease(releaseSecret.data.release);

const customComponents = [
() => <HelmReleaseData releaseSecret={releaseSecret} release={release} />,
() => <OtherReleaseVersions releaseSecret={releaseSecret} secrets={data} />,
() => (
<EventsList
key="events"
namespace={namespace}
filter={filterByResource('Secret', releaseSecret.metadata.name)}
hideInvolvedObjects={true}
/>
),
];

const customStatusColumns = [
{
header: t('helm-releases.headers.revision'),
value: () => releaseSecret.metadata.labels.version,
},
{
header: t('common.headers.description'),
value: () => release.info.description,
},
];

const statusBadge = () => (
<HelmReleaseStatus status={releaseSecret.metadata.labels.status} />
);

return (
<>
<DynamicPageComponent
title={releaseName}
<ResourceDetails
description={ResourceDescription}
showYamlTab={showYamlTab}
layoutNumber="MidColumn"
inlineEditForm={() => (
resourceType="HelmReleases"
namespace={namespace}
resourceName={releaseName}
customTitle={releaseName}
resourceUrl={resourceUrl}
resource={releaseSecret}
customStatusColumns={customStatusColumns}
statusBadge={statusBadge}
createResourceForm={() => (
<ResourceCreate
title={'HelmRelease'}
isEdit={true}
Expand All @@ -63,41 +98,9 @@ function HelmReleasesDetails({ releaseName, namespace }) {
)}
/>
)}
content={
<>
<HelmReleaseData encodedRelease={releaseSecret.data.release} />
<OtherReleaseVersions
releaseSecret={releaseSecret}
secrets={data}
/>
</>
}
>
{releaseSecret && (
<>
<DynamicPageComponent.Column title={t('secrets.name_singular')}>
<Link
url={namespaceUrl(`secrets/${releaseSecret.metadata.name}`, {
namespace: releaseSecret.metadata.namespace,
})}
>
{releaseSecret.metadata.name}
</Link>
</DynamicPageComponent.Column>
<DynamicPageComponent.Column
title={t('helm-releases.headers.revision')}
>
{releaseSecret.metadata.labels.version}
</DynamicPageComponent.Column>
<DynamicPageComponent.Column title={t('common.headers.status')}>
<HelmReleaseStatus
status={releaseSecret.metadata.labels.status}
/>
</DynamicPageComponent.Column>
</>
)}
{createPortal(<YamlUploadDialog />, document.body)}
</DynamicPageComponent>
customComponents={customComponents}
disableDelete
/>
</>
);
}
Expand Down
134 changes: 61 additions & 73 deletions src/components/HelmReleases/HelmReleasesList.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { useRecoilValue } from 'recoil';

import { useTranslation } from 'react-i18next';
import { Text } from '@ui5/webcomponents-react';
import { groupBy } from 'lodash';

import { useGetList } from 'shared/hooks/BackendAPI/useGet';
import { DynamicPageComponent } from 'shared/components/DynamicPageComponent/DynamicPageComponent';
import { GenericList } from 'shared/components/GenericList/GenericList';
import { decodeHelmRelease } from './decodeHelmRelease';
import { findRecentRelease } from './findRecentRelease';
import { HelmReleaseStatus } from './HelmReleaseStatus';
import { activeNamespaceIdState } from 'state/activeNamespaceIdAtom';
import { useUrl } from 'hooks/useUrl';
import YamlUploadDialog from 'resources/Namespaces/YamlUpload/YamlUploadDialog';
import { ResourceDescription } from 'components/HelmReleases';
import { createPortal } from 'react-dom';
import { ResourceDescription, docsURL } from 'components/HelmReleases';
import { ResourcesList } from 'shared/components/ResourcesList/ResourcesList';
import { HelmReleaseStatus } from './HelmReleaseStatus';

function HelmReleasesList() {
const { t } = useTranslation();
Expand All @@ -26,41 +20,20 @@ function HelmReleasesList() {
});
};

const { data, loading, error } = useGetList(
s => s.type === 'helm.sh/release.v1',
)(
const dataUrl =
namespace === '-all-'
? `/api/v1/secrets`
: `/api/v1/namespaces/${namespace}/secrets`,
);
: `/api/v1/namespaces/${namespace}/secrets`;

const headerRenderer = () => [
t('common.headers.name'),
namespace === '-all-' ? t('common.headers.namespace') : null,
t('common.headers.labels'),
t('helm-releases.headers.chart'),
t('helm-releases.headers.revision'),
t('helm-releases.headers.chart-version'),
t('common.headers.status'),
];

const rowRenderer = entry => [
<>
<Text style={{ fontWeight: 'bold', color: 'var(--sapLinkColor)' }}>
{entry.releaseName}
</Text>
</>,
namespace === '-all-' ? entry.namespace : null,
entry.recentRelease?.chart.metadata.name || t('common.statuses.unknown'),
entry.revision,
entry.recentRelease?.chart.metadata.version || t('common.statuses.unknown'),
<HelmReleaseStatus status={entry.status} />,
];
const { data, loading, error } = useGetList(
s => s.type === 'helm.sh/release.v1',
)(dataUrl);

const entries = Object.entries(
groupBy(data || [], r => r.metadata.labels.name),
).map(([releaseName, releases]) => {
const recentRelease = findRecentRelease(releases);
recentRelease.metadata.name = releaseName;
return {
name: releaseName,
releaseName,
Expand All @@ -69,50 +42,65 @@ function HelmReleasesList() {
revision: releases.length,
status: recentRelease?.metadata.labels.status || 'unknown',
namespace: recentRelease?.metadata.namespace,
...recentRelease,
};
});

const customColumns = [
{
header: t('helm-releases.headers.chart'),
value: entry =>
entry.recentRelease?.chart.metadata.name ||
t('common.statuses.unknown'),
},
{
header: t('helm-releases.headers.revision'),
value: entry => entry.revision,
},
{
header: t('helm-releases.headers.chart-version'),
value: entry =>
entry.recentRelease?.chart.metadata.version ||
t('common.statuses.unknown'),
},
{
header: t('common.headers.status'),
value: entry => (
<HelmReleaseStatus status={entry.metadata.labels.status} />
),
},
];

return (
<>
<DynamicPageComponent
title={t('helm-releases.title')}
<ResourcesList
resources={entries}
customColumns={customColumns}
serverDataLoading={loading}
serverDataError={error}
allowSlashShortcut
hasDetailsView
enableColumnLayout
customUrl={resourceUrl}
resourceUrl={dataUrl}
resourceType="HelmReleases"
sortBy={{
name: (a, b) => a.releaseName.localeCompare(b.releaseName),
}}
searchSettings={{
textSearchProperties: [
'recentRelease.chart.metadata.name',
'releaseName',
],
}}
emptyListProps={{
subtitleText: ResourceDescription,
url: docsURL,
showButton: false,
}}
readOnly
description={ResourceDescription}
layoutNumber={'StartColumn'}
content={
<GenericList
entries={entries}
headerRenderer={headerRenderer}
rowRenderer={rowRenderer}
serverDataLoading={loading}
serverDataError={error}
allowSlashShortcut
hasDetailsView
displayArrow
enableColumnLayout={true}
customUrl={resourceUrl}
resourceType="HelmReleases"
sortBy={{
name: (a, b) => a.releaseName.localeCompare(b.releaseName),
}}
searchSettings={{
textSearchProperties: [
'recentRelease.chart.metadata.name',
'releaseName',
],
}}
emptyListProps={{
titleText: `${t('common.labels.no')} ${t(
'helm-releases.title',
).toLocaleLowerCase()}`,
subtitleText: t('helm-releases.description'),
url:
'https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces',
buttonText: t('common.buttons.connect'),
}}
/>
}
/>
{createPortal(<YamlUploadDialog />, document.body)}
</>
);
}
Expand Down
Loading
Loading