mirror of
https://github.com/portainer/portainer.git
synced 2025-08-04 21:35:23 +02:00
feat(helm): show manifest previews/changes when installing and upgrading a helm chart [r8s-405] (#898)
This commit is contained in:
parent
a4cff13531
commit
60bc04bc33
41 changed files with 763 additions and 157 deletions
|
@ -9,7 +9,7 @@ import { buildConfirmButton } from '@@/modals/utils';
|
|||
import { confirm } from '@@/modals/confirm';
|
||||
import { ModalType } from '@@/modals';
|
||||
|
||||
import { useHelmRollbackMutation } from '../queries/useHelmRollbackMutation';
|
||||
import { useHelmRollbackMutation } from '../../helmReleaseQueries/useHelmRollbackMutation';
|
||||
|
||||
type Props = {
|
||||
latestRevision: number;
|
||||
|
|
|
@ -5,7 +5,7 @@ import { notifySuccess } from '@/portainer/services/notifications';
|
|||
|
||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
||||
|
||||
import { useUninstallHelmAppMutation } from '../queries/useUninstallHelmAppMutation';
|
||||
import { useUninstallHelmAppMutation } from '../../helmReleaseQueries/useUninstallHelmAppMutation';
|
||||
|
||||
export function UninstallButton({
|
||||
environmentId,
|
||||
|
|
|
@ -9,7 +9,7 @@ import { withUserProvider } from '@/react/test-utils/withUserProvider';
|
|||
import {
|
||||
useHelmRepoVersions,
|
||||
ChartVersion,
|
||||
} from '../../queries/useHelmRepoVersions';
|
||||
} from '../../helmChartSourceQueries/useHelmRepoVersions';
|
||||
import { HelmRelease } from '../../types';
|
||||
|
||||
import { openUpgradeHelmModal } from './UpgradeHelmModal';
|
||||
|
@ -25,32 +25,56 @@ vi.mock('@/portainer/services/notifications', () => ({
|
|||
notifySuccess: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../queries/useHelmRepositories', () => ({
|
||||
useUserHelmRepositories: vi.fn(() => ({
|
||||
data: ['repo1', 'repo2'],
|
||||
isInitialLoading: false,
|
||||
isError: false,
|
||||
})),
|
||||
}));
|
||||
vi.mock('../../helmChartSourceQueries/useHelmRepositories', async () => {
|
||||
const actual = await vi.importActual(
|
||||
'../../helmChartSourceQueries/useHelmRepositories'
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
useUserHelmRepositories: vi.fn(() => ({
|
||||
data: ['repo1', 'repo2'],
|
||||
isInitialLoading: false,
|
||||
isError: false,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../../queries/useHelmRepoVersions', () => ({
|
||||
useHelmRepoVersions: vi.fn(),
|
||||
}));
|
||||
vi.mock('../../helmChartSourceQueries/useHelmRepoVersions', async () => {
|
||||
const actual = await vi.importActual(
|
||||
'../../helmChartSourceQueries/useHelmRepoVersions'
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
useHelmRepoVersions: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
// Mock the useHelmRelease hook
|
||||
vi.mock('../queries/useHelmRelease', () => ({
|
||||
useHelmRelease: vi.fn(() => ({
|
||||
data: '1.0.0',
|
||||
})),
|
||||
}));
|
||||
vi.mock('../../helmReleaseQueries/useHelmRelease', async () => {
|
||||
const actual = await vi.importActual(
|
||||
'../../helmReleaseQueries/useHelmRelease'
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
useHelmRelease: vi.fn(() => ({
|
||||
data: '1.0.0',
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
// Mock the useUpdateHelmReleaseMutation hook
|
||||
vi.mock('../../queries/useUpdateHelmReleaseMutation', () => ({
|
||||
useUpdateHelmReleaseMutation: vi.fn(() => ({
|
||||
mutate: vi.fn(),
|
||||
isLoading: false,
|
||||
})),
|
||||
}));
|
||||
vi.mock('../../helmReleaseQueries/useUpdateHelmReleaseMutation', async () => {
|
||||
const actual = await vi.importActual(
|
||||
'../../helmReleaseQueries/useUpdateHelmReleaseMutation'
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
useUpdateHelmReleaseMutation: vi.fn(() => ({
|
||||
mutate: vi.fn(),
|
||||
isLoading: false,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
function renderButton(props = {}) {
|
||||
const defaultProps = {
|
||||
|
@ -157,12 +181,14 @@ describe('UpgradeButton', () => {
|
|||
metadata: {
|
||||
name: 'test-chart',
|
||||
version: '1.0.0',
|
||||
appVersion: '1.0.0',
|
||||
},
|
||||
},
|
||||
values: {
|
||||
userSuppliedValues: '{}',
|
||||
},
|
||||
manifest: '',
|
||||
namespace: 'default',
|
||||
} as HelmRelease;
|
||||
|
||||
vi.mocked(useHelmRepoVersions).mockReturnValue({
|
||||
|
@ -193,7 +219,9 @@ describe('UpgradeButton', () => {
|
|||
expect.arrayContaining([
|
||||
{ Version: '1.0.0', Repo: 'stable' },
|
||||
{ Version: '1.1.0', Repo: 'stable' },
|
||||
])
|
||||
]),
|
||||
'', // releaseManifest
|
||||
1 // environmentId
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,10 +12,13 @@ import { Tooltip } from '@@/Tip/Tooltip';
|
|||
import { Link } from '@@/Link';
|
||||
|
||||
import { HelmRelease, UpdateHelmReleasePayload } from '../../types';
|
||||
import { useUpdateHelmReleaseMutation } from '../../queries/useUpdateHelmReleaseMutation';
|
||||
import { useHelmRepoVersions } from '../../queries/useHelmRepoVersions';
|
||||
import { useHelmRelease } from '../queries/useHelmRelease';
|
||||
import { useUserHelmRepositories } from '../../queries/useHelmRepositories';
|
||||
import { useUpdateHelmReleaseMutation } from '../../helmReleaseQueries/useUpdateHelmReleaseMutation';
|
||||
import { useHelmRepoVersions } from '../../helmChartSourceQueries/useHelmRepoVersions';
|
||||
import { useHelmRelease } from '../../helmReleaseQueries/useHelmRelease';
|
||||
import {
|
||||
flattenHelmRegistries,
|
||||
useUserHelmRepositories,
|
||||
} from '../../helmChartSourceQueries/useHelmRepositories';
|
||||
|
||||
import { openUpgradeHelmModal } from './UpgradeHelmModal';
|
||||
|
||||
|
@ -36,7 +39,9 @@ export function UpgradeButton({
|
|||
const [useCache, setUseCache] = useState(true);
|
||||
const updateHelmReleaseMutation = useUpdateHelmReleaseMutation(environmentId);
|
||||
|
||||
const userRepositoriesQuery = useUserHelmRepositories();
|
||||
const userRepositoriesQuery = useUserHelmRepositories({
|
||||
select: flattenHelmRegistries,
|
||||
});
|
||||
const helmRepoVersionsQuery = useHelmRepoVersions(
|
||||
release?.chart.metadata?.name || '',
|
||||
60 * 60 * 1000, // 1 hour
|
||||
|
@ -164,7 +169,9 @@ export function UpgradeButton({
|
|||
async function handleUpgrade() {
|
||||
const submittedUpgradeValues = await openUpgradeHelmModal(
|
||||
editableHelmRelease,
|
||||
filteredVersions
|
||||
filteredVersions,
|
||||
release?.manifest || '',
|
||||
environmentId
|
||||
);
|
||||
|
||||
if (submittedUpgradeValues) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { ArrowUp } from 'lucide-react';
|
||||
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { ChartVersion } from '@/react/kubernetes/helm/queries/useHelmRepoVersions';
|
||||
import { ChartVersion } from '@/react/kubernetes/helm/helmChartSourceQueries/useHelmRepoVersions';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { Modal, OnSubmit, openModal } from '@@/modals';
|
||||
import { Button } from '@@/buttons';
|
||||
|
@ -15,26 +16,32 @@ import { Option, PortainerSelect } from '@@/form-components/PortainerSelect';
|
|||
|
||||
import { UpdateHelmReleasePayload } from '../../types';
|
||||
import { HelmValuesInput } from '../../components/HelmValuesInput';
|
||||
import { useHelmChartValues } from '../../queries/useHelmChartValues';
|
||||
import { useHelmChartValues } from '../../helmChartSourceQueries/useHelmChartValues';
|
||||
import { ManifestPreviewFormSection } from '../../components/ManifestPreviewFormSection';
|
||||
|
||||
interface Props {
|
||||
onSubmit: OnSubmit<UpdateHelmReleasePayload>;
|
||||
payload: UpdateHelmReleasePayload;
|
||||
helmReleaseInitialValues: UpdateHelmReleasePayload;
|
||||
releaseManifest: string;
|
||||
versions: ChartVersion[];
|
||||
chartName: string;
|
||||
environmentId: EnvironmentId;
|
||||
}
|
||||
|
||||
export function UpgradeHelmModal({
|
||||
payload,
|
||||
helmReleaseInitialValues,
|
||||
releaseManifest,
|
||||
versions,
|
||||
onSubmit,
|
||||
chartName,
|
||||
environmentId,
|
||||
}: Props) {
|
||||
const versionOptions: Option<ChartVersion>[] = versions.map((version) => {
|
||||
const repo = payload.repo === version.Repo ? version.Repo : '';
|
||||
const repo =
|
||||
helmReleaseInitialValues.repo === version.Repo ? version.Repo : '';
|
||||
const isCurrentVersion =
|
||||
version.AppVersion === payload.appVersion &&
|
||||
version.Version === payload.version;
|
||||
version.AppVersion === helmReleaseInitialValues.appVersion &&
|
||||
version.Version === helmReleaseInitialValues.version;
|
||||
|
||||
const label = `${repo}@${version.Version}${
|
||||
isCurrentVersion ? ' (current)' : ''
|
||||
|
@ -50,13 +57,16 @@ export function UpgradeHelmModal({
|
|||
const defaultVersion =
|
||||
versionOptions.find(
|
||||
(v) =>
|
||||
v.value.AppVersion === payload.appVersion &&
|
||||
v.value.Version === payload.version &&
|
||||
v.value.Repo === payload.repo
|
||||
v.value.AppVersion === helmReleaseInitialValues.appVersion &&
|
||||
v.value.Version === helmReleaseInitialValues.version &&
|
||||
v.value.Repo === helmReleaseInitialValues.repo
|
||||
)?.value || versionOptions[0]?.value;
|
||||
const [version, setVersion] = useState<ChartVersion>(defaultVersion);
|
||||
const [userValues, setUserValues] = useState<string>(payload.values || '');
|
||||
const [userValues, setUserValues] = useState<string>(
|
||||
helmReleaseInitialValues.values || ''
|
||||
);
|
||||
const [atomic, setAtomic] = useState<boolean>(true);
|
||||
const [previewIsValid, setPreviewIsValid] = useState<boolean>(false);
|
||||
|
||||
const chartValuesRefQuery = useHelmChartValues({
|
||||
chart: chartName,
|
||||
|
@ -64,6 +74,19 @@ export function UpgradeHelmModal({
|
|||
version: version.Version,
|
||||
});
|
||||
|
||||
const submitPayload = useMemo(
|
||||
() => ({
|
||||
name: helmReleaseInitialValues.name,
|
||||
values: userValues,
|
||||
namespace: helmReleaseInitialValues.namespace,
|
||||
chart: helmReleaseInitialValues.chart,
|
||||
repo: version.Repo,
|
||||
version: version.Version,
|
||||
atomic,
|
||||
}),
|
||||
[helmReleaseInitialValues, userValues, version, atomic]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onDismiss={() => onSubmit()}
|
||||
|
@ -84,7 +107,7 @@ export function UpgradeHelmModal({
|
|||
>
|
||||
<Input
|
||||
id="release-name-input"
|
||||
value={payload.name}
|
||||
value={helmReleaseInitialValues.name}
|
||||
readOnly
|
||||
disabled
|
||||
data-cy="helm-release-name-input"
|
||||
|
@ -97,7 +120,7 @@ export function UpgradeHelmModal({
|
|||
>
|
||||
<Input
|
||||
id="namespace-input"
|
||||
value={payload.namespace}
|
||||
value={helmReleaseInitialValues.namespace}
|
||||
readOnly
|
||||
disabled
|
||||
data-cy="helm-namespace-input"
|
||||
|
@ -134,6 +157,15 @@ export function UpgradeHelmModal({
|
|||
valuesRef={chartValuesRefQuery.data?.values ?? ''}
|
||||
isValuesRefLoading={chartValuesRefQuery.isInitialLoading}
|
||||
/>
|
||||
<div className="mb-10">
|
||||
<ManifestPreviewFormSection
|
||||
payload={submitPayload}
|
||||
onChangePreviewValidation={setPreviewIsValid}
|
||||
title="Manifest changes"
|
||||
currentManifest={releaseManifest}
|
||||
environmentId={environmentId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</div>
|
||||
|
@ -149,17 +181,8 @@ export function UpgradeHelmModal({
|
|||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() =>
|
||||
onSubmit({
|
||||
name: payload.name,
|
||||
values: userValues,
|
||||
namespace: payload.namespace,
|
||||
chart: payload.chart,
|
||||
repo: version.Repo,
|
||||
version: version.Version,
|
||||
atomic,
|
||||
})
|
||||
}
|
||||
onClick={() => onSubmit(submitPayload)}
|
||||
disabled={!previewIsValid}
|
||||
color="primary"
|
||||
key="update-button"
|
||||
size="medium"
|
||||
|
@ -174,12 +197,16 @@ export function UpgradeHelmModal({
|
|||
}
|
||||
|
||||
export async function openUpgradeHelmModal(
|
||||
payload: UpdateHelmReleasePayload,
|
||||
versions: ChartVersion[]
|
||||
helmReleaseInitialValues: UpdateHelmReleasePayload,
|
||||
versions: ChartVersion[],
|
||||
releaseManifest: string,
|
||||
environmentId: EnvironmentId
|
||||
) {
|
||||
return openModal(withReactQuery(withCurrentUser(UpgradeHelmModal)), {
|
||||
payload,
|
||||
helmReleaseInitialValues,
|
||||
versions,
|
||||
chartName: payload.chart,
|
||||
chartName: helmReleaseInitialValues.chart,
|
||||
releaseManifest,
|
||||
environmentId,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@ import { Alert } from '@@/Alert';
|
|||
|
||||
import { HelmRelease } from '../types';
|
||||
import { useIsSystemNamespace } from '../../namespaces/queries/useIsSystemNamespace';
|
||||
import { useHelmRelease } from '../helmReleaseQueries/useHelmRelease';
|
||||
import { useHelmHistory } from '../helmReleaseQueries/useHelmHistory';
|
||||
|
||||
import { HelmSummary } from './HelmSummary';
|
||||
import { ReleaseTabs } from './ReleaseDetails/ReleaseTabs';
|
||||
import { useHelmRelease } from './queries/useHelmRelease';
|
||||
import { ChartActions } from './ChartActions/ChartActions';
|
||||
import { HelmRevisionList } from './HelmRevisionList';
|
||||
import { HelmRevisionListSheet } from './HelmRevisionListSheet';
|
||||
import { useHelmHistory } from './queries/useHelmHistory';
|
||||
|
||||
export function HelmApplicationView() {
|
||||
const environmentId = useEnvironmentId();
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Badge } from '@@/Badge';
|
|||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { HelmRelease } from '../../types';
|
||||
import { useHelmHistory } from '../queries/useHelmHistory';
|
||||
import { useHelmHistory } from '../../helmReleaseQueries/useHelmHistory';
|
||||
|
||||
import { ManifestDetails } from './ManifestDetails';
|
||||
import { NotesDetails } from './NotesDetails';
|
||||
|
|
|
@ -3,8 +3,7 @@ import { describe, it, expect, vi, afterEach } from 'vitest';
|
|||
|
||||
import { withTestRouter } from '@/react/test-utils/withRouter';
|
||||
import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';
|
||||
|
||||
import { GenericResource } from '../../../types';
|
||||
import { GenericResource } from '@/react/kubernetes/helm/types';
|
||||
|
||||
import { ResourcesTable } from './ResourcesTable';
|
||||
|
||||
|
@ -22,7 +21,7 @@ vi.mock('@/react/hooks/useEnvironmentId', () => ({
|
|||
useEnvironmentId: () => mockUseEnvironmentId(),
|
||||
}));
|
||||
|
||||
vi.mock('../../queries/useHelmRelease', () => ({
|
||||
vi.mock('@/react/kubernetes/helm/helmReleaseQueries/useHelmRelease', () => ({
|
||||
useHelmRelease: () => mockUseHelmRelease(),
|
||||
}));
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
import { useHelmRelease } from '@/react/kubernetes/helm/helmReleaseQueries/useHelmRelease';
|
||||
|
||||
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||
import {
|
||||
|
@ -13,8 +14,6 @@ import { Widget } from '@@/Widget';
|
|||
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
|
||||
import { useHelmRelease } from '../../queries/useHelmRelease';
|
||||
|
||||
import { columns } from './columns';
|
||||
import { useResourceRows } from './useResourceRows';
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useMemo } from 'react';
|
||||
|
||||
import { StatusBadgeType } from '@@/StatusBadge';
|
||||
import { GenericResource } from '@/react/kubernetes/helm/types';
|
||||
|
||||
import { GenericResource } from '../../../types';
|
||||
import { StatusBadgeType } from '@@/StatusBadge';
|
||||
|
||||
import { ResourceLink, ResourceRow } from './types';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { HelmRelease } from '../../types';
|
||||
import { useHelmRelease } from '../queries/useHelmRelease';
|
||||
import { useHelmRelease } from '../../helmReleaseQueries/useHelmRelease';
|
||||
|
||||
import { DiffViewMode } from './DiffControl';
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import { HelmRelease } from '../../types';
|
||||
|
||||
export function useHelmHistory(
|
||||
environmentId: EnvironmentId,
|
||||
name: string,
|
||||
namespace: string
|
||||
) {
|
||||
return useQuery(
|
||||
[environmentId, 'helm', 'releases', namespace, name, 'history'],
|
||||
() => getHelmHistory(environmentId, name, namespace),
|
||||
{
|
||||
enabled: !!environmentId && !!name && !!namespace,
|
||||
...withGlobalError('Unable to retrieve helm application history'),
|
||||
retry: 3,
|
||||
// occasionally the application shows before the release is created, take some more time to refetch
|
||||
retryDelay: 2000,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function getHelmHistory(
|
||||
environmentId: EnvironmentId,
|
||||
name: string,
|
||||
namespace: string
|
||||
) {
|
||||
try {
|
||||
const response = await axios.get<HelmRelease[]>(
|
||||
`endpoints/${environmentId}/kubernetes/helm/${name}/history`,
|
||||
{
|
||||
params: { namespace },
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw parseAxiosError(error, 'Unable to retrieve helm application history');
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import { HelmRelease } from '../../types';
|
||||
|
||||
type Options<T> = {
|
||||
select?: (data: HelmRelease) => T;
|
||||
showResources?: boolean;
|
||||
refetchInterval?: number;
|
||||
enabled?: boolean;
|
||||
staleTime?: number;
|
||||
/** when the revision is undefined, the latest revision is fetched */
|
||||
revision?: number;
|
||||
};
|
||||
/**
|
||||
* React hook to fetch a specific Helm release
|
||||
*/
|
||||
export function useHelmRelease<T = HelmRelease>(
|
||||
environmentId: EnvironmentId,
|
||||
name: string,
|
||||
namespace: string,
|
||||
options: Options<T> = {}
|
||||
) {
|
||||
const { select, showResources, refetchInterval, revision, staleTime } =
|
||||
options;
|
||||
return useQuery(
|
||||
[
|
||||
environmentId,
|
||||
'helm',
|
||||
'releases',
|
||||
namespace,
|
||||
name,
|
||||
revision,
|
||||
showResources,
|
||||
],
|
||||
() =>
|
||||
getHelmRelease(environmentId, name, {
|
||||
namespace,
|
||||
showResources,
|
||||
revision,
|
||||
}),
|
||||
{
|
||||
enabled: !!environmentId && !!name && !!namespace && options.enabled,
|
||||
...withGlobalError('Unable to retrieve helm application details'),
|
||||
retry: 3,
|
||||
// occasionally the application shows before the release is created, take some more time to refetch
|
||||
retryDelay: 2000,
|
||||
select,
|
||||
refetchInterval,
|
||||
staleTime,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
type Params = {
|
||||
namespace: string;
|
||||
showResources?: boolean;
|
||||
revision?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a specific Helm release
|
||||
*/
|
||||
async function getHelmRelease(
|
||||
environmentId: EnvironmentId,
|
||||
name: string,
|
||||
params: Params
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<HelmRelease>(
|
||||
`endpoints/${environmentId}/kubernetes/helm/${name}`,
|
||||
{
|
||||
params,
|
||||
}
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to retrieve helm application details');
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import {
|
||||
queryClient,
|
||||
withInvalidate,
|
||||
withGlobalError,
|
||||
} from '@/react-tools/react-query';
|
||||
import axios from '@/portainer/services/axios';
|
||||
import { queryKeys } from '@/react/kubernetes/applications/queries/query-keys';
|
||||
|
||||
/**
|
||||
* Parameters for helm rollback operation
|
||||
*
|
||||
* @see https://helm.sh/docs/helm/helm_rollback/
|
||||
*/
|
||||
interface RollbackQueryParams {
|
||||
/** Optional namespace for the release (defaults to "default" if not specified) */
|
||||
namespace?: string;
|
||||
/** Revision to rollback to (if omitted or set to 0, rolls back to the previous release) */
|
||||
revision?: number;
|
||||
/** If set, waits until resources are in a ready state before marking the release as successful (default: false) */
|
||||
wait?: boolean;
|
||||
/** If set and --wait enabled, waits until all Jobs have been completed before marking the release as successful (default: false) */
|
||||
waitForJobs?: boolean;
|
||||
/** Performs pods restart for the resources if applicable (default: true) */
|
||||
recreate?: boolean;
|
||||
/** Force resource update through delete/recreate if needed (default: false) */
|
||||
force?: boolean;
|
||||
/** Time to wait for any individual Kubernetes operation in seconds (default: 300) */
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
interface RollbackPayload {
|
||||
releaseName: string;
|
||||
params: RollbackQueryParams;
|
||||
}
|
||||
|
||||
async function rollbackRelease({
|
||||
releaseName,
|
||||
params,
|
||||
environmentId,
|
||||
}: RollbackPayload & { environmentId: EnvironmentId }) {
|
||||
return axios.post<Record<string, unknown>>(
|
||||
`/endpoints/${environmentId}/kubernetes/helm/${releaseName}/rollback`,
|
||||
null,
|
||||
{ params }
|
||||
);
|
||||
}
|
||||
|
||||
export function useHelmRollbackMutation(environmentId: EnvironmentId) {
|
||||
return useMutation({
|
||||
mutationFn: ({ releaseName, params }: RollbackPayload) =>
|
||||
rollbackRelease({ releaseName, params, environmentId }),
|
||||
...withGlobalError('Unable to rollback Helm release'),
|
||||
...withInvalidate(queryClient, [
|
||||
[environmentId, 'helm', 'releases'],
|
||||
queryKeys.applications(environmentId),
|
||||
]),
|
||||
});
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { withGlobalError, withInvalidate } from '@/react-tools/react-query';
|
||||
import { queryKeys as applicationsQueryKeys } from '@/react/kubernetes/applications/queries/query-keys';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
export function useUninstallHelmAppMutation(environmentId: EnvironmentId) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
releaseName,
|
||||
namespace,
|
||||
}: {
|
||||
releaseName: string;
|
||||
namespace?: string;
|
||||
}) => uninstallHelmApplication(environmentId, releaseName, namespace),
|
||||
...withInvalidate(queryClient, [
|
||||
applicationsQueryKeys.applications(environmentId),
|
||||
]),
|
||||
...withGlobalError('Unable to uninstall helm application'),
|
||||
});
|
||||
}
|
||||
|
||||
export async function uninstallHelmApplication(
|
||||
environmentId: EnvironmentId,
|
||||
releaseName: string,
|
||||
namespace?: string
|
||||
) {
|
||||
try {
|
||||
await axios.delete(
|
||||
`/endpoints/${environmentId}/kubernetes/helm/${releaseName}`,
|
||||
{ params: { namespace } }
|
||||
);
|
||||
} catch (error) {
|
||||
// parseAxiosError, because it's a regular portainer api error
|
||||
throw parseAxiosError(error, 'Unable to remove application');
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue