mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-17 02:06:23 +00:00
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:
@@ -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,
|
||||
|
||||
134
cmd/kube-controller-manager/app/resource.go
Normal file
134
cmd/kube-controller-manager/app/resource.go
Normal 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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user