Skip to content

Commit

Permalink
Extend helm chart configuration for Templates
Browse files Browse the repository at this point in the history
Closes #615
  • Loading branch information
eromanova committed Dec 10, 2024
1 parent 5787bc9 commit 1f5dc87
Show file tree
Hide file tree
Showing 34 changed files with 705 additions and 121 deletions.
19 changes: 13 additions & 6 deletions api/v1alpha1/templates_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"

helmcontrollerv2 "github.com/fluxcd/helm-controller/api/v2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)

Expand All @@ -30,27 +31,33 @@ const (
ChartAnnotationProviderName = "cluster.x-k8s.io/provider"

chartAnnoCAPIPrefix = "cluster.x-k8s.io/"

DefaultRepoName = "hmc-templates"
)

// +kubebuilder:validation:XValidation:rule="(has(self.chartName) && !has(self.chartRef)) || (!has(self.chartName) && has(self.chartRef))", message="either chartName or chartRef must be set"
var DefaultSourceRef = sourcev1.LocalHelmChartSourceReference{
Kind: sourcev1.HelmRepositoryKind,
Name: DefaultRepoName,
}

// +kubebuilder:validation:XValidation:rule="(has(self.chartSpec) && !has(self.chartRef)) || (!has(self.chartSpec) && has(self.chartRef))", message="either chartSpec or chartRef must be set"

// HelmSpec references a Helm chart representing the HMC template
type HelmSpec struct {
// ChartSpec defines the desired state of the HelmChart to be created by the controller
ChartSpec *sourcev1.HelmChartSpec `json:"chartSpec,omitempty"`

// ChartRef is a reference to a source controller resource containing the
// Helm chart representing the template.
ChartRef *helmcontrollerv2.CrossNamespaceSourceReference `json:"chartRef,omitempty"`
// ChartName is a name of a Helm chart representing the template in the HMC repository.
ChartName string `json:"chartName,omitempty"`
// ChartVersion is a version of a Helm chart representing the template in the HMC repository.
ChartVersion string `json:"chartVersion,omitempty"`
}

func (s *HelmSpec) String() string {
if s.ChartRef != nil {
return s.ChartRef.Namespace + "/" + s.ChartRef.Name + ", Kind=" + s.ChartRef.Kind
}

return s.ChartName + ": " + s.ChartVersion
return s.ChartSpec.Chart + ": " + s.ChartSpec.Version
}

// TemplateStatusCommon defines the observed state of Template common for all Template types
Expand Down
6 changes: 6 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions hack/templates.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ metadata:
helm.sh/resource-policy: keep
spec:
helm:
chartName: $name
chartVersion: $version
chartSpec:
chart: $name
version: $version
interval: 10m0s
sourceRef:
kind: HelmRepository
name: hmc-templates
EOF

echo "Generated $TEMPLATES_OUTPUT_DIR/$name.yaml"
Expand Down
10 changes: 8 additions & 2 deletions internal/controller/managedcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ func (r *ManagedClusterReconciler) updateCluster(ctx context.Context, mc *hmc.Ma
return ctrl.Result{}, fmt.Errorf("error setting identity values: %w", err)
}

