Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3019e8ec11 |
@@ -611,7 +611,7 @@
|
||||
"RequiredPasswordLength": 12
|
||||
},
|
||||
"KubeconfigExpiry": "0",
|
||||
"KubectlShellImage": "portainer/kubectl-shell:2.31.2",
|
||||
"KubectlShellImage": "portainer/kubectl-shell:2.31.1",
|
||||
"LDAPSettings": {
|
||||
"AnonymousMode": true,
|
||||
"AutoCreateUsers": true,
|
||||
@@ -939,7 +939,7 @@
|
||||
}
|
||||
],
|
||||
"version": {
|
||||
"VERSION": "{\"SchemaVersion\":\"2.31.2\",\"MigratorCount\":0,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
|
||||
"VERSION": "{\"SchemaVersion\":\"2.31.1\",\"MigratorCount\":0,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
|
||||
},
|
||||
"webhooks": null
|
||||
}
|
||||
@@ -81,7 +81,7 @@ type Handler struct {
|
||||
}
|
||||
|
||||
// @title PortainerCE API
|
||||
// @version 2.31.2
|
||||
// @version 2.31.1
|
||||
// @description.markdown api-description.md
|
||||
// @termsOfService
|
||||
|
||||
|
||||
@@ -1728,7 +1728,7 @@ type (
|
||||
|
||||
const (
|
||||
// APIVersion is the version number of the Portainer API
|
||||
APIVersion = "2.31.2"
|
||||
APIVersion = "2.31.1"
|
||||
// Support annotation for the API version ("STS" for Short-Term Support or "LTS" for Long-Term Support)
|
||||
APIVersionSupport = "STS"
|
||||
// Edition is what this edition of Portainer is called
|
||||
|
||||
@@ -44,6 +44,7 @@ export function UpgradeButton({
|
||||
useCache
|
||||
);
|
||||
const versions = helmRepoVersionsQuery.data;
|
||||
const repo = versions?.[0]?.Repo;
|
||||
|
||||
// Combined loading state
|
||||
const isLoading =
|
||||
@@ -62,28 +63,17 @@ export function UpgradeButton({
|
||||
latestVersionQuery?.data &&
|
||||
semverCompare(latestVersionAvailable, latestVersionQuery?.data) === 1
|
||||
);
|
||||
|
||||
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 currentVersion = release?.chart.metadata?.version;
|
||||
|
||||
const editableHelmRelease: UpdateHelmReleasePayload = {
|
||||
name: releaseName,
|
||||
namespace: namespace || '',
|
||||
values: release?.values?.userSuppliedValues,
|
||||
chart: release?.chart.metadata?.name || '',
|
||||
appVersion: release?.chart.metadata?.appVersion,
|
||||
version: release?.chart.metadata?.version,
|
||||
repo: currentRepo ?? '',
|
||||
version: currentVersion,
|
||||
repo,
|
||||
};
|
||||
|
||||
const filteredVersions = currentRepo
|
||||
? versions?.filter((v) => v.Repo === currentRepo) || []
|
||||
: versions || [];
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<LoadingButton
|
||||
@@ -161,7 +151,7 @@ export function UpgradeButton({
|
||||
async function handleUpgrade() {
|
||||
const submittedUpgradeValues = await openUpgradeHelmModal(
|
||||
editableHelmRelease,
|
||||
filteredVersions
|
||||
versions
|
||||
);
|
||||
|
||||
if (submittedUpgradeValues) {
|
||||
|
||||
@@ -19,48 +19,39 @@ import { useHelmChartValues } from '../../queries/useHelmChartValues';
|
||||
|
||||
interface Props {
|
||||
onSubmit: OnSubmit<UpdateHelmReleasePayload>;
|
||||
payload: UpdateHelmReleasePayload;
|
||||
values: UpdateHelmReleasePayload;
|
||||
versions: ChartVersion[];
|
||||
chartName: string;
|
||||
repo: string;
|
||||
}
|
||||
|
||||
export function UpgradeHelmModal({
|
||||
payload,
|
||||
values,
|
||||
versions,
|
||||
onSubmit,
|
||||
chartName,
|
||||
repo,
|
||||
}: Props) {
|
||||
const versionOptions: Option<ChartVersion>[] = versions.map((version) => {
|
||||
const repo = payload.repo === version.Repo ? version.Repo : '';
|
||||
const isCurrentVersion =
|
||||
version.AppVersion === payload.appVersion &&
|
||||
version.Version === payload.version;
|
||||
|
||||
const label = `${repo}@${version.Version}${
|
||||
const isCurrentVersion = version.Version === values.version;
|
||||
const label = `${version.Repo}@${version.Version}${
|
||||
isCurrentVersion ? ' (current)' : ''
|
||||
}`;
|
||||
|
||||
return {
|
||||
repo,
|
||||
label,
|
||||
value: version,
|
||||
};
|
||||
});
|
||||
|
||||
const defaultVersion =
|
||||
versionOptions.find(
|
||||
(v) =>
|
||||
v.value.AppVersion === payload.appVersion &&
|
||||
v.value.Version === payload.version &&
|
||||
v.value.Repo === payload.repo
|
||||
)?.value || versionOptions[0]?.value;
|
||||
versionOptions.find((v) => v.value.Version === values.version)?.value ||
|
||||
versionOptions[0]?.value;
|
||||
const [version, setVersion] = useState<ChartVersion>(defaultVersion);
|
||||
const [userValues, setUserValues] = useState<string>(payload.values || '');
|
||||
const [userValues, setUserValues] = useState<string>(values.values || '');
|
||||
const [atomic, setAtomic] = useState<boolean>(true);
|
||||
|
||||
const chartValuesRefQuery = useHelmChartValues({
|
||||
chart: chartName,
|
||||
repo: version.Repo,
|
||||
repo,
|
||||
version: version.Version,
|
||||
});
|
||||
|
||||
@@ -84,7 +75,7 @@ export function UpgradeHelmModal({
|
||||
>
|
||||
<Input
|
||||
id="release-name-input"
|
||||
value={payload.name}
|
||||
value={values.name}
|
||||
readOnly
|
||||
disabled
|
||||
data-cy="helm-release-name-input"
|
||||
@@ -97,7 +88,7 @@ export function UpgradeHelmModal({
|
||||
>
|
||||
<Input
|
||||
id="namespace-input"
|
||||
value={payload.namespace}
|
||||
value={values.namespace}
|
||||
readOnly
|
||||
disabled
|
||||
data-cy="helm-namespace-input"
|
||||
@@ -151,10 +142,10 @@ export function UpgradeHelmModal({
|
||||
<Button
|
||||
onClick={() =>
|
||||
onSubmit({
|
||||
name: payload.name,
|
||||
name: values.name,
|
||||
values: userValues,
|
||||
namespace: payload.namespace,
|
||||
chart: payload.chart,
|
||||
namespace: values.namespace,
|
||||
chart: values.chart,
|
||||
repo: version.Repo,
|
||||
version: version.Version,
|
||||
atomic,
|
||||
@@ -174,12 +165,13 @@ export function UpgradeHelmModal({
|
||||
}
|
||||
|
||||
export async function openUpgradeHelmModal(
|
||||
payload: UpdateHelmReleasePayload,
|
||||
values: UpdateHelmReleasePayload,
|
||||
versions: ChartVersion[]
|
||||
) {
|
||||
return openModal(withReactQuery(withCurrentUser(UpgradeHelmModal)), {
|
||||
payload,
|
||||
values,
|
||||
versions,
|
||||
chartName: payload.chart,
|
||||
chartName: values.chart,
|
||||
repo: values.repo ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,15 +10,12 @@ interface HelmSearch {
|
||||
}
|
||||
|
||||
interface Entries {
|
||||
[key: string]: { version: string; appVersion: string }[];
|
||||
[key: string]: { version: string }[];
|
||||
}
|
||||
|
||||
export interface ChartVersion {
|
||||
Chart?: string;
|
||||
Repo: string;
|
||||
Label?: string;
|
||||
Version: string;
|
||||
AppVersion?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,10 +77,8 @@ async function getSearchHelmRepo(
|
||||
const versions = data.entries[chart];
|
||||
return (
|
||||
versions?.map((v) => ({
|
||||
Chart: chart,
|
||||
Repo: repo,
|
||||
Version: v.version,
|
||||
AppVersion: v.appVersion,
|
||||
})) ?? []
|
||||
);
|
||||
} catch (err) {
|
||||
|
||||
@@ -120,10 +120,9 @@ export interface InstallChartPayload {
|
||||
export interface UpdateHelmReleasePayload {
|
||||
namespace: string;
|
||||
values?: string;
|
||||
repo: string;
|
||||
repo?: string;
|
||||
name: string;
|
||||
chart: string;
|
||||
appVersion?: string;
|
||||
version?: string;
|
||||
atomic?: boolean;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Portainer.io",
|
||||
"name": "portainer",
|
||||
"homepage": "http://portainer.io",
|
||||
"version": "2.31.2",
|
||||
"version": "2.31.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:portainer/portainer.git"
|
||||
|
||||
@@ -36,8 +36,6 @@ type Release struct {
|
||||
Manifest string `json:"manifest,omitempty"`
|
||||
// Hooks are all of the hooks declared for this release.
|
||||
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 int `json:"version,omitempty"`
|
||||
// Namespace is the kubernetes namespace of the release.
|
||||
|
||||
@@ -90,17 +90,8 @@ func (hspm *HelmSDKPackageManager) SearchRepo(searchRepoOpts options.SearchRepoO
|
||||
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
|
||||
indexPath, err := downloadRepoIndex(repoURL.String(), repoSettings, repoName)
|
||||
indexPath, err := downloadRepoIndex(repoURL.String(), repoSettings, searchRepoOpts.Repo)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
@@ -172,8 +163,7 @@ func downloadRepoIndex(repoURLString string, repoSettings *cli.EnvSettings, repo
|
||||
// Create chart repository object
|
||||
rep, err := repo.NewChartRepository(
|
||||
&repo.Entry{
|
||||
Name: repoName,
|
||||
URL: repoURLString,
|
||||
URL: repoURLString,
|
||||
},
|
||||
getter.All(repoSettings),
|
||||
)
|
||||
|
||||
@@ -2,8 +2,7 @@ package sdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
@@ -33,33 +32,25 @@ func (hspm *HelmSDKPackageManager) Show(showOpts options.ShowOptions) ([]byte, e
|
||||
Str("output_format", string(showOpts.OutputFormat)).
|
||||
Msg("Showing chart information")
|
||||
|
||||
repoURL, err := parseRepoURL(showOpts.Repo)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("repo", showOpts.Repo).
|
||||
Err(err).
|
||||
Msg("Invalid repository URL")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Initialize action configuration (no namespace or cluster access needed)
|
||||
actionConfig := new(action.Configuration)
|
||||
err = hspm.initActionConfig(actionConfig, "", nil)
|
||||
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)
|
||||
}
|
||||
|
||||
// Create temporary directory for chart download
|
||||
tempDir, err := os.MkdirTemp("", "helm-show-*")
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to create temp directory")
|
||||
return nil, fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Create showClient action
|
||||
showClient, err := initShowClient(actionConfig, showOpts)
|
||||
if err != nil {
|
||||
@@ -77,12 +68,11 @@ func (hspm *HelmSDKPackageManager) Show(showOpts options.ShowOptions) ([]byte, e
|
||||
Str("repo", showOpts.Repo).
|
||||
Msg("Locating chart")
|
||||
|
||||
fullChartPath := fmt.Sprintf("%s/%s", repoName, showOpts.Chart)
|
||||
chartPath, err := showClient.ChartPathOptions.LocateChart(fullChartPath, hspm.settings)
|
||||
chartPath, err := showClient.ChartPathOptions.LocateChart(showOpts.Chart, hspm.settings)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", fullChartPath).
|
||||
Str("chart", showOpts.Chart).
|
||||
Str("repo", showOpts.Repo).
|
||||
Err(err).
|
||||
Msg("Failed to locate chart")
|
||||
@@ -114,10 +104,13 @@ func (hspm *HelmSDKPackageManager) Show(showOpts options.ShowOptions) ([]byte, e
|
||||
// and return the show client.
|
||||
func initShowClient(actionConfig *action.Configuration, showOpts options.ShowOptions) (*action.Show, error) {
|
||||
showClient := action.NewShowWithConfig(action.ShowAll, actionConfig)
|
||||
showClient.ChartPathOptions.Version = showOpts.Version
|
||||
showClient.ChartPathOptions.RepoURL = showOpts.Repo
|
||||
showClient.ChartPathOptions.Version = showOpts.Version // If version is "", it will use the latest version
|
||||
|
||||
// Set output type based on ShowOptions
|
||||
switch showOpts.OutputFormat {
|
||||
case options.ShowAll:
|
||||
showClient.OutputFormat = action.ShowAll
|
||||
case options.ShowChart:
|
||||
showClient.OutputFormat = action.ShowChart
|
||||
case options.ShowValues:
|
||||
@@ -134,26 +127,3 @@ func initShowClient(actionConfig *action.Configuration, showOpts options.ShowOpt
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user