Pass individual informers, move DRA controllers to resource.go, simplify retry logic and metric tests

Signed-off-by: Nour <nurmn3m@gmail.com>
This commit is contained in:
Nour
2026-03-18 21:46:53 +02:00
parent 1d634d81f2
commit 58cbde2aff
6 changed files with 201 additions and 178 deletions

View File

@@ -30,7 +30,6 @@ import (
v1 "k8s.io/api/core/v1"
genericfeatures "k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/quota/v1/generic"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/discovery"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/metadata"
@@ -42,7 +41,6 @@ import (
"k8s.io/klog/v2"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
pkgcontroller "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controller/devicetainteviction"
endpointcontroller "k8s.io/kubernetes/pkg/controller/endpoint"
"k8s.io/kubernetes/pkg/controller/garbagecollector"
namespacecontroller "k8s.io/kubernetes/pkg/controller/namespace"
@@ -52,8 +50,6 @@ import (
lifecyclecontroller "k8s.io/kubernetes/pkg/controller/nodelifecycle"
"k8s.io/kubernetes/pkg/controller/podgc"
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
"k8s.io/kubernetes/pkg/controller/resourceclaim"
"k8s.io/kubernetes/pkg/controller/resourcepoolstatusrequest"
resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/controller/storageversiongc"
@@ -249,41 +245,6 @@ func newTaintEvictionController(ctx context.Context, controllerContext Controlle
return newControllerLoop(tec.Run, controllerName), nil
}
func newDeviceTaintEvictionControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.DeviceTaintEvictionController,
constructor: newDeviceTaintEvictionController,
requiredFeatureGates: []featuregate.Feature{
// TODO update app.TestFeatureGatedControllersShouldNotDefineAliases when removing these feature gates.
features.DynamicResourceAllocation,
features.DRADeviceTaints,
},
}
}
func newDeviceTaintEvictionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) {
client, err := controllerContext.NewClient(names.DeviceTaintEvictionController)
if err != nil {
return nil, err
}
deviceTaintEvictionController := devicetainteviction.New(
client,
controllerContext.InformerFactory.Core().V1().Pods(),
controllerContext.InformerFactory.Resource().V1().ResourceClaims(),
controllerContext.InformerFactory.Resource().V1().ResourceSlices(),
controllerContext.InformerFactory.Resource().V1beta2().DeviceTaintRules(),
controllerContext.InformerFactory.Resource().V1().DeviceClasses(),
controllerName,
)
return newControllerLoop(func(ctx context.Context) {
if err := deviceTaintEvictionController.Run(ctx, int(controllerContext.ComponentConfig.DeviceTaintEvictionController.ConcurrentSyncs)); err != nil {
klog.FromContext(ctx).Error(err, "Device taint processing leading to Pod eviction failed and is now paused")
}
<-ctx.Done()
}, controllerName), nil
}
func newCloudNodeLifecycleControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: cpnames.CloudNodeLifecycleController,
@@ -461,72 +422,6 @@ func newEphemeralVolumeController(ctx context.Context, controllerContext Control
}, controllerName), nil
}
func newResourceClaimControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ResourceClaimController,
aliases: []string{"resource-claim-controller"},
constructor: newResourceClaimController,
requiredFeatureGates: []featuregate.Feature{
features.DynamicResourceAllocation, // TODO update app.TestFeatureGatedControllersShouldNotDefineAliases when removing this feature
},
}
}
func newResourceClaimController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) {
client, err := controllerContext.NewClient("resource-claim-controller")
if err != nil {
return nil, err
}
ephemeralController, err := resourceclaim.NewController(
klog.FromContext(ctx),
resourceclaim.Features{
AdminAccess: utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess),
PrioritizedList: utilfeature.DefaultFeatureGate.Enabled(features.DRAPrioritizedList),
},
client,
controllerContext.InformerFactory.Core().V1().Pods(),
controllerContext.InformerFactory.Resource().V1().ResourceClaims(),
controllerContext.InformerFactory.Resource().V1().ResourceClaimTemplates())
if err != nil {
return nil, fmt.Errorf("failed to init resource claim controller: %w", err)
}
return newControllerLoop(func(ctx context.Context) {
ephemeralController.Run(ctx, int(controllerContext.ComponentConfig.ResourceClaimController.ConcurrentSyncs))
}, controllerName), nil
}
func newResourcePoolStatusRequestControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ResourcePoolStatusRequestController,
constructor: newResourcePoolStatusRequestController,
requiredFeatureGates: []featuregate.Feature{
features.DRAResourcePoolStatus,
},
}
}
func newResourcePoolStatusRequestController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) {
client, err := controllerContext.NewClient("resourcepoolstatusrequest-controller")
if err != nil {
return nil, err
}
controller, err := resourcepoolstatusrequest.NewController(
ctx,
client,
controllerContext.InformerFactory,
)
if err != nil {
return nil, fmt.Errorf("failed to init resourcepoolstatusrequest controller: %w", err)
}
return newControllerLoop(func(ctx context.Context) {
controller.Run(ctx, 1) // Single worker is sufficient for this controller
}, controllerName), nil
}
func newEndpointsControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.EndpointsController,

