diff --git a/api/v1beta1/clustersummary_types.go b/api/v1beta1/clustersummary_types.go index 860c28b0..42ab8749 100644 --- a/api/v1beta1/clustersummary_types.go +++ b/api/v1beta1/clustersummary_types.go @@ -114,6 +114,10 @@ type HelmChartSummary struct { // chart or there is a conflict // +optional ConflictMessage string `json:"conflictMessage,omitempty"` + + // +optional + // FailureMessage provides the specific error from the Helm engine for this release + FailureMessage *string `json:"failureMessage,omitempty"` } // ClusterSummarySpec defines the desired state of ClusterSummary diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 9241f874..f8d93c48 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -816,6 +816,11 @@ func (in *HelmChartSummary) DeepCopyInto(out *HelmChartSummary) { *out = make([]byte, len(*in)) copy(*out, *in) } + if in.FailureMessage != nil { + in, out := &in.FailureMessage, &out.FailureMessage + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartSummary. diff --git a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml index 83abc8f1..3afc53b9 100644 --- a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml +++ b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml @@ -1650,6 +1650,10 @@ spec: Status indicates whether ClusterSummary can manage the helm chart or there is a conflict type: string + failureMessage: + description: FailureMessage provides the specific error from + the Helm engine for this release + type: string releaseName: description: ReleaseName is the chart release minLength: 1 diff --git a/controllers/handlers_helm.go b/controllers/handlers_helm.go index 1d2163da..dcd3de9a 100644 --- a/controllers/handlers_helm.go +++ b/controllers/handlers_helm.go @@ -1007,8 +1007,9 @@ func walkChartsAndDeploy(ctx context.Context, c client.Client, clusterSummary *c var report *configv1beta1.ReleaseReport var currentRelease *releaseInfo - currentRelease, report, err = handleChart(ctx, clusterSummary, mgmtResources, instantiatedChart, kubeconfig, - isPullMode, logger) + currentRelease, report, err = handleChart(ctx, clusterSummary, mgmtResources, instantiatedChart, + kubeconfig, isPullMode, logger) + setHelmFailureMessageOnHelmChartSummary(clusterSummary, currentChart, err) if err != nil { if clusterSummary.Spec.ClusterProfileSpec.ContinueOnError { errorMsg += fmt.Sprintf("chart: %s, release: %s, %v\n", @@ -1018,7 +1019,8 @@ func walkChartsAndDeploy(ctx context.Context, c client.Client, clusterSummary *c return releaseReports, chartDeployed, err } - valueHash, err := updateValueHashOnHelmChartSummary(ctx, instantiatedChart, clusterSummary, mgmtResources, logger) + valueHash, err := updateValueHashOnHelmChartSummary(ctx, instantiatedChart, clusterSummary, mgmtResources, + logger) if err != nil { return releaseReports, chartDeployed, err } @@ -1032,10 +1034,12 @@ func walkChartsAndDeploy(ctx context.Context, c client.Client, clusterSummary *c valueHash, currentRelease.Status)) } else { logger.V(logs.LogInfo).Info(fmt.Sprintf("release %s/%s (version %s) status: %s", - currentRelease.ReleaseNamespace, currentRelease.ReleaseName, currentRelease.ChartVersion, currentRelease.Status)) + currentRelease.ReleaseNamespace, currentRelease.ReleaseName, currentRelease.ChartVersion, + currentRelease.Status)) } if currentRelease.Status == releasecommon.StatusDeployed.String() { - // Deployed chart is used for updating ClusterConfiguration. There is no ClusterConfiguration for mgmt cluster + // Deployed chart is used for updating ClusterConfiguration. There is no ClusterConfiguration + // for mgmt cluster chartDeployed = append(chartDeployed, configv1beta1.Chart{ RepoURL: instantiatedChart.RepositoryURL, Namespace: currentRelease.ReleaseNamespace, @@ -1065,6 +1069,59 @@ func walkChartsAndDeploy(ctx context.Context, c client.Client, clusterSummary *c return releaseReports, chartDeployed, nil } +func setHelmFailureMessageOnHelmChartSummary(clusterSummary *configv1beta1.ClusterSummary, + instantiatedChart *configv1beta1.HelmChart, err error) { + + for i := range clusterSummary.Status.HelmReleaseSummaries { + hr := &clusterSummary.Status.HelmReleaseSummaries[i] + if hr.ReleaseNamespace == instantiatedChart.ReleaseNamespace && + hr.ReleaseName == instantiatedChart.ReleaseName { + + if err == nil { + clusterSummary.Status.HelmReleaseSummaries[i].FailureMessage = nil + } else { + failureMessage := err.Error() + clusterSummary.Status.HelmReleaseSummaries[i].FailureMessage = &failureMessage + } + return + } + } +} + +// getFailureMessageFromHelmChartSummary returns the failureMessage stored for this chart +// in the ClusterSummary +func getFailureMessageFromHelmChartSummary(requestedChart *configv1beta1.HelmChart, + clusterSummary *configv1beta1.ClusterSummary) *string { + + for i := range clusterSummary.Status.HelmReleaseSummaries { + rs := &clusterSummary.Status.HelmReleaseSummaries[i] + if rs.ReleaseName == requestedChart.ReleaseName && + rs.ReleaseNamespace == requestedChart.ReleaseNamespace { + + return rs.FailureMessage + } + } + + return nil +} + +// getValueHashFromHelmChartSummary returns the valueHash stored for this chart +// in the ClusterSummary +func getValueHashFromHelmChartSummary(requestedChart *configv1beta1.HelmChart, + clusterSummary *configv1beta1.ClusterSummary) []byte { + + for i := range clusterSummary.Status.HelmReleaseSummaries { + rs := &clusterSummary.Status.HelmReleaseSummaries[i] + if rs.ReleaseName == requestedChart.ReleaseName && + rs.ReleaseNamespace == requestedChart.ReleaseNamespace { + + return rs.ValuesHash + } + } + + return nil +} + func generateConflictForHelmChart(ctx context.Context, clusterSummary *configv1beta1.ClusterSummary, instantiatedChart *configv1beta1.HelmChart) string { @@ -1553,10 +1610,10 @@ func installRelease(ctx context.Context, clusterSummary *configv1beta1.ClusterSu // This condition should never occur. A previous check ensures that only one // ClusterProfile/Profile can manage a Helm Chart with a given name in a // specific namespace within a managed cluster. If this code is reached, - // that check has already passed. Therefore, the "cannot re-use a name that + // that check has already passed. Therefore, the "cannot reuse a name that // is still in use" error should be impossible. // There is no constant defined in the helm library but this is an error seen more than once. - if err.Error() == "cannot re-use a name that is still in use" { + if strings.Contains(err.Error(), "cannot reuse a name that is still in use") { _, err = upgradeRelease(ctx, clusterSummary, settings, requestedChart, kubeconfig, registryOptions, values, mgmtResources, logger) return nil, err @@ -2529,6 +2586,7 @@ func updateStatusForReferencedHelmReleases(ctx context.Context, c client.Client, ReleaseName: instantiatedChart.ReleaseName, ReleaseNamespace: instantiatedChart.ReleaseNamespace, Status: configv1beta1.HelmChartStatusManaging, + FailureMessage: getFailureMessageFromHelmChartSummary(instantiatedChart, clusterSummary), ValuesHash: getValueHashFromHelmChartSummary(instantiatedChart, clusterSummary), // if a value is currently stored, keep it. // after chart is deployed such value will be updated } @@ -2596,19 +2654,6 @@ func updateStatusForNonReferencedHelmReleases(ctx context.Context, c client.Clie currentlyReferenced[helmInfo(instantiatedChart.ReleaseNamespace, instantiatedChart.ReleaseName)] = true } - helmReleaseSummaries := make([]configv1beta1.HelmChartSummary, 0, len(clusterSummary.Status.HelmReleaseSummaries)) - for i := range clusterSummary.Status.HelmReleaseSummaries { - summary := &clusterSummary.Status.HelmReleaseSummaries[i] - if _, ok := currentlyReferenced[helmInfo(summary.ReleaseNamespace, summary.ReleaseName)]; ok { - helmReleaseSummaries = append(helmReleaseSummaries, *summary) - } - } - - if len(helmReleaseSummaries) == len(clusterSummary.Status.HelmReleaseSummaries) { - // Nothing has changed - return clusterSummary, nil - } - currentClusterSummary := &configv1beta1.ClusterSummary{} err := c.Get(ctx, types.NamespacedName{Namespace: clusterSummary.Namespace, Name: clusterSummary.Name}, currentClusterSummary) @@ -2616,6 +2661,14 @@ func updateStatusForNonReferencedHelmReleases(ctx context.Context, c client.Clie return clusterSummary, err } + helmReleaseSummaries := make([]configv1beta1.HelmChartSummary, 0, len(currentClusterSummary.Status.HelmReleaseSummaries)) + for i := range currentClusterSummary.Status.HelmReleaseSummaries { + summary := ¤tClusterSummary.Status.HelmReleaseSummaries[i] + if _, ok := currentlyReferenced[helmInfo(summary.ReleaseNamespace, summary.ReleaseName)]; ok { + helmReleaseSummaries = append(helmReleaseSummaries, *summary) + } + } + currentClusterSummary.Status.HelmReleaseSummaries = helmReleaseSummaries err = c.Status().Update(ctx, currentClusterSummary) @@ -3813,23 +3866,6 @@ func updateValueHashOnHelmChartSummary(ctx context.Context, requestedChart *conf return helmChartValuesHash, err } -// getValueHashFromHelmChartSummary returns the valueHash stored for this chart -// in the ClusterSummary -func getValueHashFromHelmChartSummary(requestedChart *configv1beta1.HelmChart, - clusterSummary *configv1beta1.ClusterSummary) []byte { - - for i := range clusterSummary.Status.HelmReleaseSummaries { - rs := &clusterSummary.Status.HelmReleaseSummaries[i] - if rs.ReleaseName == requestedChart.ReleaseName && - rs.ReleaseNamespace == requestedChart.ReleaseNamespace { - - return rs.ValuesHash - } - } - - return nil -} - func getCredentialsAndCAFiles(ctx context.Context, c client.Client, clusterSummary *configv1beta1.ClusterSummary, requestedChart *configv1beta1.HelmChart) (credentialsPath, caPath string, err error) { diff --git a/go.mod b/go.mod index b4dfc59d..5e8528cb 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/onsi/ginkgo/v2 v2.28.1 github.com/onsi/gomega v1.39.1 github.com/pkg/errors v0.9.1 - github.com/projectsveltos/libsveltos v1.6.1 + github.com/projectsveltos/libsveltos v1.6.2-0.20260323171327-a0d7029ca880 github.com/prometheus/client_golang v1.23.2 github.com/robfig/cron v1.2.0 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index 77d7e2c9..ce78839c 100644 --- a/go.sum +++ b/go.sum @@ -278,8 +278,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= -github.com/projectsveltos/libsveltos v1.6.1 h1:+NRYGDWONyUfjX0/LKFtPwvK/eQhO/jz1yvcPBj2lVA= -github.com/projectsveltos/libsveltos v1.6.1/go.mod h1:Wi/ICx8bv1SGBuzIu6ZFEJAosauY3jA/wbIe1SCvnEs= +github.com/projectsveltos/libsveltos v1.6.2-0.20260323171327-a0d7029ca880 h1:PrrZB3Fw9WT7fQ2G7VmZKzd70gjBSLzWKeIHQnSssFM= +github.com/projectsveltos/libsveltos v1.6.2-0.20260323171327-a0d7029ca880/go.mod h1:j3RWlB9jVFbZasy89M2xkp7wqyy6p46oea++VbP3E3Y= github.com/projectsveltos/lua-utils/glua-json v0.0.0-20251212200258-2b3cdcb7c0f5 h1:khnc+994UszxZYu69J+R5FKiLA/Nk1JQj0EYAkwTWz0= github.com/projectsveltos/lua-utils/glua-json v0.0.0-20251212200258-2b3cdcb7c0f5/go.mod h1:yVL8KQFa9tmcxgwl9nwIMtKgtmIVC1zaFRSCfOwYvPY= github.com/projectsveltos/lua-utils/glua-runes v0.0.0-20251212200258-2b3cdcb7c0f5 h1:YbsebwRwTRhV8QacvEAdFqxcxHdeu7JTVtsBovbkgos= diff --git a/manifest/manifest.yaml b/manifest/manifest.yaml index 477f9682..643224a6 100644 --- a/manifest/manifest.yaml +++ b/manifest/manifest.yaml @@ -5783,6 +5783,10 @@ spec: Status indicates whether ClusterSummary can manage the helm chart or there is a conflict type: string + failureMessage: + description: FailureMessage provides the specific error from + the Helm engine for this release + type: string releaseName: description: ReleaseName is the chart release minLength: 1