hr, _, err := helm.ReconcileHelmRelease(ctx, r.Client, mc.Name, mc.Namespace, helm.ReconcileHelmReleaseOpts{
hrReconcileOpts := helm.ReconcileHelmReleaseOpts{
Values: helmValues,
OwnerReference: &metav1.OwnerReference{
APIVersion: hmc.GroupVersion.String(),
Expand All @@ -311,7 +311,13 @@ func (r *ManagedClusterReconciler) updateCluster(ctx context.Context, mc *hmc.Ma
UID: mc.UID,
},
ChartRef: clusterTpl.Status.ChartRef,
})
}
reconcileInterval := clusterTpl.Spec.Helm.ChartSpec.Interval.Duration
if reconcileInterval != 0 {
hrReconcileOpts.ReconcileInterval = &reconcileInterval
}

hr, _, err := helm.ReconcileHelmRelease(ctx, r.Client, mc.Name, mc.Namespace, hrReconcileOpts)
if err != nil {
apimeta.SetStatusCondition(mc.GetConditions(), metav1.Condition{
Type: hmc.HelmReleaseReadyCondition,
Expand Down
10 changes: 8 additions & 2 deletions internal/controller/management_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,19 @@ func (r *ManagementReconciler) Update(ctx context.Context, management *hmc.Manag
continue
}

if _, _, err := helm.ReconcileHelmRelease(ctx, r.Client, component.helmReleaseName, r.SystemNamespace, helm.ReconcileHelmReleaseOpts{
hrReconcileOpts := helm.ReconcileHelmReleaseOpts{
Values: component.Config,
ChartRef: template.Status.ChartRef,
DependsOn: component.dependsOn,
TargetNamespace: component.targetNamespace,
CreateNamespace: component.createNamespace,
}); err != nil {
}
reconcileInterval := template.Spec.Helm.ChartSpec.Interval.Duration
if reconcileInterval != 0 {
hrReconcileOpts.ReconcileInterval = &reconcileInterval
}

if _, _, err := helm.ReconcileHelmRelease(ctx, r.Client, component.helmReleaseName, r.SystemNamespace, hrReconcileOpts); err != nil {
errMsg := fmt.Sprintf("Failed to reconcile HelmRelease %s/%s: %s", r.SystemNamespace, component.helmReleaseName, err)
updateComponentsStatus(statusAccumulator, component, nil, errMsg)
errs = errors.Join(errs, errors.New(errMsg))
Expand Down
6 changes: 4 additions & 2 deletions internal/controller/management_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ var _ = Describe("Management Controller", func() {
},
Spec: hmcmirantiscomv1alpha1.ProviderTemplateSpec{
Helm: hmcmirantiscomv1alpha1.HelmSpec{
ChartName: "required-chart",
ChartVersion: "required-version",
ChartSpec: &sourcev1.HelmChartSpec{
Chart: "required-chart",
Version: "required-version",
},
},
},
}
Expand Down
6 changes: 4 additions & 2 deletions internal/controller/multiclusterservice_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ var _ = Describe("MultiClusterService Controller", func() {
},
Spec: hmc.ServiceTemplateSpec{
Helm: hmc.HelmSpec{
ChartName: helmChartName,
ChartVersion: helmChartVersion,
ChartSpec: &sourcev1.HelmChartSpec{
Chart: helmChartName,
Version: helmChartVersion,
},
},
},
}
Expand Down
13 changes: 5 additions & 8 deletions internal/controller/release_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, releaseNa
if releaseName == "" {
releaseName = utils.ReleaseNameFromVersion(build.Version)
releaseVersion = build.Version
err := helm.ReconcileHelmRepository(ctx, r.Client, defaultRepoName, r.SystemNamespace, r.DefaultRegistryConfig.HelmRepositorySpec())
err := helm.ReconcileHelmRepository(ctx, r.Client, hmc.DefaultRepoName, r.SystemNamespace, r.DefaultRegistryConfig.HelmRepositorySpec())
if err != nil {
l.Error(err, "Failed to reconcile default HelmRepository", "namespace", r.SystemNamespace)
return err
Expand Down Expand Up @@ -280,13 +280,10 @@ func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, releaseNa
}
helmChart.Labels[hmc.HMCManagedLabelKey] = hmc.HMCManagedLabelValue
helmChart.Spec = sourcev1.HelmChartSpec{
Chart: r.HMCTemplatesChartName,
Version: releaseVersion,
SourceRef: sourcev1.LocalHelmChartSourceReference{
Kind: sourcev1.HelmRepositoryKind,
Name: defaultRepoName,
},
Interval: metav1.Duration{Duration: helm.DefaultReconcileInterval},
Chart: r.HMCTemplatesChartName,
Version: releaseVersion,
SourceRef: hmc.DefaultSourceRef,
Interval: metav1.Duration{Duration: helm.DefaultReconcileInterval},
}
return nil
})
Expand Down
32 changes: 6 additions & 26 deletions internal/controller/template_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@ import (
"github.com/Mirantis/hmc/internal/utils"
)

const (
defaultRepoName = "hmc-templates"

defaultRequeueTime = 1 * time.Minute
)
const defaultRequeueTime = 1 * time.Minute

// TemplateReconciler reconciles a *Template object
type TemplateReconciler struct {
Expand Down Expand Up @@ -184,8 +180,8 @@ func (r *TemplateReconciler) ReconcileTemplate(ctx context.Context, template tem
return ctrl.Result{}, err
}
} else {
if helmSpec.ChartName == "" {
err := errors.New("neither chartName nor chartRef is set")
if helmSpec.ChartSpec == nil {
err := errors.New("neither chartSpec nor chartRef is set")
l.Error(err, "invalid helm chart reference")
return ctrl.Result{}, err
}
Expand All @@ -194,7 +190,7 @@ func (r *TemplateReconciler) ReconcileTemplate(ctx context.Context, template tem
if namespace == "" {
namespace = r.SystemNamespace
}
err := helm.ReconcileHelmRepository(ctx, r.Client, defaultRepoName, namespace, r.DefaultRegistryConfig.HelmRepositorySpec())
err := helm.ReconcileHelmRepository(ctx, r.Client, hmc.DefaultRepoName, namespace, r.DefaultRegistryConfig.HelmRepositorySpec())
if err != nil {
l.Error(err, "Failed to reconcile default HelmRepository")
return ctrl.Result{}, err
Expand Down Expand Up @@ -314,25 +310,9 @@ func (r *TemplateReconciler) reconcileHelmChart(ctx context.Context, template te
}

helmChart.Labels[hmc.HMCManagedLabelKey] = hmc.HMCManagedLabelValue
helmChart.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: hmc.GroupVersion.String(),
Kind: template.GetObjectKind().GroupVersionKind().Kind,
Name: template.GetName(),
UID: template.GetUID(),
},
}

helmChart.Spec = sourcev1.HelmChartSpec{
Chart: helmSpec.ChartName,
Version: helmSpec.ChartVersion,
SourceRef: sourcev1.LocalHelmChartSourceReference{
Kind: sourcev1.HelmRepositoryKind,
Name: defaultRepoName,
},
Interval: metav1.Duration{Duration: helm.DefaultReconcileInterval},
}
utils.AddOwnerReference(helmChart, template)

helmChart.Spec = *helmSpec.ChartSpec
return nil
})

Expand Down
3 changes: 2 additions & 1 deletion internal/controller/templatechain_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"time"

helmcontrollerv2 "github.com/fluxcd/helm-controller/api/v2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -52,7 +53,7 @@ var _ = Describe("Template Chain Controller", func() {
}

chartName := "test"
templateHelmSpec := hmcmirantiscomv1alpha1.HelmSpec{ChartName: chartName}
templateHelmSpec := hmcmirantiscomv1alpha1.HelmSpec{ChartSpec: &sourcev1.HelmChartSpec{Chart: chartName}}
chartRef := &helmcontrollerv2.CrossNamespaceSourceReference{
Kind: "HelmChart",
Namespace: utils.DefaultSystemNamespace,
Expand Down
2 changes: 1 addition & 1 deletion internal/telemetry/tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (t *Tracker) trackManagedClusterHeartbeat(ctx context.Context) error {
string(managedCluster.UID),
clusterID,
managedCluster.Spec.Template,
template.Spec.Helm.ChartVersion,
template.Spec.Helm.ChartSpec.Version,
template.Status.Providers,
)
if err != nil {
Expand Down
35 changes: 32 additions & 3 deletions internal/webhook/template_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"github.com/Mirantis/hmc/api/v1alpha1"
"github.com/Mirantis/hmc/internal/helm"
)

var errTemplateDeletionForbidden = errors.New("template deletion is forbidden")
Expand Down Expand Up @@ -95,7 +96,12 @@ func (v *ClusterTemplateValidator) ValidateDelete(ctx context.Context, obj runti
}

// Default implements webhook.Defaulter so a webhook will be registered for the type.
func (*ClusterTemplateValidator) Default(context.Context, runtime.Object) error {
func (*ClusterTemplateValidator) Default(_ context.Context, obj runtime.Object) error {
template, ok := obj.(*v1alpha1.ClusterTemplate)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected ClusterTemplate but got a %T", obj))
}
setHelmChartDefaults(template.GetHelmSpec())
return nil
}

Expand Down Expand Up @@ -168,7 +174,12 @@ func (v *ServiceTemplateValidator) ValidateDelete(ctx context.Context, obj runti
}

// Default implements webhook.Defaulter so a webhook will be registered for the type.
func (*ServiceTemplateValidator) Default(_ context.Context, _ runtime.Object) error {
func (*ServiceTemplateValidator) Default(_ context.Context, obj runtime.Object) error {
template, ok := obj.(*v1alpha1.ServiceTemplate)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected ServiceTemplate but got a %T", obj))
}
setHelmChartDefaults(template.GetHelmSpec())
return nil
}

Expand Down Expand Up @@ -229,7 +240,12 @@ func (v *ProviderTemplateValidator) ValidateDelete(ctx context.Context, obj runt
}

// Default implements webhook.Defaulter so a webhook will be registered for the type.
func (*ProviderTemplateValidator) Default(_ context.Context, _ runtime.Object) error {
func (*ProviderTemplateValidator) Default(_ context.Context, obj runtime.Object) error {
template, ok := obj.(*v1alpha1.ProviderTemplate)
if !ok {
return apierrors.NewBadRequest(fmt.Sprintf("expected ProviderTemplate but got a %T", obj))
}
setHelmChartDefaults(template.GetHelmSpec())
return nil
}

Expand Down Expand Up @@ -267,3 +283,16 @@ func getOwnersWithKind(template client.Object, kind string) []string {
}
return owners
}

func setHelmChartDefaults(helmSpec *v1alpha1.HelmSpec) {
if helmSpec == nil || helmSpec.ChartSpec == nil {
return
}
chartSpec := helmSpec.ChartSpec
if chartSpec.SourceRef.Name == "" && chartSpec.SourceRef.Kind == "" {
chartSpec.SourceRef = v1alpha1.DefaultSourceRef
}
if chartSpec.Interval.Duration == 0 {
chartSpec.Interval.Duration = helm.DefaultReconcileInterval
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ metadata:
helm.sh/resource-policy: keep
spec:
helm:
chartName: aws-eks
chartVersion: 0.0.2
chartSpec:
chart: aws-eks
version: 0.0.2
interval: 10m0s
sourceRef:
kind: HelmRepository
name: hmc-templates
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ metadata:
helm.sh/resource-policy: keep
spec:
helm:
chartName: aws-hosted-cp
chartVersion: 0.0.3
chartSpec:
chart: aws-hosted-cp
version: 0.0.3
interval: 10m0s
sourceRef:
kind: HelmRepository
name: hmc-templates
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ metadata:
helm.sh/resource-policy: keep
spec:
helm:
chartName: aws-standalone-cp
chartVersion: 0.0.3
chartSpec:
chart: aws-standalone-cp
version: 0.0.3
interval: 10m0s
sourceRef:
kind: HelmRepository
name: hmc-templates
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ metadata:
helm.sh/resource-policy: keep
spec:
helm:
chartName: azure-hosted-cp
chartVersion: 0.0.3
chartSpec:
chart: azure-hosted-cp
version: 0.0.3
interval: 10m0s
sourceRef:
kind: HelmRepository
name: hmc-templates
Loading

0 comments on commit 1f5dc87

Please sign in to comment.