View File

@@ -0,0 +1,134 @@
/*
Copyright The Kubernetes Authors.
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 app
import (
"context"
"fmt"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/component-base/featuregate"
"k8s.io/klog/v2"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/devicetainteviction"
"k8s.io/kubernetes/pkg/controller/resourceclaim"
"k8s.io/kubernetes/pkg/controller/resourcepoolstatusrequest"
"k8s.io/kubernetes/pkg/features"
)
func newDeviceTaintEvictionControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.DeviceTaintEvictionController,
constructor: newDeviceTaintEvictionController,
requiredFeatureGates: []featuregate.Feature{
// TODO update app.TestFeatureGatedControllersShouldNotDefineAliases when removing these feature gates.
features.DynamicResourceAllocation,
features.DRADeviceTaints,
},
}
}
func newDeviceTaintEvictionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) {
client, err := controllerContext.NewClient(names.DeviceTaintEvictionController)
if err != nil {
return nil, err
}
deviceTaintEvictionController := devicetainteviction.New(
client,
controllerContext.InformerFactory.Core().V1().Pods(),
controllerContext.InformerFactory.Resource().V1().ResourceClaims(),
controllerContext.InformerFactory.Resource().V1().ResourceSlices(),
controllerContext.InformerFactory.Resource().V1beta2().DeviceTaintRules(),
controllerContext.InformerFactory.Resource().V1().DeviceClasses(),
controllerName,
)
return newControllerLoop(func(ctx context.Context) {
if err := deviceTaintEvictionController.Run(ctx, int(controllerContext.ComponentConfig.DeviceTaintEvictionController.ConcurrentSyncs)); err != nil {
klog.FromContext(ctx).Error(err, "Device taint processing leading to Pod eviction failed and is now paused")
}
<-ctx.Done()
}, controllerName), nil
}
func newResourceClaimControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ResourceClaimController,
aliases: []string{"resource-claim-controller"},
constructor: newResourceClaimController,
requiredFeatureGates: []featuregate.Feature{
features.DynamicResourceAllocation, // TODO update app.TestFeatureGatedControllersShouldNotDefineAliases when removing this feature
},
}
}
func newResourceClaimController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) {
client, err := controllerContext.NewClient("resource-claim-controller")
if err != nil {
return nil, err
}
ephemeralController, err := resourceclaim.NewController(
klog.FromContext(ctx),
resourceclaim.Features{
AdminAccess: utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess),
PrioritizedList: utilfeature.DefaultFeatureGate.Enabled(features.DRAPrioritizedList),
},
client,
controllerContext.InformerFactory.Core().V1().Pods(),
controllerContext.InformerFactory.Resource().V1().ResourceClaims(),
controllerContext.InformerFactory.Resource().V1().ResourceClaimTemplates())
if err != nil {
return nil, fmt.Errorf("failed to init resource claim controller: %w", err)
}
return newControllerLoop(func(ctx context.Context) {
ephemeralController.Run(ctx, int(controllerContext.ComponentConfig.ResourceClaimController.ConcurrentSyncs))
}, controllerName), nil
}
func newResourcePoolStatusRequestControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ResourcePoolStatusRequestController,
constructor: newResourcePoolStatusRequestController,
requiredFeatureGates: []featuregate.Feature{
features.DRAResourcePoolStatus,
},
}
}
func newResourcePoolStatusRequestController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) {
client, err := controllerContext.NewClient("resourcepoolstatusrequest-controller")
if err != nil {
return nil, err
}
controller, err := resourcepoolstatusrequest.NewController(
ctx,
client,
controllerContext.InformerFactory.Resource().V1alpha3().ResourcePoolStatusRequests(),
controllerContext.InformerFactory.Resource().V1().ResourceSlices(),
controllerContext.InformerFactory.Resource().V1().ResourceClaims(),
)
if err != nil {
return nil, fmt.Errorf("failed to init resourcepoolstatusrequest controller: %w", err)
}
return newControllerLoop(func(ctx context.Context) {
controller.Run(ctx, 1) // Single worker is sufficient for this controller
}, controllerName), nil
}

View File

@@ -28,7 +28,8 @@ import (
"k8s.io/apimachinery/pkg/labels"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/informers"
resourcev1informers "k8s.io/client-go/informers/resource/v1"
resourcev1alpha3informers "k8s.io/client-go/informers/resource/v1alpha3"
clientset "k8s.io/client-go/kubernetes"
resourcev1listers "k8s.io/client-go/listers/resource/v1"
resourcev1alpha3listers "k8s.io/client-go/listers/resource/v1alpha3"
@@ -87,14 +88,12 @@ type Controller struct {
func NewController(
ctx context.Context,
client clientset.Interface,
informerFactory informers.SharedInformerFactory,
requestInformer resourcev1alpha3informers.ResourcePoolStatusRequestInformer,
sliceInformer resourcev1informers.ResourceSliceInformer,
claimInformer resourcev1informers.ResourceClaimInformer,
) (*Controller, error) {
logger := klog.FromContext(ctx)
requestInformer := informerFactory.Resource().V1alpha3().ResourcePoolStatusRequests()
sliceInformer := informerFactory.Resource().V1().ResourceSlices()
claimInformer := informerFactory.Resource().V1().ResourceClaims()
c := &Controller{
client: client,
requestLister: requestInformer.Lister(),
@@ -115,10 +114,7 @@ func NewController(
c.enqueueRequest(logger, obj)
},
UpdateFunc: func(old, new interface{}) {
newReq := new.(*resourcev1alpha3.ResourcePoolStatusRequest)
if newReq.Status == nil {
c.enqueueRequest(logger, new)
}
c.enqueueRequest(logger, new)
},
})
if err != nil {
@@ -235,7 +231,7 @@ func (c *Controller) syncRequest(ctx context.Context, key string) error {
break
}
}
if hasIncomplete && c.workqueue.NumRequeues(key) < maxRetries {
if hasIncomplete {
return fmt.Errorf("incomplete pools detected, requeueing")
}

View File

@@ -332,7 +332,11 @@ func TestCalculatePoolStatus(t *testing.T) {
informerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
// Create controller
controller, err := NewController(ctx, fakeClient, informerFactory)
controller, err := NewController(ctx, fakeClient,
informerFactory.Resource().V1alpha3().ResourcePoolStatusRequests(),
informerFactory.Resource().V1().ResourceSlices(),
informerFactory.Resource().V1().ResourceClaims(),
)
if err != nil {
t.Fatalf("Failed to create controller: %v", err)
}
@@ -456,7 +460,11 @@ func TestSyncRequest(t *testing.T) {
informerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
// Create controller
controller, err := NewController(ctx, fakeClient, informerFactory)
controller, err := NewController(ctx, fakeClient,
informerFactory.Resource().V1alpha3().ResourcePoolStatusRequests(),
informerFactory.Resource().V1().ResourceSlices(),
informerFactory.Resource().V1().ResourceClaims(),
)
if err != nil {
t.Fatalf("Failed to create controller: %v", err)
}
@@ -507,7 +515,11 @@ func TestSyncRequestRequeuesIncompletePool(t *testing.T) {
fakeClient := fake.NewClientset(request)
informerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
controller, err := NewController(ctx, fakeClient, informerFactory)
controller, err := NewController(ctx, fakeClient,
informerFactory.Resource().V1alpha3().ResourcePoolStatusRequests(),
informerFactory.Resource().V1().ResourceSlices(),
informerFactory.Resource().V1().ResourceClaims(),
)
if err != nil {
t.Fatalf("Failed to create controller: %v", err)
}
@@ -525,7 +537,8 @@ func TestSyncRequestRequeuesIncompletePool(t *testing.T) {
t.Fatalf("Failed to add slice to informer: %v", err)
}
// First sync should return error to trigger requeue (NumRequeues=0 < maxRetries)
// syncRequest should always return an error for incomplete pools,
// letting processNextWorkItem handle retry counting and drop logic.
err = controller.syncRequest(ctx, "test-request")
if err == nil {
t.Fatal("Expected syncRequest to return error for incomplete pool requeue, got nil")
@@ -538,31 +551,18 @@ func TestSyncRequestRequeuesIncompletePool(t *testing.T) {
}
}
// Simulate exhausted retries by adding the key to the workqueue enough times
// Even after retries are exhausted, syncRequest still returns an error;
// it is processNextWorkItem that decides to drop the key.
for range maxRetries {
controller.workqueue.AddRateLimited("test-request")
// Get and Done to process each add
key, _ := controller.workqueue.Get()
controller.workqueue.Done(key)
}
// Now NumRequeues >= maxRetries, syncRequest should succeed and set status
fakeClient.ClearActions()
err = controller.syncRequest(ctx, "test-request")
if err != nil {
t.Fatalf("Expected syncRequest to succeed after retries exhausted, got: %v", err)
}
// Verify status was updated with incomplete pools having ValidationError
var foundUpdate bool
for _, action := range fakeClient.Actions() {
if action.GetVerb() == "update" && action.GetSubresource() == "status" {
foundUpdate = true
break
}
}
if !foundUpdate {
t.Error("Expected status update after retries exhausted")
if err == nil {
t.Fatal("Expected syncRequest to still return error for incomplete pools after retries exhausted")
}
}
@@ -593,7 +593,11 @@ func TestSkipProcessedRequest(t *testing.T) {
fakeClient := fake.NewClientset(request)
informerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
controller, err := NewController(ctx, fakeClient, informerFactory)
controller, err := NewController(ctx, fakeClient,
informerFactory.Resource().V1alpha3().ResourcePoolStatusRequests(),
informerFactory.Resource().V1().ResourceSlices(),
informerFactory.Resource().V1().ResourceClaims(),
)
if err != nil {
t.Fatalf("Failed to create controller: %v", err)
}
@@ -729,7 +733,11 @@ func TestShouldDeleteRequest(t *testing.T) {
fakeClient := fake.NewClientset()
informerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
controller, err := NewController(ctx, fakeClient, informerFactory)
controller, err := NewController(ctx, fakeClient,
informerFactory.Resource().V1alpha3().ResourcePoolStatusRequests(),
informerFactory.Resource().V1().ResourceSlices(),
informerFactory.Resource().V1().ResourceClaims(),
)
if err != nil {
t.Fatalf("Failed to create controller: %v", err)
}
@@ -880,7 +888,11 @@ func TestCleanupExpiredRequests(t *testing.T) {
fakeClient := fake.NewClientset(expiredRequest, activeRequest)
informerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
controller, err := NewController(ctx, fakeClient, informerFactory)
controller, err := NewController(ctx, fakeClient,
informerFactory.Resource().V1alpha3().ResourcePoolStatusRequests(),
informerFactory.Resource().V1().ResourceSlices(),
informerFactory.Resource().V1().ResourceClaims(),
)
if err != nil {
t.Fatalf("Failed to create controller: %v", err)
}

View File

@@ -67,52 +67,36 @@ func TestRequestProcessingDuration(t *testing.T) {
RequestProcessingDuration.WithLabelValues("driver-a.example.com").Observe(0.5)
RequestProcessingDuration.WithLabelValues("driver-b.example.com").Observe(1.0)
// Validate metric metadata and label structure; histogram bucket values
// are deterministic given the observations above.
// Use gatherWithoutDurations to strip timing-dependent histogram fields
// (SampleSum and Bucket), verifying only _count lines.
want := `# HELP resourcepoolstatusrequest_controller_request_processing_duration_seconds [ALPHA] Time taken to process a ResourcePoolStatusRequest
# TYPE resourcepoolstatusrequest_controller_request_processing_duration_seconds histogram
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.001"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.002"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.004"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.008"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.016"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.032"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.064"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.128"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.256"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="0.512"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="1.024"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="2.048"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="4.096"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="8.192"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="16.384"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-a.example.com",le="+Inf"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_sum{driver_name="driver-a.example.com"} 0.5
resourcepoolstatusrequest_controller_request_processing_duration_seconds_count{driver_name="driver-a.example.com"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.001"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.002"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.004"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.008"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.016"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.032"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.064"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.128"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.256"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="0.512"} 0
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="1.024"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="2.048"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="4.096"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="8.192"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="16.384"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_bucket{driver_name="driver-b.example.com",le="+Inf"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_sum{driver_name="driver-b.example.com"} 1
resourcepoolstatusrequest_controller_request_processing_duration_seconds_count{driver_name="driver-b.example.com"} 1
`
if err := testutil.GatherAndCompare(registry, strings.NewReader(want), "resourcepoolstatusrequest_controller_request_processing_duration_seconds"); err != nil {
if err := testutil.GatherAndCompare(gatherWithoutDurations(registry), strings.NewReader(want), "resourcepoolstatusrequest_controller_request_processing_duration_seconds"); err != nil {
t.Errorf("unexpected metric output: %v", err)
}
}
// gatherWithoutDurations wraps a registry and strips timing-dependent fields
// (SampleSum and Bucket) from histograms so that tests only verify _count.
func gatherWithoutDurations(registry metrics.KubeRegistry) testutil.GathererFunc {
return func() ([]*testutil.MetricFamily, error) {
got, err := registry.Gather()
for _, mf := range got {
for _, m := range mf.Metric {
if m.Histogram == nil {
continue
}
m.Histogram.SampleSum = nil
m.Histogram.Bucket = nil
}
}
return got, err
}
}
func TestRegister(t *testing.T) {
// Verify Register does not panic when called multiple times.
Register()

View File

@@ -132,7 +132,9 @@ func (c *resourcePoolStatusRequestControllerSingleton) start(tCtx ktesting.TCont
controller, err := resourcepoolstatusrequest.NewController(
controllerCtx,
client,
c.informerFactory,
c.informerFactory.Resource().V1alpha3().ResourcePoolStatusRequests(),
c.informerFactory.Resource().V1().ResourceSlices(),
c.informerFactory.Resource().V1().ResourceClaims(),
)
tCtx.ExpectNoError(err, "create ResourcePoolStatusRequest controller")