mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 05:19:39 +02:00
fix: fetching values from both install and upgrade views - develop [R8S-368] (#820)
This commit is contained in:
parent
d51e9205d9
commit
c897baad20
7 changed files with 111 additions and 45 deletions
|
@ -44,7 +44,6 @@ export function UpgradeButton({
|
||||||
useCache
|
useCache
|
||||||
);
|
);
|
||||||
const versions = helmRepoVersionsQuery.data;
|
const versions = helmRepoVersionsQuery.data;
|
||||||
const repo = versions?.[0]?.Repo;
|
|
||||||
|
|
||||||
// Combined loading state
|
// Combined loading state
|
||||||
const isLoading =
|
const isLoading =
|
||||||
|
@ -63,17 +62,28 @@ export function UpgradeButton({
|
||||||
latestVersionQuery?.data &&
|
latestVersionQuery?.data &&
|
||||||
semverCompare(latestVersionAvailable, latestVersionQuery?.data) === 1
|
semverCompare(latestVersionAvailable, latestVersionQuery?.data) === 1
|
||||||
);
|
);
|
||||||
const currentVersion = release?.chart.metadata?.version;
|
|
||||||
|
const currentRepo = versions?.find(
|
||||||
|
(v) =>
|
||||||
|
v.Chart === release?.chart.metadata?.name &&
|
||||||
|
v.AppVersion === release?.chart.metadata?.appVersion &&
|
||||||
|
v.Version === release?.chart.metadata?.version
|
||||||
|
)?.Repo;
|
||||||
|
|
||||||
const editableHelmRelease: UpdateHelmReleasePayload = {
|
const editableHelmRelease: UpdateHelmReleasePayload = {
|
||||||
name: releaseName,
|
name: releaseName,
|
||||||
namespace: namespace || '',
|
namespace: namespace || '',
|
||||||
values: release?.values?.userSuppliedValues,
|
values: release?.values?.userSuppliedValues,
|
||||||
chart: release?.chart.metadata?.name || '',
|
chart: release?.chart.metadata?.name || '',
|
||||||
version: currentVersion,
|
appVersion: release?.chart.metadata?.appVersion,
|
||||||
repo,
|
version: release?.chart.metadata?.version,
|
||||||
|
repo: currentRepo ?? '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const filteredVersions = currentRepo
|
||||||
|
? versions?.filter((v) => v.Repo === currentRepo) || []
|
||||||
|
: versions || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
|
@ -151,7 +161,7 @@ export function UpgradeButton({
|
||||||
async function handleUpgrade() {
|
async function handleUpgrade() {
|
||||||
const submittedUpgradeValues = await openUpgradeHelmModal(
|
const submittedUpgradeValues = await openUpgradeHelmModal(
|
||||||
editableHelmRelease,
|
editableHelmRelease,
|
||||||
versions
|
filteredVersions
|
||||||
);
|
);
|
||||||
|
|
||||||
if (submittedUpgradeValues) {
|
if (submittedUpgradeValues) {
|
||||||
|
|
|
@ -19,39 +19,48 @@ import { useHelmChartValues } from '../../queries/useHelmChartValues';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onSubmit: OnSubmit<UpdateHelmReleasePayload>;
|
onSubmit: OnSubmit<UpdateHelmReleasePayload>;
|
||||||
values: UpdateHelmReleasePayload;
|
payload: UpdateHelmReleasePayload;
|
||||||
versions: ChartVersion[];
|
versions: ChartVersion[];
|
||||||
chartName: string;
|
chartName: string;
|
||||||
repo: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpgradeHelmModal({
|
export function UpgradeHelmModal({
|
||||||
values,
|
payload,
|
||||||
versions,
|
versions,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
chartName,
|
chartName,
|
||||||
repo,
|
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const versionOptions: Option<ChartVersion>[] = versions.map((version) => {
|
const versionOptions: Option<ChartVersion>[] = versions.map((version) => {
|
||||||
const isCurrentVersion = version.Version === values.version;
|
const repo = payload.repo === version.Repo ? version.Repo : '';
|
||||||
const label = `${version.Repo}@${version.Version}${
|
const isCurrentVersion =
|
||||||
|
version.AppVersion === payload.appVersion &&
|
||||||
|
version.Version === payload.version;
|
||||||
|
|
||||||
|
const label = `${repo}@${version.Version}${
|
||||||
isCurrentVersion ? ' (current)' : ''
|
isCurrentVersion ? ' (current)' : ''
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
repo,
|
||||||
label,
|
label,
|
||||||
value: version,
|
value: version,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultVersion =
|
const defaultVersion =
|
||||||
versionOptions.find((v) => v.value.Version === values.version)?.value ||
|
versionOptions.find(
|
||||||
versionOptions[0]?.value;
|
(v) =>
|
||||||
|
v.value.AppVersion === payload.appVersion &&
|
||||||
|
v.value.Version === payload.version &&
|
||||||
|
v.value.Repo === payload.repo
|
||||||
|
)?.value || versionOptions[0]?.value;
|
||||||
const [version, setVersion] = useState<ChartVersion>(defaultVersion);
|
const [version, setVersion] = useState<ChartVersion>(defaultVersion);
|
||||||
const [userValues, setUserValues] = useState<string>(values.values || '');
|
const [userValues, setUserValues] = useState<string>(payload.values || '');
|
||||||
const [atomic, setAtomic] = useState<boolean>(true);
|
const [atomic, setAtomic] = useState<boolean>(true);
|
||||||
|
|
||||||
const chartValuesRefQuery = useHelmChartValues({
|
const chartValuesRefQuery = useHelmChartValues({
|
||||||
chart: chartName,
|
chart: chartName,
|
||||||
repo,
|
repo: version.Repo,
|
||||||
version: version.Version,
|
version: version.Version,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -75,7 +84,7 @@ export function UpgradeHelmModal({
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
id="release-name-input"
|
id="release-name-input"
|
||||||
value={values.name}
|
value={payload.name}
|
||||||
readOnly
|
readOnly
|
||||||
disabled
|
disabled
|
||||||
data-cy="helm-release-name-input"
|
data-cy="helm-release-name-input"
|
||||||
|
@ -88,7 +97,7 @@ export function UpgradeHelmModal({
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
id="namespace-input"
|
id="namespace-input"
|
||||||
value={values.namespace}
|
value={payload.namespace}
|
||||||
readOnly
|
readOnly
|
||||||
disabled
|
disabled
|
||||||
data-cy="helm-namespace-input"
|
data-cy="helm-namespace-input"
|
||||||
|
@ -142,10 +151,10 @@ export function UpgradeHelmModal({
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
onSubmit({
|
onSubmit({
|
||||||
name: values.name,
|
name: payload.name,
|
||||||
values: userValues,
|
values: userValues,
|
||||||
namespace: values.namespace,
|
namespace: payload.namespace,
|
||||||
chart: values.chart,
|
chart: payload.chart,
|
||||||
repo: version.Repo,
|
repo: version.Repo,
|
||||||
version: version.Version,
|
version: version.Version,
|
||||||
atomic,
|
atomic,
|
||||||
|
@ -165,13 +174,12 @@ export function UpgradeHelmModal({
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openUpgradeHelmModal(
|
export async function openUpgradeHelmModal(
|
||||||
values: UpdateHelmReleasePayload,
|
payload: UpdateHelmReleasePayload,
|
||||||
versions: ChartVersion[]
|
versions: ChartVersion[]
|
||||||
) {
|
) {
|
||||||
return openModal(withReactQuery(withCurrentUser(UpgradeHelmModal)), {
|
return openModal(withReactQuery(withCurrentUser(UpgradeHelmModal)), {
|
||||||
values,
|
payload,
|
||||||
versions,
|
versions,
|
||||||
chartName: values.chart,
|
chartName: payload.chart,
|
||||||
repo: values.repo ?? '',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,15 @@ interface HelmSearch {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Entries {
|
interface Entries {
|
||||||
[key: string]: { version: string }[];
|
[key: string]: { version: string; appVersion: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChartVersion {
|
export interface ChartVersion {
|
||||||
|
Chart?: string;
|
||||||
Repo: string;
|
Repo: string;
|
||||||
|
Label?: string;
|
||||||
Version: string;
|
Version: string;
|
||||||
|
AppVersion?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,8 +80,10 @@ async function getSearchHelmRepo(
|
||||||
const versions = data.entries[chart];
|
const versions = data.entries[chart];
|
||||||
return (
|
return (
|
||||||
versions?.map((v) => ({
|
versions?.map((v) => ({
|
||||||
|
Chart: chart,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Version: v.version,
|
Version: v.version,
|
||||||
|
AppVersion: v.appVersion,
|
||||||
})) ?? []
|
})) ?? []
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -120,9 +120,10 @@ export interface InstallChartPayload {
|
||||||
export interface UpdateHelmReleasePayload {
|
export interface UpdateHelmReleasePayload {
|
||||||
namespace: string;
|
namespace: string;
|
||||||
values?: string;
|
values?: string;
|
||||||
repo?: string;
|
repo: string;
|
||||||
name: string;
|
name: string;
|
||||||
chart: string;
|
chart: string;
|
||||||
|
appVersion?: string;
|
||||||
version?: string;
|
version?: string;
|
||||||
atomic?: boolean;
|
atomic?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ type Release struct {
|
||||||
Manifest string `json:"manifest,omitempty"`
|
Manifest string `json:"manifest,omitempty"`
|
||||||
// Hooks are all of the hooks declared for this release.
|
// Hooks are all of the hooks declared for this release.
|
||||||
Hooks []*Hook `json:"hooks,omitempty"`
|
Hooks []*Hook `json:"hooks,omitempty"`
|
||||||
|
// AppVersion is the app version of the release.
|
||||||
|
AppVersion string `json:"appVersion,omitempty"`
|
||||||
// Version is an int which represents the revision of the release.
|
// Version is an int which represents the revision of the release.
|
||||||
Version int `json:"version,omitempty"`
|
Version int `json:"version,omitempty"`
|
||||||
// Namespace is the kubernetes namespace of the release.
|
// Namespace is the kubernetes namespace of the release.
|
||||||
|
|
|
@ -90,8 +90,17 @@ func (hspm *HelmSDKPackageManager) SearchRepo(searchRepoOpts options.SearchRepoO
|
||||||
return nil, errors.Wrap(err, "failed to ensure Helm directories exist")
|
return nil, errors.Wrap(err, "failed to ensure Helm directories exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repoName, err := getRepoNameFromURL(repoURL.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Error().
|
||||||
|
Str("context", "HelmClient").
|
||||||
|
Err(err).
|
||||||
|
Msg("Failed to get hostname from URL")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Download the index file and update repository configuration
|
// Download the index file and update repository configuration
|
||||||
indexPath, err := downloadRepoIndex(repoURL.String(), repoSettings, searchRepoOpts.Repo)
|
indexPath, err := downloadRepoIndex(repoURL.String(), repoSettings, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Str("context", "HelmClient").
|
Str("context", "HelmClient").
|
||||||
|
@ -163,7 +172,8 @@ func downloadRepoIndex(repoURLString string, repoSettings *cli.EnvSettings, repo
|
||||||
// Create chart repository object
|
// Create chart repository object
|
||||||
rep, err := repo.NewChartRepository(
|
rep, err := repo.NewChartRepository(
|
||||||
&repo.Entry{
|
&repo.Entry{
|
||||||
URL: repoURLString,
|
Name: repoName,
|
||||||
|
URL: repoURLString,
|
||||||
},
|
},
|
||||||
getter.All(repoSettings),
|
getter.All(repoSettings),
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,8 @@ package sdk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||||
|
@ -32,24 +33,32 @@ func (hspm *HelmSDKPackageManager) Show(showOpts options.ShowOptions) ([]byte, e
|
||||||
Str("output_format", string(showOpts.OutputFormat)).
|
Str("output_format", string(showOpts.OutputFormat)).
|
||||||
Msg("Showing chart information")
|
Msg("Showing chart information")
|
||||||
|
|
||||||
// Initialize action configuration (no namespace or cluster access needed)
|
repoURL, err := parseRepoURL(showOpts.Repo)
|
||||||
actionConfig := new(action.Configuration)
|
|
||||||
err := hspm.initActionConfig(actionConfig, "", nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// error is already logged in initActionConfig
|
log.Error().
|
||||||
return nil, fmt.Errorf("failed to initialize helm configuration: %w", err)
|
Str("context", "HelmClient").
|
||||||
|
Str("repo", showOpts.Repo).
|
||||||
|
Err(err).
|
||||||
|
Msg("Invalid repository URL")
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create temporary directory for chart download
|
repoName, err := getRepoNameFromURL(repoURL.String())
|
||||||
tempDir, err := os.MkdirTemp("", "helm-show-*")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Str("context", "HelmClient").
|
Str("context", "HelmClient").
|
||||||
Err(err).
|
Err(err).
|
||||||
Msg("Failed to create temp directory")
|
Msg("Failed to get hostname from URL")
|
||||||
return nil, fmt.Errorf("failed to create temp directory: %w", err)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize action configuration (no namespace or cluster access needed)
|
||||||
|
actionConfig := new(action.Configuration)
|
||||||
|
err = hspm.initActionConfig(actionConfig, "", nil)
|
||||||
|
if err != nil {
|
||||||
|
// error is already logged in initActionConfig
|
||||||
|
return nil, fmt.Errorf("failed to initialize helm configuration: %w", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tempDir)
|
|
||||||
|
|
||||||
// Create showClient action
|
// Create showClient action
|
||||||
showClient, err := initShowClient(actionConfig, showOpts)
|
showClient, err := initShowClient(actionConfig, showOpts)
|
||||||
|
@ -68,11 +77,12 @@ func (hspm *HelmSDKPackageManager) Show(showOpts options.ShowOptions) ([]byte, e
|
||||||
Str("repo", showOpts.Repo).
|
Str("repo", showOpts.Repo).
|
||||||
Msg("Locating chart")
|
Msg("Locating chart")
|
||||||
|
|
||||||
chartPath, err := showClient.ChartPathOptions.LocateChart(showOpts.Chart, hspm.settings)
|
fullChartPath := fmt.Sprintf("%s/%s", repoName, showOpts.Chart)
|
||||||
|
chartPath, err := showClient.ChartPathOptions.LocateChart(fullChartPath, hspm.settings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Str("context", "HelmClient").
|
Str("context", "HelmClient").
|
||||||
Str("chart", showOpts.Chart).
|
Str("chart", fullChartPath).
|
||||||
Str("repo", showOpts.Repo).
|
Str("repo", showOpts.Repo).
|
||||||
Err(err).
|
Err(err).
|
||||||
Msg("Failed to locate chart")
|
Msg("Failed to locate chart")
|
||||||
|
@ -104,13 +114,10 @@ func (hspm *HelmSDKPackageManager) Show(showOpts options.ShowOptions) ([]byte, e
|
||||||
// and return the show client.
|
// and return the show client.
|
||||||
func initShowClient(actionConfig *action.Configuration, showOpts options.ShowOptions) (*action.Show, error) {
|
func initShowClient(actionConfig *action.Configuration, showOpts options.ShowOptions) (*action.Show, error) {
|
||||||
showClient := action.NewShowWithConfig(action.ShowAll, actionConfig)
|
showClient := action.NewShowWithConfig(action.ShowAll, actionConfig)
|
||||||
showClient.ChartPathOptions.RepoURL = showOpts.Repo
|
showClient.ChartPathOptions.Version = showOpts.Version
|
||||||
showClient.ChartPathOptions.Version = showOpts.Version // If version is "", it will use the latest version
|
|
||||||
|
|
||||||
// Set output type based on ShowOptions
|
// Set output type based on ShowOptions
|
||||||
switch showOpts.OutputFormat {
|
switch showOpts.OutputFormat {
|
||||||
case options.ShowAll:
|
|
||||||
showClient.OutputFormat = action.ShowAll
|
|
||||||
case options.ShowChart:
|
case options.ShowChart:
|
||||||
showClient.OutputFormat = action.ShowChart
|
showClient.OutputFormat = action.ShowChart
|
||||||
case options.ShowValues:
|
case options.ShowValues:
|
||||||
|
@ -127,3 +134,26 @@ func initShowClient(actionConfig *action.Configuration, showOpts options.ShowOpt
|
||||||
|
|
||||||
return showClient, nil
|
return showClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getRepoNameFromURL extracts a unique repository identifier from a URL string.
|
||||||
|
// It combines hostname and path to ensure uniqueness across different repositories on the same host.
|
||||||
|
// Examples:
|
||||||
|
// - https://portainer.github.io/test-public-repo/ -> portainer.github.io-test-public-repo
|
||||||
|
// - https://portainer.github.io/another-repo/ -> portainer.github.io-another-repo
|
||||||
|
// - https://charts.helm.sh/stable -> charts.helm.sh-stable
|
||||||
|
func getRepoNameFromURL(urlStr string) (string, error) {
|
||||||
|
parsedURL, err := url.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname := parsedURL.Hostname()
|
||||||
|
path := parsedURL.Path
|
||||||
|
path = strings.Trim(path, "/")
|
||||||
|
path = strings.ReplaceAll(path, "/", "-")
|
||||||
|
|
||||||
|
if path == "" {
|
||||||
|
return hostname, nil
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s-%s", hostname, path), nil
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue