2025-05-13 22:15:04 +12:00
import { ArrowUp } from 'lucide-react' ;
import { useRouter } from '@uirouter/react' ;
2025-05-27 15:20:28 +12:00
import { useState } from 'react' ;
2025-05-13 22:15:04 +12:00
import { EnvironmentId } from '@/react/portainer/environments/types' ;
import { notifySuccess } from '@/portainer/services/notifications' ;
import { semverCompare } from '@/react/common/semver-utils' ;
2025-06-05 13:13:45 +12:00
import { Button , LoadingButton } from '@@/buttons' ;
2025-05-13 22:15:04 +12:00
import { InlineLoader } from '@@/InlineLoader' ;
import { Tooltip } from '@@/Tip/Tooltip' ;
import { Link } from '@@/Link' ;
2025-06-05 13:13:45 +12:00
import { HelmRelease , UpdateHelmReleasePayload } from '../../types' ;
import { useUpdateHelmReleaseMutation } from '../../queries/useUpdateHelmReleaseMutation' ;
2025-06-09 20:08:50 +12:00
import { useHelmRepoVersions } from '../../queries/useHelmRepoVersions' ;
2025-05-13 22:15:04 +12:00
import { useHelmRelease } from '../queries/useHelmRelease' ;
2025-06-09 20:08:50 +12:00
import { useHelmRegistries } from '../../queries/useHelmRegistries' ;
2025-05-13 22:15:04 +12:00
import { openUpgradeHelmModal } from './UpgradeHelmModal' ;
export function UpgradeButton ( {
environmentId ,
releaseName ,
namespace ,
release ,
updateRelease ,
} : {
environmentId : EnvironmentId ;
releaseName : string ;
namespace : string ;
release? : HelmRelease ;
updateRelease : ( release : HelmRelease ) = > void ;
} ) {
const router = useRouter ( ) ;
2025-06-05 13:13:45 +12:00
const [ useCache , setUseCache ] = useState ( true ) ;
2025-05-13 22:15:04 +12:00
const updateHelmReleaseMutation = useUpdateHelmReleaseMutation ( environmentId ) ;
2025-06-09 20:08:50 +12:00
const registriesQuery = useHelmRegistries ( ) ;
2025-05-13 22:15:04 +12:00
const helmRepoVersionsQuery = useHelmRepoVersions (
release ? . chart . metadata ? . name || '' ,
60 * 60 * 1000 , // 1 hour
2025-06-09 20:08:50 +12:00
registriesQuery . data ,
2025-05-27 15:20:28 +12:00
useCache
2025-05-13 22:15:04 +12:00
) ;
const versions = helmRepoVersionsQuery . data ;
2025-06-05 13:13:45 +12:00
const repo = versions ? . [ 0 ] ? . Repo ;
2025-05-13 22:15:04 +12:00
// Combined loading state
2025-05-27 15:20:28 +12:00
const isLoading =
2025-06-09 20:08:50 +12:00
registriesQuery . isInitialLoading || helmRepoVersionsQuery . isFetching ; // use 'isFetching' for helmRepoVersionsQuery because we want to show when it's refetching
const isError = registriesQuery . isError || helmRepoVersionsQuery . isError ;
2025-06-05 13:13:45 +12:00
const latestVersionQuery = useHelmRelease (
environmentId ,
releaseName ,
namespace ,
{
select : ( data ) = > data . chart . metadata ? . version ,
}
) ;
2025-05-13 22:15:04 +12:00
const latestVersionAvailable = versions [ 0 ] ? . Version ? ? '' ;
2025-05-27 15:20:28 +12:00
const isNewVersionAvailable = Boolean (
2025-06-05 13:13:45 +12:00
latestVersionQuery ? . data &&
semverCompare ( latestVersionAvailable , latestVersionQuery ? . data ) === 1
2025-05-27 15:20:28 +12:00
) ;
2025-06-05 13:13:45 +12:00
const currentVersion = release ? . chart . metadata ? . version ;
2025-05-13 22:15:04 +12:00
const editableHelmRelease : UpdateHelmReleasePayload = {
name : releaseName ,
namespace : namespace || '' ,
values : release?.values?.userSuppliedValues ,
chart : release?.chart.metadata?.name || '' ,
2025-06-05 13:13:45 +12:00
version : currentVersion ,
repo ,
2025-05-13 22:15:04 +12:00
} ;
return (
< div className = "relative" >
< LoadingButton
color = "secondary"
data - cy = "k8sApp-upgradeHelmChartButton"
2025-06-05 13:13:45 +12:00
onClick = { handleUpgrade }
2025-05-13 22:15:04 +12:00
disabled = {
versions . length === 0 ||
2025-05-27 15:20:28 +12:00
isLoading ||
2025-05-13 22:15:04 +12:00
isError ||
release ? . info ? . status ? . startsWith ( 'pending' )
}
loadingText = "Upgrading..."
isLoading = { updateHelmReleaseMutation . isLoading }
icon = { ArrowUp }
size = "medium"
>
Upgrade
< / LoadingButton >
2025-05-27 15:20:28 +12:00
{ isLoading && (
2025-05-13 22:15:04 +12:00
< InlineLoader
size = "xs"
className = "absolute -bottom-5 left-0 right-0 whitespace-nowrap"
>
Checking for new versions . . .
< / InlineLoader >
) }
2025-05-27 15:20:28 +12:00
{ ! isLoading && ! isError && (
2025-05-13 22:15:04 +12:00
< span className = "absolute flex items-center -bottom-5 left-0 right-0 text-xs text-muted text-center whitespace-nowrap" >
2025-05-27 15:20:28 +12:00
{ getStatusMessage (
versions . length === 0 ,
latestVersionAvailable ,
isNewVersionAvailable
) }
{ versions . length === 0 && (
< Tooltip
message = {
< div >
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 { ' ' }
< Link
to = "portainer.account"
params = { { '#' : 'helm-repositories' } }
data - cy = "user-settings-link"
>
Helm repositories settings
< / Link >
< / div >
}
/ >
) }
2025-06-05 13:13:45 +12:00
< Button
data - cy = "k8sApp-refreshHelmChartVersionsButton"
color = "link"
size = "xsmall"
2025-05-27 15:20:28 +12:00
onClick = { handleRefreshVersions }
type = "button"
>
2025-06-05 13:13:45 +12:00
Refresh
< / Button >
2025-05-13 22:15:04 +12:00
< / span >
) }
< / div >
) ;
2025-06-05 13:13:45 +12:00
function handleRefreshVersions() {
if ( useCache ) {
// clicking 'refresh versions' should get the latest versions from the repo, not the cached versions
setUseCache ( false ) ;
2025-05-13 22:15:04 +12:00
}
2025-06-05 13:13:45 +12:00
helmRepoVersionsQuery . refetch ( ) ;
2025-05-13 22:15:04 +12:00
}
2025-06-05 13:13:45 +12:00
async function handleUpgrade() {
const submittedUpgradeValues = await openUpgradeHelmModal (
editableHelmRelease ,
versions
) ;
if ( submittedUpgradeValues ) {
upgrade ( submittedUpgradeValues , release ) ;
}
function upgrade ( payload : UpdateHelmReleasePayload , release? : HelmRelease ) {
if ( release ? . info ) {
const updatedRelease = {
. . . release ,
info : {
. . . release . info ,
status : 'pending-upgrade' ,
description : 'Preparing upgrade' ,
} ,
} ;
updateRelease ( updatedRelease ) ;
}
updateHelmReleaseMutation . mutate ( payload , {
onSuccess : ( ) = > {
notifySuccess ( 'Success' , 'Helm chart upgraded successfully' ) ;
// set the revision url param to undefined to refresh the page at the latest revision
router . stateService . go ( 'kubernetes.helm' , {
namespace ,
name : releaseName ,
revision : undefined ,
} ) ;
2025-05-13 22:15:04 +12:00
} ,
2025-06-05 13:13:45 +12:00
} ) ;
2025-05-13 22:15:04 +12:00
}
}
2025-06-05 13:13:45 +12:00
}
2025-05-27 15:20:28 +12:00
2025-06-05 13:13:45 +12:00
function getStatusMessage (
hasNoAvailableVersions : boolean ,
latestVersionAvailable : string ,
isNewVersionAvailable : boolean
) {
if ( hasNoAvailableVersions ) {
return 'No versions available ' ;
}
if ( isNewVersionAvailable ) {
return ` New version available ( ${ latestVersionAvailable } ) ` ;
2025-05-27 15:20:28 +12:00
}
2025-06-05 13:13:45 +12:00
return 'Latest version installed' ;
2025-05-13 22:15:04 +12:00
}