Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat): ClusterProfile HelmCharts can be expressed as a template #800

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions controllers/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ var (
GetHelmReferenceResourceHash = getHelmReferenceResourceHash
GetHelmChartValuesHash = getHelmChartValuesHash
GetCredentialsAndCAFiles = getCredentialsAndCAFiles
GetInstantiatedChart = getInstantiatedChart

InstantiateTemplateValues = instantiateTemplateValues

Expand Down
87 changes: 64 additions & 23 deletions controllers/handlers_helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/gdexlab/go-render/render"
"github.com/go-logr/logr"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
Expand Down Expand Up @@ -441,18 +442,23 @@ func getHelmRefs(clusterSummary *configv1beta1.ClusterSummary) []configv1beta1.P
func handleCharts(ctx context.Context, clusterSummary *configv1beta1.ClusterSummary,
c, remoteClient client.Client, kubeconfig string, logger logr.Logger) error {

mgmtResources, err := collectTemplateResourceRefs(ctx, clusterSummary)
if err != nil {
return err
}

// Before any helm release, managed by this ClusterSummary, is deployed, update ClusterSummary
// Status. Order is important. If pod is restarted, it needs to rebuild internal state keeping
// track of which ClusterSummary was managing which helm release.
// Here only currently referenced helm releases are considered. If ClusterSummary was managing
// an helm release and it is not referencing it anymore, such entry will be removed from ClusterSummary.Status
// only after helm release is successfully undeployed.
clusterSummary, _, err := updateStatusForeferencedHelmReleases(ctx, c, clusterSummary, logger)
clusterSummary, _, err = updateStatusForeferencedHelmReleases(ctx, c, clusterSummary, mgmtResources, logger)
if err != nil {
return err
}

releaseReports, chartDeployed, deployError := walkChartsAndDeploy(ctx, c, clusterSummary, kubeconfig, logger)
releaseReports, chartDeployed, deployError := walkChartsAndDeploy(ctx, c, clusterSummary, kubeconfig, mgmtResources, logger)
// Even if there is a deployment error do not return just yet. Update various status and clean stale resources.

// If there was an helm release previous managed by this ClusterSummary and currently not referenced
Expand Down Expand Up @@ -503,12 +509,8 @@ func handleCharts(ctx context.Context, clusterSummary *configv1beta1.ClusterSumm
// walkChartsAndDeploy walks all referenced helm charts. Deploys (install or upgrade) any chart
// this clusterSummary is registered to manage.
func walkChartsAndDeploy(ctx context.Context, c client.Client, clusterSummary *configv1beta1.ClusterSummary,
kubeconfig string, logger logr.Logger) ([]configv1beta1.ReleaseReport, []configv1beta1.Chart, error) {

mgmtResources, err := collectTemplateResourceRefs(ctx, clusterSummary)
if err != nil {
return nil, nil, err
}
kubeconfig string, mgmtResources map[string]*unstructured.Unstructured, logger logr.Logger,
) ([]configv1beta1.ReleaseReport, []configv1beta1.Chart, error) {

chartManager, err := chartmanager.GetChartManagerInstance(ctx, c)
if err != nil {
Expand All @@ -520,16 +522,22 @@ func walkChartsAndDeploy(ctx context.Context, c client.Client, clusterSummary *c
chartDeployed := make([]configv1beta1.Chart, 0, len(clusterSummary.Spec.ClusterProfileSpec.HelmCharts))
for i := range clusterSummary.Spec.ClusterProfileSpec.HelmCharts {
currentChart := &clusterSummary.Spec.ClusterProfileSpec.HelmCharts[i]

instantiatedChart, err := getInstantiatedChart(ctx, clusterSummary, currentChart, mgmtResources, logger)
if err != nil {
return releaseReports, chartDeployed, err
}

// Eventual conflicts are already resolved before this method is called (in updateStatusForeferencedHelmReleases)
// So it is safe to call CanManageChart here
if !chartManager.CanManageChart(clusterSummary, currentChart) {
if !chartManager.CanManageChart(clusterSummary, instantiatedChart) {
var report *configv1beta1.ReleaseReport
report, err = createReportForUnmanagedHelmRelease(ctx, c, clusterSummary, currentChart, logger)
report, err = createReportForUnmanagedHelmRelease(ctx, c, clusterSummary, instantiatedChart, logger)
if err != nil {
return releaseReports, chartDeployed, err
}
releaseReports = append(releaseReports, *report)
conflictErrorMessage += generateConflictForHelmChart(ctx, clusterSummary, currentChart)
conflictErrorMessage += generateConflictForHelmChart(ctx, clusterSummary, instantiatedChart)
// error is reported above, in updateHelmChartStatus.
if clusterSummary.Spec.ClusterProfileSpec.ContinueOnConflict ||
clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun {
Expand All @@ -546,11 +554,11 @@ func walkChartsAndDeploy(ctx context.Context, c client.Client, clusterSummary *c

var report *configv1beta1.ReleaseReport
var currentRelease *releaseInfo
currentRelease, report, err = handleChart(ctx, clusterSummary, mgmtResources, currentChart, kubeconfig, logger)
currentRelease, report, err = handleChart(ctx, clusterSummary, mgmtResources, instantiatedChart, kubeconfig, logger)
if err != nil {
return releaseReports, chartDeployed, err
}
err = updateValueHashOnHelmChartSummary(ctx, currentChart, clusterSummary, logger)
err = updateValueHashOnHelmChartSummary(ctx, instantiatedChart, clusterSummary, logger)
if err != nil {
return releaseReports, chartDeployed, err
}
Expand All @@ -563,7 +571,7 @@ func walkChartsAndDeploy(ctx context.Context, c client.Client, clusterSummary *c
if currentRelease.Status == release.StatusDeployed.String() {
// Deployed chart is used for updating ClusterConfiguration. There is no ClusterConfiguration for mgmt cluster
chartDeployed = append(chartDeployed, configv1beta1.Chart{
RepoURL: currentChart.RepositoryURL,
RepoURL: instantiatedChart.RepositoryURL,
Namespace: currentRelease.ReleaseNamespace,
ReleaseName: currentRelease.ReleaseName,
ChartVersion: currentRelease.ChartVersion,
Expand Down Expand Up @@ -1586,7 +1594,8 @@ func undeployStaleReleases(ctx context.Context, c client.Client, clusterSummary
// allowed to manage.
// No action in DryRun mode.
func updateStatusForeferencedHelmReleases(ctx context.Context, c client.Client,
clusterSummary *configv1beta1.ClusterSummary, logger logr.Logger) (*configv1beta1.ClusterSummary, bool, error) {
clusterSummary *configv1beta1.ClusterSummary, mgmtResources map[string]*unstructured.Unstructured,
logger logr.Logger) (*configv1beta1.ClusterSummary, bool, error) {

// No-op in DryRun mode
if clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun {
Expand Down Expand Up @@ -1623,30 +1632,36 @@ func updateStatusForeferencedHelmReleases(ctx context.Context, c client.Client,
helmReleaseSummaries := make([]configv1beta1.HelmChartSummary, len(currentClusterSummary.Spec.ClusterProfileSpec.HelmCharts))
for i := range currentClusterSummary.Spec.ClusterProfileSpec.HelmCharts {
currentChart := &currentClusterSummary.Spec.ClusterProfileSpec.HelmCharts[i]

instantiatedChart, err := getInstantiatedChart(ctx, clusterSummary, currentChart, mgmtResources, logger)
if err != nil {
return err
}

var canManage bool
canManage, err = determineChartOwnership(ctx, c, clusterSummary, currentChart, logger)
canManage, err = determineChartOwnership(ctx, c, clusterSummary, instantiatedChart, logger)
if err != nil {
return err
}
if canManage {
helmReleaseSummaries[i] = configv1beta1.HelmChartSummary{
ReleaseName: currentChart.ReleaseName,
ReleaseNamespace: currentChart.ReleaseNamespace,
ReleaseName: instantiatedChart.ReleaseName,
ReleaseNamespace: instantiatedChart.ReleaseNamespace,
Status: configv1beta1.HelmChartStatusManaging,
ValuesHash: getValueHashFromHelmChartSummary(currentChart, clusterSummary), // if a value is currently stored, keep it.
ValuesHash: getValueHashFromHelmChartSummary(instantiatedChart, clusterSummary), // if a value is currently stored, keep it.
// after chart is deployed such value will be updated
}
currentlyReferenced[helmInfo(currentChart.ReleaseNamespace, currentChart.ReleaseName)] = true
currentlyReferenced[helmInfo(instantiatedChart.ReleaseNamespace, instantiatedChart.ReleaseName)] = true
} else {
var managerName string
managerName, err = chartManager.GetManagerForChart(currentClusterSummary.Spec.ClusterNamespace,
currentClusterSummary.Spec.ClusterName, currentClusterSummary.Spec.ClusterType, currentChart)
currentClusterSummary.Spec.ClusterName, currentClusterSummary.Spec.ClusterType, instantiatedChart)
if err != nil {
return err
}
helmReleaseSummaries[i] = configv1beta1.HelmChartSummary{
ReleaseName: currentChart.ReleaseName,
ReleaseNamespace: currentChart.ReleaseNamespace,
ReleaseName: instantiatedChart.ReleaseName,
ReleaseNamespace: instantiatedChart.ReleaseNamespace,
Status: configv1beta1.HelmChartStatusConflict,
ConflictMessage: fmt.Sprintf("ClusterSummary %s managing it", managerName),
}
Expand Down Expand Up @@ -2696,3 +2711,29 @@ func requeueAllOtherClusterSummaries(ctx context.Context, c client.Client,

return nil
}

func getInstantiatedChart(ctx context.Context, clusterSummary *configv1beta1.ClusterSummary,
currentChart *configv1beta1.HelmChart, mgmtResources map[string]*unstructured.Unstructured,
logger logr.Logger) (*configv1beta1.HelmChart, error) {

// Marshal the struct to YAML
jsonData, err := yaml.Marshal(*currentChart)
if err != nil {
return nil, err
}

instantiatedChartString, err := instantiateTemplateValues(ctx, getManagementClusterConfig(),
getManagementClusterClient(), clusterSummary.Spec.ClusterType, clusterSummary.Spec.ClusterNamespace,
clusterSummary.Spec.ClusterName, currentChart.ChartName, string(jsonData), mgmtResources, logger)
if err != nil {
return nil, err
}

var instantiatedChart configv1beta1.HelmChart
err = yaml.Unmarshal([]byte(instantiatedChartString), &instantiatedChart)
if err != nil {
return nil, err
}

return &instantiatedChart, nil
}
Loading