diff --git a/cmd/main.go b/cmd/main.go index c72bed4fd..4da306c2c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -39,6 +39,7 @@ import ( hmcmirantiscomv1alpha1 "github.com/Mirantis/hmc/api/v1alpha1" "github.com/Mirantis/hmc/internal/controller" + "github.com/Mirantis/hmc/internal/helm" "github.com/Mirantis/hmc/internal/telemetry" "github.com/Mirantis/hmc/internal/utils" hmcwebhook "github.com/Mirantis/hmc/internal/webhook" @@ -183,13 +184,15 @@ func main() { currentNamespace := utils.CurrentNamespace() templateReconciler := controller.TemplateReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - SystemNamespace: currentNamespace, - DefaultRegistryURL: defaultRegistryURL, - DefaultRepoType: determinedRepositoryType, - RegistryCredentialsSecret: registryCredentialsSecret, - InsecureRegistry: insecureRegistry, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + SystemNamespace: currentNamespace, + DefaultRegistryConfig: helm.DefaultRegistryConfig{ + URL: defaultRegistryURL, + RepoType: determinedRepositoryType, + CredentialsSecret: registryCredentialsSecret, + Insecure: insecureRegistry, + }, } if err = (&controller.ClusterTemplateReconciler{ @@ -261,6 +264,12 @@ func main() { CreateRelease: createRelease, HMCTemplatesChartName: hmcTemplatesChartName, SystemNamespace: currentNamespace, + DefaultRegistryConfig: helm.DefaultRegistryConfig{ + URL: defaultRegistryURL, + RepoType: determinedRepositoryType, + CredentialsSecret: registryCredentialsSecret, + Insecure: insecureRegistry, + }, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Release") os.Exit(1) diff --git a/internal/controller/release_controller.go b/internal/controller/release_controller.go index 452df0a5d..bb3af7bac 100644 --- a/internal/controller/release_controller.go +++ b/internal/controller/release_controller.go @@ -56,6 +56,8 @@ type ReleaseReconciler struct { HMCTemplatesChartName string SystemNamespace string + + DefaultRegistryConfig helm.DefaultRegistryConfig } func (r *ReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { @@ -150,8 +152,8 @@ func (r *ReleaseReconciler) ensureManagement(ctx context.Context) error { func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, req ctrl.Request) error { l := ctrl.LoggerFrom(ctx) - if !r.CreateRelease { - l.Info("Reconciling HMC Templates is skipped") + if initialReconcile(req) && !r.CreateRelease { + l.Info("Initial creation of HMC Release is skipped") return nil } name := utils.ReleaseNameFromVersion(build.Version) @@ -175,6 +177,14 @@ func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, req ctrl. } } + if initialReconcile(req) { + err := helm.ReconcileHelmRepository(ctx, r.Client, defaultRepoName, r.SystemNamespace, r.DefaultRegistryConfig.HelmRepositorySpec()) + if err != nil { + l.Error(err, "Failed to reconcile default HelmRepository", "namespace", r.SystemNamespace) + return err + } + } + helmChart := &sourcev1.HelmChart{ ObjectMeta: metav1.ObjectMeta{ Name: name, diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 6ea6f98db..da312028f 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -21,7 +21,6 @@ import ( "strings" helmcontrollerv2 "github.com/fluxcd/helm-controller/api/v2" - "github.com/fluxcd/pkg/apis/meta" sourcev1 "github.com/fluxcd/source-controller/api/v1" "helm.sh/helm/v3/pkg/chart" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -30,7 +29,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" hmc "github.com/Mirantis/hmc/api/v1alpha1" "github.com/Mirantis/hmc/internal/helm" @@ -46,14 +44,7 @@ type TemplateReconciler struct { Scheme *runtime.Scheme SystemNamespace string - // DefaultRepoType is the type specified by default in HelmRepository - // objects. Valid types are 'default' for http/https repositories, and - // 'oci' for OCI repositories. The RepositoryType is set in main based on - // the URI scheme of the DefaultRegistryURL. - DefaultRepoType string - DefaultRegistryURL string - RegistryCredentialsSecret string - InsecureRegistry bool + DefaultRegistryConfig helm.DefaultRegistryConfig downloadHelmChartFunc func(context.Context, *sourcev1.Artifact) (*chart.Chart, error) } @@ -148,7 +139,11 @@ func (r *TemplateReconciler) ReconcileTemplate(ctx context.Context, template Tem return ctrl.Result{}, err } if template.GetNamespace() == r.SystemNamespace || !templateManagedByHMC(template) { - err := r.reconcileDefaultHelmRepository(ctx, template.GetNamespace()) + namespace := template.GetNamespace() + if namespace == "" { + namespace = r.SystemNamespace + } + err := helm.ReconcileHelmRepository(ctx, r.Client, defaultRepoName, namespace, r.DefaultRegistryConfig.HelmRepositorySpec()) if err != nil { l.Error(err, "Failed to reconcile default HelmRepository", "namespace", template.GetNamespace()) return ctrl.Result{}, err @@ -271,45 +266,6 @@ func (r *TemplateReconciler) updateStatus(ctx context.Context, template Template return nil } -func (r *TemplateReconciler) reconcileDefaultHelmRepository(ctx context.Context, namespace string) error { - l := ctrl.LoggerFrom(ctx) - if namespace == "" { - namespace = r.SystemNamespace - } - helmRepo := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: defaultRepoName, - Namespace: namespace, - }, - } - operation, err := ctrl.CreateOrUpdate(ctx, r.Client, helmRepo, func() error { - if helmRepo.Labels == nil { - helmRepo.Labels = make(map[string]string) - } - - helmRepo.Labels[hmc.HMCManagedLabelKey] = hmc.HMCManagedLabelValue - helmRepo.Spec = sourcev1.HelmRepositorySpec{ - Type: r.DefaultRepoType, - URL: r.DefaultRegistryURL, - Interval: metav1.Duration{Duration: helm.DefaultReconcileInterval}, - Insecure: r.InsecureRegistry, - } - if r.RegistryCredentialsSecret != "" { - helmRepo.Spec.SecretRef = &meta.LocalObjectReference{ - Name: r.RegistryCredentialsSecret, - } - } - return nil - }) - if err != nil { - return err - } - if operation == controllerutil.OperationResultCreated || operation == controllerutil.OperationResultUpdated { - l.Info(fmt.Sprintf("Successfully %s %s/%s HelmRepository", operation, namespace, defaultRepoName)) - } - return nil -} - func (r *TemplateReconciler) reconcileHelmChart(ctx context.Context, template Template) (*sourcev1.HelmChart, error) { spec := template.GetSpec() namespace := template.GetNamespace() diff --git a/internal/helm/repo.go b/internal/helm/repo.go new file mode 100644 index 000000000..e6756a9da --- /dev/null +++ b/internal/helm/repo.go @@ -0,0 +1,83 @@ +// Copyright 2024 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package helm + +import ( + "context" + "fmt" + + "github.com/fluxcd/pkg/apis/meta" + sourcev1 "github.com/fluxcd/source-controller/api/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + hmc "github.com/Mirantis/hmc/api/v1alpha1" +) + +type DefaultRegistryConfig struct { + // RepoType is the type specified by default in HelmRepository + // objects. Valid types are 'default' for http/https repositories, and + // 'oci' for OCI repositories. The RepositoryType is set in main based on + // the URI scheme of the DefaultRegistryURL. + RepoType string + URL string + CredentialsSecret string + Insecure bool +} + +func (r *DefaultRegistryConfig) HelmRepositorySpec() sourcev1.HelmRepositorySpec { + return sourcev1.HelmRepositorySpec{ + Type: r.RepoType, + URL: r.URL, + Interval: metav1.Duration{Duration: DefaultReconcileInterval}, + Insecure: r.Insecure, + SecretRef: func() *meta.LocalObjectReference { + if r.CredentialsSecret != "" { + return &meta.LocalObjectReference{ + Name: r.CredentialsSecret, + } + } + return nil + }(), + } +} + +func ReconcileHelmRepository(ctx context.Context, cl client.Client, name, namespace string, spec sourcev1.HelmRepositorySpec) error { + l := ctrl.LoggerFrom(ctx) + helmRepo := &sourcev1.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + operation, err := ctrl.CreateOrUpdate(ctx, cl, helmRepo, func() error { + if helmRepo.Labels == nil { + helmRepo.Labels = make(map[string]string) + } + + helmRepo.Labels[hmc.HMCManagedLabelKey] = hmc.HMCManagedLabelValue + helmRepo.Spec = spec + return nil + }) + if err != nil { + return err + } + if operation == controllerutil.OperationResultCreated || operation == controllerutil.OperationResultUpdated { + l.Info(fmt.Sprintf("Successfully %s %s/%s HelmRepository", operation, namespace, name)) + } + return nil +}