1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-23 07:19:41 +02:00

feat(helm): helm actions [r8s-259] (#715)

Co-authored-by: James Player <james.player@portainer.io>
Co-authored-by: Cara Ryan <cara.ryan@portainer.io>
Co-authored-by: stevensbkang <skan070@gmail.com>
This commit is contained in:
Ali 2025-05-13 22:15:04 +12:00 committed by GitHub
parent dfa32b6755
commit 4ee349bd6b
117 changed files with 4161 additions and 696 deletions

View file

@ -0,0 +1,44 @@
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');
}
}

View file

@ -6,6 +6,15 @@ 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
*/
@ -13,39 +22,52 @@ export function useHelmRelease<T = HelmRelease>(
environmentId: EnvironmentId,
name: string,
namespace: string,
options: {
select?: (data: HelmRelease) => T;
showResources?: boolean;
refetchInterval?: number;
} = {}
options: Options<T> = {}
) {
const { select, showResources, refetchInterval } = options;
const { select, showResources, refetchInterval, revision, staleTime } =
options;
return useQuery(
[environmentId, 'helm', 'releases', namespace, name, options.showResources],
[
environmentId,
'helm',
'releases',
namespace,
name,
revision,
showResources,
],
() =>
getHelmRelease(environmentId, name, {
namespace,
showResources,
revision,
}),
{
enabled: !!environmentId && !!name && !!namespace,
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: {
namespace: string;
showResources?: boolean;
}
params: Params
) {
try {
const { data } = await axios.get<HelmRelease>(

View file

@ -0,0 +1,99 @@
import { useQuery, useQueries } from '@tanstack/react-query';
import { useMemo } from 'react';
import { compact, flatMap } from 'lodash';
import { withGlobalError } from '@/react-tools/react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { useCurrentUser } from '@/react/hooks/useUser';
import { getHelmRepositories } from '../../queries/useHelmChartList';
interface HelmSearch {
entries: Entries;
}
interface Entries {
[key: string]: { version: string }[];
}
export interface ChartVersion {
Repo: string;
Version: string;
}
/**
* Hook to fetch all Helm repositories for the current user
*/
export function useHelmRepositories() {
const { user } = useCurrentUser();
return useQuery(
['helm', 'repositories'],
async () => getHelmRepositories(user.Id),
{
enabled: !!user.Id,
...withGlobalError('Unable to retrieve helm repositories'),
}
);
}
/**
* React hook to get a list of available versions for a chart from specified repositories
*
* @param chart The chart name to get versions for
* @param repositories Array of repository URLs to search in
*/
export function useHelmRepoVersions(
chart: string,
staleTime: number,
repositories: string[] = []
) {
// Fetch versions from each repository in parallel as separate queries
const versionQueries = useQueries({
queries: useMemo(
() =>
repositories.map((repo) => ({
queryKey: ['helm', 'repositories', chart, repo],
queryFn: () => getSearchHelmRepo(repo, chart),
enabled: !!chart && repositories.length > 0,
staleTime,
...withGlobalError(`Unable to retrieve versions from ${repo}`),
})),
[repositories, chart, staleTime]
),
});
// Combine the results from all repositories for easier consumption
const allVersions = useMemo(() => {
const successfulResults = compact(versionQueries.map((q) => q.data));
return flatMap(successfulResults);
}, [versionQueries]);
return {
data: allVersions,
isInitialLoading: versionQueries.some((q) => q.isLoading),
isError: versionQueries.some((q) => q.isError),
};
}
/**
* Get Helm repositories for user
*/
async function getSearchHelmRepo(
repo: string,
chart: string
): Promise<ChartVersion[]> {
try {
const { data } = await axios.get<HelmSearch>(`templates/helm`, {
params: { repo, chart },
});
const versions = data.entries[chart];
return (
versions?.map((v) => ({
Repo: repo,
Version: v.version,
})) ?? []
);
} catch (err) {
throw parseAxiosError(err, 'Unable to retrieve helm repositories for user');
}
}

View file

@ -0,0 +1,44 @@
import { useQueryClient, useMutation } 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';
import { HelmRelease } from '../../types';
export interface UpdateHelmReleasePayload {
namespace: string;
values?: string;
repo?: string;
name: string;
chart: string;
version?: string;
}
export function useUpdateHelmReleaseMutation(environmentId: EnvironmentId) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (payload: UpdateHelmReleasePayload) =>
updateHelmRelease(environmentId, payload),
...withInvalidate(queryClient, [
[environmentId, 'helm', 'releases'],
applicationsQueryKeys.applications(environmentId),
]),
...withGlobalError('Unable to uninstall helm application'),
});
}
async function updateHelmRelease(
environmentId: EnvironmentId,
payload: UpdateHelmReleasePayload
) {
try {
const { data } = await axios.post<HelmRelease>(
`endpoints/${environmentId}/kubernetes/helm`,
payload
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to update helm release');
}
}