diff --git a/api/http/handler/helm/helm_repo_search.go b/api/http/handler/helm/helm_repo_search.go index 976558b9b..aab9c523d 100644 --- a/api/http/handler/helm/helm_repo_search.go +++ b/api/http/handler/helm/helm_repo_search.go @@ -7,7 +7,6 @@ import ( "github.com/portainer/portainer/pkg/libhelm/options" httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" "github.com/pkg/errors" ) @@ -33,18 +32,13 @@ func (handler *Handler) helmRepoSearch(w http.ResponseWriter, r *http.Request) * return httperror.BadRequest("Bad request", errors.New("missing `repo` query parameter")) } - chart, _ := request.RetrieveQueryParameter(r, "chart", false) - useCache, _ := request.RetrieveBooleanQueryParameter(r, "useCache", false) - _, err := url.ParseRequestURI(repo) if err != nil { return httperror.BadRequest("Bad request", errors.Wrap(err, fmt.Sprintf("provided URL %q is not valid", repo))) } searchOpts := options.SearchRepoOptions{ - Repo: repo, - Chart: chart, - UseCache: useCache, + Repo: repo, } result, err := handler.helmPackageManager.SearchRepo(searchOpts) diff --git a/app/react/kubernetes/helm/HelmApplicationView/ChartActions/UpgradeButton.test.tsx b/app/react/kubernetes/helm/HelmApplicationView/ChartActions/UpgradeButton.test.tsx index b45a54e79..4bea0bf47 100644 --- a/app/react/kubernetes/helm/HelmApplicationView/ChartActions/UpgradeButton.test.tsx +++ b/app/react/kubernetes/helm/HelmApplicationView/ChartActions/UpgradeButton.test.tsx @@ -33,8 +33,6 @@ vi.mock('../queries/useHelmRepositories', () => ({ ], isInitialLoading: false, isError: false, - isFetching: false, - refetch: vi.fn(() => Promise.resolve([])), })), useHelmRepositories: vi.fn(() => ({ data: ['repo1', 'repo2'], @@ -83,8 +81,6 @@ describe('UpgradeButton', () => { data, isInitialLoading: false, isError: false, - isFetching: false, - refetch: vi.fn(() => Promise.resolve([])), }); renderButton(); @@ -98,8 +94,6 @@ describe('UpgradeButton', () => { data: [], isInitialLoading: true, isError: false, - isFetching: false, - refetch: vi.fn(() => Promise.resolve([])), }); renderButton(); @@ -115,8 +109,6 @@ describe('UpgradeButton', () => { data, isInitialLoading: false, isError: false, - isFetching: false, - refetch: vi.fn(() => Promise.resolve([])), }); renderButton(); @@ -147,8 +139,6 @@ describe('UpgradeButton', () => { ], isInitialLoading: false, isError: false, - isFetching: false, - refetch: vi.fn(() => Promise.resolve([])), }); renderButton({ release: mockRelease }); diff --git a/app/react/kubernetes/helm/HelmApplicationView/ChartActions/UpgradeButton.tsx b/app/react/kubernetes/helm/HelmApplicationView/ChartActions/UpgradeButton.tsx index 7376877e4..4c385c388 100644 --- a/app/react/kubernetes/helm/HelmApplicationView/ChartActions/UpgradeButton.tsx +++ b/app/react/kubernetes/helm/HelmApplicationView/ChartActions/UpgradeButton.tsx @@ -1,6 +1,5 @@ import { ArrowUp } from 'lucide-react'; import { useRouter } from '@uirouter/react'; -import { useState } from 'react'; import { EnvironmentId } from '@/react/portainer/environments/types'; import { notifySuccess } from '@/portainer/services/notifications'; @@ -42,19 +41,16 @@ export function UpgradeButton({ const updateHelmReleaseMutation = useUpdateHelmReleaseMutation(environmentId); const repositoriesQuery = useHelmRepositories(); - const [useCache, setUseCache] = useState(true); const helmRepoVersionsQuery = useHelmRepoVersions( release?.chart.metadata?.name || '', 60 * 60 * 1000, // 1 hour - repositoriesQuery.data, - useCache + repositoriesQuery.data ); const versions = helmRepoVersionsQuery.data; // Combined loading state const isInitialLoading = repositoriesQuery.isInitialLoading || - helmRepoVersionsQuery.isFetching || helmRepoVersionsQuery.isInitialLoading; const isError = repositoriesQuery.isError || helmRepoVersionsQuery.isError; @@ -62,10 +58,9 @@ export function UpgradeButton({ select: (data) => data.chart.metadata?.version, }); const latestVersionAvailable = versions[0]?.Version ?? ''; - const isNewVersionAvailable = Boolean( + const isNewVersionAvailable = latestVersion?.data && - semverCompare(latestVersionAvailable, latestVersion?.data) === 1 - ); + semverCompare(latestVersionAvailable, latestVersion?.data) === 1; const editableHelmRelease: UpdateHelmReleasePayload = { name: releaseName, @@ -75,14 +70,6 @@ export function UpgradeButton({ version: release?.chart.metadata?.version, }; - function handleRefreshVersions() { - if (useCache === false) { - helmRepoVersionsQuery.refetch(); - } else { - setUseCache(false); - } - } - return (
)} - {!isInitialLoading && !isError && ( + {versions.length === 0 && !isInitialLoading && !isError && ( - {getStatusMessage( - versions.length === 0, - latestVersionAvailable, - isNewVersionAvailable - )} - {versions.length === 0 && ( - - Portainer is unable to find any versions for this chart in the - repositories saved. Try adding a new repository which contains - the chart in the{' '} - - Helm repositories settings - -
- } - /> - )} - + No versions available + + Portainer is unable to find any versions for this chart in the + repositories saved. Try adding a new repository which contains + the chart in the{' '} + + Helm repositories settings + + + } + /> + + )} + {isNewVersionAvailable && ( + + New version available ({latestVersionAvailable}) )} @@ -185,18 +164,4 @@ export function UpgradeButton({ }, }); } - - function getStatusMessage( - hasNoAvailableVersions: boolean, - latestVersionAvailable: string, - isNewVersionAvailable: boolean - ): string { - if (hasNoAvailableVersions) { - return 'No versions available '; - } - if (isNewVersionAvailable) { - return `New version available (${latestVersionAvailable}) `; - } - return ''; - } } diff --git a/app/react/kubernetes/helm/HelmApplicationView/queries/useHelmRepositories.ts b/app/react/kubernetes/helm/HelmApplicationView/queries/useHelmRepositories.ts index bf11eadb1..733b79dea 100644 --- a/app/react/kubernetes/helm/HelmApplicationView/queries/useHelmRepositories.ts +++ b/app/react/kubernetes/helm/HelmApplicationView/queries/useHelmRepositories.ts @@ -45,21 +45,20 @@ export function useHelmRepositories() { export function useHelmRepoVersions( chart: string, staleTime: number, - repositories: string[] = [], - useCache: boolean = true + 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, useCache], - queryFn: () => getSearchHelmRepo(repo, chart, useCache), + 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, useCache] + [repositories, chart, staleTime] ), }); @@ -73,8 +72,6 @@ export function useHelmRepoVersions( data: allVersions, isInitialLoading: versionQueries.some((q) => q.isLoading), isError: versionQueries.some((q) => q.isError), - isFetching: versionQueries.some((q) => q.isFetching), - refetch: () => Promise.all(versionQueries.map((q) => q.refetch())), }; } @@ -83,12 +80,11 @@ export function useHelmRepoVersions( */ async function getSearchHelmRepo( repo: string, - chart: string, - useCache: boolean = true + chart: string ): Promise { try { const { data } = await axios.get(`templates/helm`, { - params: { repo, chart, useCache }, + params: { repo, chart }, }); const versions = data.entries[chart]; return ( diff --git a/pkg/libhelm/options/search_repo_options.go b/pkg/libhelm/options/search_repo_options.go index 0b35c0bbd..73acc5709 100644 --- a/pkg/libhelm/options/search_repo_options.go +++ b/pkg/libhelm/options/search_repo_options.go @@ -3,8 +3,6 @@ package options import "net/http" type SearchRepoOptions struct { - Repo string `example:"https://charts.gitlab.io/"` - Client *http.Client `example:"&http.Client{Timeout: time.Second * 10}"` - Chart string `example:"my-chart"` - UseCache bool `example:"false"` + Repo string `example:"https://charts.gitlab.io/"` + Client *http.Client `example:"&http.Client{Timeout: time.Second * 10}"` } diff --git a/pkg/libhelm/sdk/search_repo.go b/pkg/libhelm/sdk/search_repo.go index 63015330c..90dd98529 100644 --- a/pkg/libhelm/sdk/search_repo.go +++ b/pkg/libhelm/sdk/search_repo.go @@ -4,8 +4,6 @@ import ( "net/url" "os" "path/filepath" - "sync" - "time" "github.com/pkg/errors" "github.com/portainer/portainer/pkg/libhelm/options" @@ -27,17 +25,6 @@ type RepoIndex struct { Generated string `json:"generated"` } -type RepoIndexCache struct { - Index *repo.IndexFile - Timestamp time.Time -} - -var ( - indexCache = make(map[string]RepoIndexCache) - cacheMutex sync.RWMutex - cacheDuration = 60 * time.Minute -) - // SearchRepo downloads the `index.yaml` file for specified repo, parses it and returns JSON to caller. func (hspm *HelmSDKPackageManager) SearchRepo(searchRepoOpts options.SearchRepoOptions) ([]byte, error) { // Validate input options @@ -66,18 +53,6 @@ func (hspm *HelmSDKPackageManager) SearchRepo(searchRepoOpts options.SearchRepoO return nil, err } - // Check cache first - if searchRepoOpts.UseCache { - cacheMutex.RLock() - if cached, exists := indexCache[repoURL.String()]; exists { - if time.Since(cached.Timestamp) < cacheDuration { - cacheMutex.RUnlock() - return convertAndMarshalIndex(cached.Index, searchRepoOpts.Chart) - } - } - cacheMutex.RUnlock() - } - // Set up Helm CLI environment repoSettings := cli.New() @@ -117,21 +92,23 @@ func (hspm *HelmSDKPackageManager) SearchRepo(searchRepoOpts options.SearchRepoO return nil, err } - // Update cache and remove old entries - cacheMutex.Lock() - indexCache[searchRepoOpts.Repo] = RepoIndexCache{ - Index: indexFile, - Timestamp: time.Now(), - } - for key, index := range indexCache { - if time.Since(index.Timestamp) > cacheDuration { - delete(indexCache, key) - } + // Convert the index file to our response format + result, err := convertIndexToResponse(indexFile) + if err != nil { + log.Error(). + Str("context", "HelmClient"). + Err(err). + Msg("Failed to convert index to response format") + return nil, errors.Wrap(err, "failed to convert index to response format") } - cacheMutex.Unlock() + log.Debug(). + Str("context", "HelmClient"). + Str("repo", searchRepoOpts.Repo). + Int("entries_count", len(indexFile.Entries)). + Msg("Successfully searched repository") - return convertAndMarshalIndex(indexFile, searchRepoOpts.Chart) + return json.Marshal(result) } // validateSearchRepoOptions validates the required search repository options. @@ -239,7 +216,7 @@ func loadIndexFile(indexPath string) (*repo.IndexFile, error) { } // convertIndexToResponse converts the Helm index file to our response format. -func convertIndexToResponse(indexFile *repo.IndexFile, chartName string) (RepoIndex, error) { +func convertIndexToResponse(indexFile *repo.IndexFile) (RepoIndex, error) { result := RepoIndex{ APIVersion: indexFile.APIVersion, Entries: make(map[string][]ChartInfo), @@ -248,9 +225,7 @@ func convertIndexToResponse(indexFile *repo.IndexFile, chartName string) (RepoIn // Convert Helm SDK types to our response types for name, charts := range indexFile.Entries { - if chartName == "" || name == chartName { - result.Entries[name] = convertChartsToChartInfo(charts) - } + result.Entries[name] = convertChartsToChartInfo(charts) } return result, nil @@ -374,23 +349,3 @@ func ensureHelmDirectoriesExist(settings *cli.EnvSettings) error { return nil } - -func convertAndMarshalIndex(indexFile *repo.IndexFile, chartName string) ([]byte, error) { - // Convert the index file to our response format - result, err := convertIndexToResponse(indexFile, chartName) - if err != nil { - log.Error(). - Str("context", "HelmClient"). - Err(err). - Msg("Failed to convert index to response format") - return nil, errors.Wrap(err, "failed to convert index to response format") - } - - log.Debug(). - Str("context", "HelmClient"). - Str("repo", chartName). - Int("entries_count", len(indexFile.Entries)). - Msg("Successfully searched repository") - - return json.Marshal(result) -}