From c40e57224df0c6628b061236e7c9239b23b0f570 Mon Sep 17 00:00:00 2001 From: Gianluca Mardente Date: Fri, 16 Jan 2026 22:00:13 +0100 Subject: [PATCH 1/2] (opt) Classifier with conflicts When a Classifier attempts to manage a label on a matching cluster already claimed by another instance, the reconciliation will now requeue every minute to retry. This change replaces the previous behavior of requeueing Classifier instances whenever the set of matching clusters changed. --- controllers/classifier_controller.go | 39 ++++++--- controllers/classifier_predicates.go | 70 +-------------- controllers/classifier_predicates_test.go | 85 ------------------- controllers/classifier_transformations.go | 40 --------- .../classifier_transformations_test.go | 33 ------- controllers/export_test.go | 1 - 6 files changed, 29 insertions(+), 239 deletions(-) diff --git a/controllers/classifier_controller.go b/controllers/classifier_controller.go index 3b15833..1b7565f 100644 --- a/controllers/classifier_controller.go +++ b/controllers/classifier_controller.go @@ -64,14 +64,17 @@ const ( ) const ( - // deleteRequeueAfter is how long to wait before checking again to see if the cluster still has - // children during deletion. + // deleteRequeueAfter is how long to wait before checking again to see if the classifier + // can be deleted deleteRequeueAfter = 10 * time.Second - // normalRequeueAfter is how long to wait before checking again to see if the cluster can be moved - // to ready after or workload features (for instance ingress or reporter) have failed + // normalRequeueAfter is how long to wait before reconciling the classifier instance normalRequeueAfter = 10 * time.Second + // conflictRequeueAfter is how long to wait before retrying when a label + // management conflict is detected with another classifier instance. + conflictRequeueAfter = time.Minute + controlplaneendpoint = "controlplaneendpoint-key" configurationHash = "configurationHash" @@ -272,19 +275,19 @@ func (r *ClassifierReconciler) reconcileNormal( err := r.updateMatchingClustersAndRegistrations(ctx, classifierScope, logger) if err != nil { logger.V(logs.LogDebug).Info("failed to update matchingClusterRefs") - return reconcile.Result{}, err + return reconcile.Result{Requeue: true, RequeueAfter: normalRequeueAfter}, nil } err = r.updateLabelsOnMatchingClusters(ctx, classifierScope, logger) if err != nil { logger.V(logs.LogDebug).Info("failed to update cluster labels") - return reconcile.Result{}, err + return reconcile.Result{Requeue: true, RequeueAfter: normalRequeueAfter}, nil } err = r.updateClusterInfo(ctx, classifierScope) if err != nil { logger.V(logs.LogDebug).Info("failed to update clusterInfo") - return reconcile.Result{}, err + return reconcile.Result{Requeue: true, RequeueAfter: normalRequeueAfter}, nil } r.updateMaps(classifierScope) @@ -296,6 +299,10 @@ func (r *ClassifierReconciler) reconcileNormal( return reconcile.Result{Requeue: true, RequeueAfter: normalRequeueAfter}, nil } + if hasUnManagedLabels(classifierScope.Classifier) { + return reconcile.Result{Requeue: true, RequeueAfter: conflictRequeueAfter}, nil + } + logger.V(logs.LogDebug).Info("Reconcile success") return reconcile.Result{}, nil } @@ -319,12 +326,6 @@ func (r *ClassifierReconciler) SetupWithManager(ctx context.Context, ClassifierReportPredicate(mgr.GetLogger().WithValues("predicate", "classifierreportpredicate")), ), ). - Watches(&libsveltosv1beta1.Classifier{}, - handler.EnqueueRequestsFromMapFunc(r.requeueClassifierForClassifier), - builder.WithPredicates( - OtherClassifierPredicate(mgr.GetLogger().WithValues("predicate", "otherClassifiepredicate")), - ), - ). Watches(&libsveltosv1beta1.SveltosCluster{}, handler.EnqueueRequestsFromMapFunc(r.requeueClassifierForSveltosCluster), builder.WithPredicates( @@ -754,3 +755,15 @@ func startSveltosAgentInMgmtCluster(o deployer.Options) bool { return runInMgtmCluster } + +// hasUnManagedLabels returns true if there is a conflict where this classifier +// wants to manage labels that are already being managed by another instance. +func hasUnManagedLabels(classifier *libsveltosv1beta1.Classifier) bool { + for i := range classifier.Status.MachingClusterStatuses { + if len(classifier.Status.MachingClusterStatuses[i].UnManagedLabels) > 0 { + return true + } + } + + return false +} diff --git a/controllers/classifier_predicates.go b/controllers/classifier_predicates.go index 98982bd..1973297 100644 --- a/controllers/classifier_predicates.go +++ b/controllers/classifier_predicates.go @@ -56,7 +56,7 @@ func (p ClassifierPredicate) Update(e event.UpdateEvent) bool { if !reflect.DeepEqual(oldClassifier.Spec, newClassifier.Spec) { log.V(logs.LogVerbose).Info( - "ClusterSummary Spec changed. Will attempt to reconcile ClusterSummary.", + "Classifier Spec changed. Will attempt to reconcile ClusterSummary.", ) return true } @@ -201,7 +201,7 @@ func ConfigMapPredicates(logger logr.Logger) predicate.Funcs { } log.V(logs.LogVerbose).Info( - "ConfigMap did match expected conditions. Will attempt to reconcile associated Classifiers.") + "ConfigMap did not match expected conditions. Will not attempt to reconcile associated Classifiers.") return false }, DeleteFunc: func(e event.DeleteEvent) bool { @@ -217,7 +217,7 @@ func ConfigMapPredicates(logger logr.Logger) predicate.Funcs { } log.V(logs.LogVerbose).Info( - "ConfigMap did match expected conditions. Will attempt to reconcile associated Classifiers.") + "ConfigMap did not match expected conditions. Will not attempt to reconcile associated Classifiers.") return false }, GenericFunc: func(e event.GenericEvent) bool { @@ -296,67 +296,3 @@ func ClassifierReportPredicate(logger logr.Logger) predicate.Funcs { }, } } - -// OtherClassifierPredicate predicates for Classifier. ClassifierReconciler watches Classifier events -// and react to those by reconciling itself based on following predicates -func OtherClassifierPredicate(logger logr.Logger) predicate.Funcs { - return predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - newClassifer := e.ObjectNew.(*libsveltosv1beta1.Classifier) - oldClassifier := e.ObjectOld.(*libsveltosv1beta1.Classifier) - log := logger.WithValues("predicate", "updateEvent", - "name", newClassifer.Name, - ) - - if oldClassifier == nil { - log.V(logs.LogVerbose).Info("Old Classifier is nil. Reconcile Classifier") - return true - } - - // return true if Classifier.Status has changed - if !reflect.DeepEqual(oldClassifier.Status.MachingClusterStatuses, newClassifer.Status.MachingClusterStatuses) { - log.V(logs.LogVerbose).Info( - "Classifier Status.MachingClusterStatuses changed. Will attempt to reconcile associated Classifiers.") - return true - } - - // otherwise, return false - log.V(logs.LogVerbose).Info( - "ClassifierReport did not match expected conditions. Will not attempt to reconcile associated Classifiers.") - return false - }, - CreateFunc: func(e event.CreateEvent) bool { - classifier := e.Object.(*libsveltosv1beta1.Classifier) - log := logger.WithValues("predicate", "createEvent", - "namespace", classifier.Namespace, - "name", classifier.Name, - ) - - log.V(logs.LogVerbose).Info( - "Classifier did not match expected conditions. Will attempt to reconcile associated Classifiers.") - return false - }, - DeleteFunc: func(e event.DeleteEvent) bool { - classifier := e.Object.(*libsveltosv1beta1.Classifier) - log := logger.WithValues("predicate", "createEvent", - "namespace", classifier.Namespace, - "name", classifier.Name, - ) - - log.V(logs.LogVerbose).Info( - "Classifier did match expected conditions. Will attempt to reconcile associated Classifiers.") - return true - }, - GenericFunc: func(e event.GenericEvent) bool { - classifier := e.Object.(*libsveltosv1beta1.Classifier) - log := logger.WithValues("predicate", "createEvent", - "namespace", classifier.Namespace, - "name", classifier.Name, - ) - - log.V(logs.LogVerbose).Info( - "Classifier did not match expected conditions. Will not attempt to reconcile associated Classifiers.") - return false - }, - } -} diff --git a/controllers/classifier_predicates_test.go b/controllers/classifier_predicates_test.go index 52be0ea..1d0acf5 100644 --- a/controllers/classifier_predicates_test.go +++ b/controllers/classifier_predicates_test.go @@ -356,88 +356,3 @@ var _ = Describe("Classifier Predicates: ClassifierReportPredicate", func() { Expect(result).To(BeFalse()) }) }) - -var _ = Describe("Classifier Predicates: OtherClassifierPredicate", func() { - var logger logr.Logger - var classifier *libsveltosv1beta1.Classifier - - BeforeEach(func() { - logger = textlogger.NewLogger(textlogger.NewConfig(textlogger.Verbosity(1))) - classifier = &libsveltosv1beta1.Classifier{ - ObjectMeta: metav1.ObjectMeta{ - Name: randomString(), - }, - } - }) - - It("Create does not reprocesses", func() { - classifierPredicate := controllers.OtherClassifierPredicate(logger) - - e := event.CreateEvent{ - Object: classifier, - } - - result := classifierPredicate.Create(e) - Expect(result).To(BeFalse()) - }) - It("Delete does reprocess ", func() { - classifierPredicate := controllers.OtherClassifierPredicate(logger) - - e := event.DeleteEvent{ - Object: classifier, - } - - result := classifierPredicate.Delete(e) - Expect(result).To(BeTrue()) - }) - It("Update reprocesses when Status.MatchinClusterStatuses changes", func() { - classifierPredicate := controllers.OtherClassifierPredicate(logger) - - classifier.Status.MachingClusterStatuses = []libsveltosv1beta1.MachingClusterStatus{ - { - ClusterRef: corev1.ObjectReference{Namespace: randomString(), Name: randomString()}, - ManagedLabels: []string{randomString(), randomString()}, - }, - } - - oldClassifier := &libsveltosv1beta1.Classifier{ - ObjectMeta: metav1.ObjectMeta{ - Name: classifier.Name, - }, - } - oldClassifier.Status.MachingClusterStatuses = nil - - e := event.UpdateEvent{ - ObjectNew: classifier, - ObjectOld: oldClassifier, - } - - result := classifierPredicate.Update(e) - Expect(result).To(BeTrue()) - }) - It("Update does not reprocess when Status.MatchinClusterStatuses does not change", func() { - classifierPredicate := controllers.OtherClassifierPredicate(logger) - - classifier.Status.MachingClusterStatuses = []libsveltosv1beta1.MachingClusterStatus{ - { - ClusterRef: corev1.ObjectReference{Namespace: randomString(), Name: randomString()}, - ManagedLabels: []string{randomString(), randomString()}, - }, - } - - oldClassifier := &libsveltosv1beta1.Classifier{ - ObjectMeta: metav1.ObjectMeta{ - Name: classifier.Name, - }, - } - oldClassifier.Status.MachingClusterStatuses = classifier.Status.MachingClusterStatuses - - e := event.UpdateEvent{ - ObjectNew: classifier, - ObjectOld: oldClassifier, - } - - result := classifierPredicate.Update(e) - Expect(result).To(BeFalse()) - }) -}) diff --git a/controllers/classifier_transformations.go b/controllers/classifier_transformations.go index 4a319bf..9584d68 100644 --- a/controllers/classifier_transformations.go +++ b/controllers/classifier_transformations.go @@ -217,43 +217,3 @@ func (r *ClassifierReconciler) requeueClassifierForClassifierReport( return requests } - -func (r *ClassifierReconciler) requeueClassifierForClassifier( - ctx context.Context, o client.Object, -) []reconcile.Request { - - classifier := o.(*libsveltosv1beta1.Classifier) - logger := textlogger.NewLogger(textlogger.NewConfig(textlogger.Verbosity(1))).WithValues( - "objectMapper", - "requeueClassifierForClassifier", - "classifier", - classifier.Name, - ) - - logger.V(logs.LogDebug).Info("reacting to Classifier change") - - r.Mux.Lock() - defer r.Mux.Unlock() - - // Get all Classifier with at least one conflict - requests := make([]ctrl.Request, r.ClassifierSet.Len()) - - classifierWithConflicts := r.ClassifierSet.Items() - - for i := range classifierWithConflicts { - cName := classifierWithConflicts[i].Name - - if cName == classifier.Name { - continue - } - - logger.V(logs.LogDebug).Info(fmt.Sprintf("queing %s for reconciliation", cName)) - requests[i] = ctrl.Request{ - NamespacedName: client.ObjectKey{ - Name: cName, - }, - } - } - - return requests -} diff --git a/controllers/classifier_transformations_test.go b/controllers/classifier_transformations_test.go index 29a913a..0c50d85 100644 --- a/controllers/classifier_transformations_test.go +++ b/controllers/classifier_transformations_test.go @@ -155,36 +155,3 @@ var _ = Describe("ClassifierTransformations map functions", func() { Expect(requests).To(ContainElement(reconcile.Request{NamespacedName: types.NamespacedName{Name: classifierName}})) }) }) - -var _ = Describe("ClassifierTransformations map functions", func() { - It("requeueClassifierForClassifier returns Classifiers with at least one conflict", func() { - c := fake.NewClientBuilder().WithScheme(scheme).Build() - - reconciler := &controllers.ClassifierReconciler{ - Client: c, - Scheme: scheme, - Mux: sync.Mutex{}, - ClusterMap: make(map[corev1.ObjectReference]*libsveltosset.Set), - } - - classifierName1 := randomString() - classifierInfo1 := corev1.ObjectReference{ - Kind: libsveltosv1beta1.ClassifierKind, Name: classifierName1, APIVersion: libsveltosv1beta1.GroupVersion.String()} - reconciler.ClassifierSet.Insert(&classifierInfo1) - classifierName2 := randomString() - classifierInfo2 := corev1.ObjectReference{ - Kind: libsveltosv1beta1.ClassifierKind, Name: classifierName2, APIVersion: libsveltosv1beta1.GroupVersion.String()} - reconciler.ClassifierSet.Insert(&classifierInfo2) - - classifier := &libsveltosv1beta1.Classifier{ - ObjectMeta: metav1.ObjectMeta{ - Name: randomString(), - }, - } - - requests := controllers.RequeueClassifierForClassifier(reconciler, context.TODO(), classifier) - Expect(requests).To(HaveLen(2)) - Expect(requests).To(ContainElement(reconcile.Request{NamespacedName: types.NamespacedName{Name: classifierName2}})) - Expect(requests).To(ContainElement(reconcile.Request{NamespacedName: types.NamespacedName{Name: classifierName1}})) - }) -}) diff --git a/controllers/export_test.go b/controllers/export_test.go index 1f86a69..6b76ad7 100644 --- a/controllers/export_test.go +++ b/controllers/export_test.go @@ -53,7 +53,6 @@ var ( RequeueClassifierForCluster = (*ClassifierReconciler).requeueClassifierForCluster RequeueClassifierForMachine = (*ClassifierReconciler).requeueClassifierForMachine RequeueClassifierForClassifierReport = (*ClassifierReconciler).requeueClassifierForClassifierReport - RequeueClassifierForClassifier = (*ClassifierReconciler).requeueClassifierForClassifier UpdateMatchingClustersAndRegistrations = (*ClassifierReconciler).updateMatchingClustersAndRegistrations UpdateLabelsOnMatchingClusters = (*ClassifierReconciler).updateLabelsOnMatchingClusters HandleLabelRegistrations = (*ClassifierReconciler).handleLabelRegistrations From ce1348805d116877d7072546c8071f1efbc59849 Mon Sep 17 00:00:00 2001 From: Gianluca Mardente Date: Sat, 17 Jan 2026 09:00:44 +0100 Subject: [PATCH 2/2] Remove CAPI Machine watcher Sveltos simply uses cluster to detect when cluster is ready/paused --- controllers/classifier_controller.go | 14 ------- controllers/classifier_controller_test.go | 46 ----------------------- controllers/classifier_transformations.go | 32 ---------------- controllers/export_test.go | 1 - go.mod | 2 +- go.sum | 4 +- 6 files changed, 3 insertions(+), 96 deletions(-) diff --git a/controllers/classifier_controller.go b/controllers/classifier_controller.go index 1b7565f..8f9085f 100644 --- a/controllers/classifier_controller.go +++ b/controllers/classifier_controller.go @@ -129,8 +129,6 @@ type ClassifierReconciler struct { //+kubebuilder:rbac:groups=lib.projectsveltos.io,resources=configurationbundles/status,verbs=get;list;watch;update //+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters,verbs=get;watch;list;update //+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters/status,verbs=get;watch;list -//+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines,verbs=get;watch;list -//+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines/status,verbs=get;watch;list //+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch //+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch //+kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;list;watch @@ -376,18 +374,6 @@ func (r *ClassifierReconciler) WatchForCAPI(mgr ctrl.Manager, c controller.Contr return err } - // When cluster-api machine changes, according to ClusterPredicates, - // one or more ClusterProfiles need to be reconciled. - machineCluster := source.Kind[*clusterv1.Machine]( - mgr.GetCache(), - &clusterv1.Machine{}, - handler.TypedEnqueueRequestsFromMapFunc(r.requeueClassifierForMachine), - predicates.MachinePredicate{Logger: mgr.GetLogger().WithValues("predicate", "clusterpredicate")}, - ) - if err := c.Watch(machineCluster); err != nil { - return err - } - return nil } diff --git a/controllers/classifier_controller_test.go b/controllers/classifier_controller_test.go index f373f56..0685efc 100644 --- a/controllers/classifier_controller_test.go +++ b/controllers/classifier_controller_test.go @@ -525,50 +525,4 @@ var _ = Describe("ClassifierReconciler: requeue methods", func() { return false }, timeout, pollingInterval).Should(BeTrue()) }) - - It("RequeueClassifierForMachine returns correct Classifier for a CAPI machine", func() { - cpMachine := &clusterv1.Machine{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: cluster.Namespace, - Name: cluster.Name + randomString(), - Labels: map[string]string{ - clusterv1.ClusterNameLabel: cluster.Name, - clusterv1.MachineControlPlaneLabel: "ok", - }, - }, - } - cpMachine.Status.SetTypedPhase(clusterv1.MachinePhaseRunning) - - Expect(testEnv.Create(context.TODO(), classifier)).To(Succeed()) - Expect(testEnv.Create(context.TODO(), cpMachine)).To(Succeed()) - - Expect(waitForObject(context.TODO(), testEnv.Client, cpMachine)).To(Succeed()) - - classifierName := client.ObjectKey{ - Name: classifier.Name, - } - - dep := fakedeployer.GetClient(context.TODO(), textlogger.NewLogger(textlogger.NewConfig(textlogger.Verbosity(1))), - testEnv.Client) - Expect(dep.RegisterFeatureID(libsveltosv1beta1.FeatureClassifier)).To(Succeed()) - - clusterProfileReconciler := getClassifierReconciler(testEnv.Client, dep) - _, err := clusterProfileReconciler.Reconcile(context.TODO(), ctrl.Request{ - NamespacedName: classifierName, - }) - Expect(err).ToNot(HaveOccurred()) - - // Eventual loop so testEnv Cache is synced - Eventually(func() bool { - classifierList := controllers.RequeueClassifierForMachine(clusterProfileReconciler, - context.TODO(), cpMachine) - result := reconcile.Request{NamespacedName: types.NamespacedName{Name: classifier.Name}} - for i := range classifierList { - if classifierList[i] == result { - return true - } - } - return false - }, timeout, pollingInterval).Should(BeTrue()) - }) }) diff --git a/controllers/classifier_transformations.go b/controllers/classifier_transformations.go index 9584d68..9e6731f 100644 --- a/controllers/classifier_transformations.go +++ b/controllers/classifier_transformations.go @@ -79,38 +79,6 @@ func (r *ClassifierReconciler) requeueClassifierForACluster(o client.Object, return requests } -func (r *ClassifierReconciler) requeueClassifierForMachine( - ctx context.Context, machine *clusterv1.Machine, -) []reconcile.Request { - - logger := textlogger.NewLogger(textlogger.NewConfig(textlogger.Verbosity(1))).WithValues( - "objectMapper", - "requeueClassifierForMachine", - "namespace", - machine.Namespace, - "cluster", - machine.Name, - ) - - r.Mux.Lock() - defer r.Mux.Unlock() - - // Get all existing classifiers - classifiers := r.AllClassifierSet.Items() - requests := make([]ctrl.Request, r.AllClassifierSet.Len()) - - for i := range classifiers { - logger.V(logs.LogDebug).Info(fmt.Sprintf("requeuing classifier %s", classifiers[i].Name)) - requests[i] = ctrl.Request{ - NamespacedName: client.ObjectKey{ - Name: classifiers[i].Name, - }, - } - } - - return requests -} - func (r *ClassifierReconciler) requeueClassifierForSecret( ctx context.Context, o client.Object, ) []reconcile.Request { diff --git a/controllers/export_test.go b/controllers/export_test.go index 6b76ad7..4f87753 100644 --- a/controllers/export_test.go +++ b/controllers/export_test.go @@ -51,7 +51,6 @@ var ( ProcessClassifier = (*ClassifierReconciler).processClassifier RemoveClassifier = (*ClassifierReconciler).removeClassifier RequeueClassifierForCluster = (*ClassifierReconciler).requeueClassifierForCluster - RequeueClassifierForMachine = (*ClassifierReconciler).requeueClassifierForMachine RequeueClassifierForClassifierReport = (*ClassifierReconciler).requeueClassifierForClassifierReport UpdateMatchingClustersAndRegistrations = (*ClassifierReconciler).updateMatchingClustersAndRegistrations UpdateLabelsOnMatchingClusters = (*ClassifierReconciler).updateLabelsOnMatchingClusters diff --git a/go.mod b/go.mod index 232b422..d491f29 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/onsi/ginkgo/v2 v2.27.5 github.com/onsi/gomega v1.39.0 github.com/pkg/errors v0.9.1 - github.com/projectsveltos/libsveltos v1.4.0 + github.com/projectsveltos/libsveltos v1.4.1-0.20260117075726-0c63966fba23 github.com/prometheus/client_golang v1.23.2 github.com/spf13/pflag v1.0.10 golang.org/x/text v0.33.0 diff --git a/go.sum b/go.sum index ffc068a..bd4edab 100644 --- a/go.sum +++ b/go.sum @@ -143,8 +143,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/projectsveltos/libsveltos v1.4.0 h1:W30Bmyv5g6qmHOcGtURRmr5JFjKSx1ZA9NRpz147sS8= -github.com/projectsveltos/libsveltos v1.4.0/go.mod h1:M3ljA5j0FMQ9V0dGE3OvqhzfUEgzcU4JDGj/VTQMfZg= +github.com/projectsveltos/libsveltos v1.4.1-0.20260117075726-0c63966fba23 h1:8hlYPnuCbcd/iwdbZGHx3wSJGqr4dUD8FHlb3SUlkHU= +github.com/projectsveltos/libsveltos v1.4.1-0.20260117075726-0c63966fba23/go.mod h1:M3ljA5j0FMQ9V0dGE3OvqhzfUEgzcU4JDGj/VTQMfZg= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=