From 04311ce91021cc86bd40fc72fa4eefbc9f76d0af Mon Sep 17 00:00:00 2001 From: Andrei Pavlov Date: Fri, 1 Nov 2024 11:06:19 +0700 Subject: [PATCH] Set Release ownerreferenes on ProviderTemplates Signed-off-by: Andrei Pavlov --- api/v1alpha1/common.go | 26 ++++++++-- api/v1alpha1/release_types.go | 9 ++++ internal/controller/release_controller.go | 2 +- internal/controller/suite_test.go | 2 + internal/controller/template_controller.go | 50 +++++++++++++++++++ .../controller/template_controller_test.go | 2 +- internal/utils/kube.go | 23 +++++++++ 7 files changed, 108 insertions(+), 6 deletions(-) diff --git a/api/v1alpha1/common.go b/api/v1alpha1/common.go index 3bbc56611..ddd02be9a 100644 --- a/api/v1alpha1/common.go +++ b/api/v1alpha1/common.go @@ -67,7 +67,11 @@ func SetupIndexers(ctx context.Context, mgr ctrl.Manager) error { return err } - if err := SetupReleaseIndexer(ctx, mgr); err != nil { + if err := SetupReleaseVersionIndexer(ctx, mgr); err != nil { + return err + } + + if err := SetupReleaseTemplatesIndexer(ctx, mgr); err != nil { return err } @@ -96,10 +100,10 @@ func ExtractTemplateName(rawObj client.Object) []string { return []string{cluster.Spec.Template} } -const VersionKey = ".spec.version" +const ReleaseVersionKey = ".spec.version" -func SetupReleaseIndexer(ctx context.Context, mgr ctrl.Manager) error { - return mgr.GetFieldIndexer().IndexField(ctx, &Release{}, VersionKey, ExtractReleaseVersion) +func SetupReleaseVersionIndexer(ctx context.Context, mgr ctrl.Manager) error { + return mgr.GetFieldIndexer().IndexField(ctx, &Release{}, ReleaseVersionKey, ExtractReleaseVersion) } func ExtractReleaseVersion(rawObj client.Object) []string { @@ -110,6 +114,20 @@ func ExtractReleaseVersion(rawObj client.Object) []string { return []string{release.Spec.Version} } +const ReleaseTemplatesKey = "releaseTemplates" + +func SetupReleaseTemplatesIndexer(ctx context.Context, mgr ctrl.Manager) error { + return mgr.GetFieldIndexer().IndexField(ctx, &Release{}, ReleaseTemplatesKey, ExtractReleaseTemplates) +} + +func ExtractReleaseTemplates(rawObj client.Object) []string { + release, ok := rawObj.(*Release) + if !ok { + return nil + } + return release.Templates() +} + const ServicesTemplateKey = ".spec.services[].Template" func SetupManagedClusterServicesIndexer(ctx context.Context, mgr ctrl.Manager) error { diff --git a/api/v1alpha1/release_types.go b/api/v1alpha1/release_types.go index 83e11b78e..a3f23b5e5 100644 --- a/api/v1alpha1/release_types.go +++ b/api/v1alpha1/release_types.go @@ -57,6 +57,15 @@ func (in *Release) ProviderTemplate(name string) string { return "" } +func (in *Release) Templates() []string { + templates := make([]string, 0, len(in.Spec.Providers)+2) + templates = append(templates, in.Spec.HMC.Template, in.Spec.CAPI.Template) + for _, p := range in.Spec.Providers { + templates = append(templates, p.Template) + } + return templates +} + // ReleaseStatus defines the observed state of Release type ReleaseStatus struct { // Conditions contains details for the current state of the Release diff --git a/internal/controller/release_controller.go b/internal/controller/release_controller.go index 0ff94cb18..d54459646 100644 --- a/internal/controller/release_controller.go +++ b/internal/controller/release_controller.go @@ -284,7 +284,7 @@ func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, releaseNa func (r *ReleaseReconciler) getCurrentReleaseName(ctx context.Context) (string, error) { releases := &hmc.ReleaseList{} listOptions := client.ListOptions{ - FieldSelector: fields.SelectorFromSet(fields.Set{hmc.VersionKey: build.Version}), + FieldSelector: fields.SelectorFromSet(fields.Set{hmc.ReleaseVersionKey: build.Version}), } if err := r.Client.List(ctx, releases, &listOptions); err != nil { return "", err diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index b7dbdbc40..12e321f03 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -58,6 +58,7 @@ const ( var ( cfg *rest.Config k8sClient client.Client + mgrClient client.Client testEnv *envtest.Environment ctx context.Context cancel context.CancelFunc @@ -135,6 +136,7 @@ var _ = BeforeSuite(func() { Metrics: metricsserver.Options{BindAddress: "0"}, }) Expect(err).NotTo(HaveOccurred()) + mgrClient = mgr.GetClient() err = hmcmirantiscomv1alpha1.SetupIndexers(ctx, mgr) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index ebe3d86be..edea1c72e 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -29,13 +29,17 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" hmc "github.com/Mirantis/hmc/api/v1alpha1" "github.com/Mirantis/hmc/internal/helm" + "github.com/Mirantis/hmc/internal/utils" ) const ( @@ -131,10 +135,35 @@ func (r *ProviderTemplateReconciler) Reconcile(ctx context.Context, req ctrl.Req l.Error(err, "Failed to get ProviderTemplate") return ctrl.Result{}, err } + changed, err := r.setReleaseOwnership(ctx, providerTemplate) + if err != nil { + l.Error(err, "Failed to set OwnerReferences") + return ctrl.Result{}, err + } + if changed { + l.Info("Updating OwnerReferences with associated Releases") + return ctrl.Result{}, r.Update(ctx, providerTemplate) + } return r.ReconcileTemplate(ctx, providerTemplate) } +func (r *ProviderTemplateReconciler) setReleaseOwnership(ctx context.Context, providerTemplate *hmc.ProviderTemplate) (changed bool, err error) { + releases := &hmc.ReleaseList{} + err = r.Client.List(ctx, releases, + client.MatchingFields{hmc.ReleaseTemplatesKey: providerTemplate.Name}, + ) + if err != nil { + return changed, fmt.Errorf("failed to get associated releases: %w", err) + } + for _, release := range releases.Items { + if utils.AddOwnerReference(providerTemplate, &release) { + changed = true + } + } + return changed, nil +} + type templateCommon interface { client.Object GetHelmSpec() *hmc.HelmSpec @@ -413,5 +442,26 @@ func (r *ServiceTemplateReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *ProviderTemplateReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&hmc.ProviderTemplate{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). + Watches(&hmc.Release{}, + handler.EnqueueRequestsFromMapFunc(func(_ context.Context, o client.Object) []ctrl.Request { + release, ok := o.(*hmc.Release) + if !ok { + return nil + } + templates := release.Templates() + requests := make([]ctrl.Request, 0, len(templates)) + for _, template := range templates { + requests = append(requests, ctrl.Request{ + NamespacedName: types.NamespacedName{Name: template}, + }) + } + return requests + }), + builder.WithPredicates(predicate.Funcs{ + UpdateFunc: func(event.UpdateEvent) bool { return false }, + GenericFunc: func(event.GenericEvent) bool { return false }, + DeleteFunc: func(event.DeleteEvent) bool { return false }, + }), + ). Complete(r) } diff --git a/internal/controller/template_controller_test.go b/internal/controller/template_controller_test.go index 1669ff343..373046f72 100644 --- a/internal/controller/template_controller_test.go +++ b/internal/controller/template_controller_test.go @@ -177,7 +177,7 @@ var _ = Describe("Template Controller", func() { It("should successfully reconcile the resource", func() { templateReconciler := TemplateReconciler{ - Client: k8sClient, + Client: mgrClient, downloadHelmChartFunc: fakeDownloadHelmChartFunc, } By("Reconciling the ClusterTemplate resource") diff --git a/internal/utils/kube.go b/internal/utils/kube.go index 2ad6bbdd5..dbda4ad2d 100644 --- a/internal/utils/kube.go +++ b/internal/utils/kube.go @@ -60,3 +60,26 @@ func CurrentNamespace() string { } return DefaultSystemNamespace } + +func AddOwnerReference(dependent, owner client.Object) (changed bool) { + ownerRefs := dependent.GetOwnerReferences() + if ownerRefs == nil { + ownerRefs = []metav1.OwnerReference{} + } + for _, ref := range ownerRefs { + if ref.UID == owner.GetUID() { + return false + } + } + apiVersion, kind := owner.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind() + ownerRefs = append(ownerRefs, + metav1.OwnerReference{ + APIVersion: apiVersion, + Kind: kind, + Name: owner.GetName(), + UID: owner.GetUID(), + }, + ) + dependent.SetOwnerReferences(ownerRefs) + return true +}