k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/apf_filter_test.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package flowcontrol 18 19 import ( 20 "context" 21 "testing" 22 "time" 23 24 flowcontrol "k8s.io/api/flowcontrol/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/util/wait" 28 "k8s.io/apiserver/pkg/endpoints/request" 29 fq "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing" 30 fqs "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/queueset" 31 "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/testing/eventclock" 32 "k8s.io/apiserver/pkg/util/flowcontrol/metrics" 33 fcrequest "k8s.io/apiserver/pkg/util/flowcontrol/request" 34 "k8s.io/client-go/informers" 35 clientsetfake "k8s.io/client-go/kubernetes/fake" 36 "k8s.io/utils/ptr" 37 ) 38 39 // TestQueueWaitTimeLatencyTracker tests the queue wait times recorded by the P&F latency tracker 40 // when calling Handle. 41 func TestQueueWaitTimeLatencyTracker(t *testing.T) { 42 metrics.Register() 43 44 var fsObj *flowcontrol.FlowSchema 45 var plcObj *flowcontrol.PriorityLevelConfiguration 46 cfgObjs := []runtime.Object{} 47 48 plName := "test-pl" 49 username := "test-user" 50 fsName := "test-fs" 51 lendable := int32(0) 52 borrowingLimit := int32(0) 53 fsObj = &flowcontrol.FlowSchema{ 54 ObjectMeta: metav1.ObjectMeta{ 55 Name: fsName, 56 }, 57 Spec: flowcontrol.FlowSchemaSpec{ 58 MatchingPrecedence: 100, 59 PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{ 60 Name: plName, 61 }, 62 DistinguisherMethod: &flowcontrol.FlowDistinguisherMethod{ 63 Type: flowcontrol.FlowDistinguisherMethodByUserType, 64 }, 65 Rules: []flowcontrol.PolicyRulesWithSubjects{{ 66 Subjects: []flowcontrol.Subject{{ 67 Kind: flowcontrol.SubjectKindUser, 68 User: &flowcontrol.UserSubject{Name: username}, 69 }}, 70 NonResourceRules: []flowcontrol.NonResourcePolicyRule{{ 71 Verbs: []string{"*"}, 72 NonResourceURLs: []string{"*"}, 73 }}, 74 }}, 75 }, 76 } 77 plcObj = &flowcontrol.PriorityLevelConfiguration{ 78 ObjectMeta: metav1.ObjectMeta{ 79 Name: plName, 80 }, 81 Spec: flowcontrol.PriorityLevelConfigurationSpec{ 82 Type: flowcontrol.PriorityLevelEnablementLimited, 83 Limited: &flowcontrol.LimitedPriorityLevelConfiguration{ 84 NominalConcurrencyShares: ptr.To(int32(100)), 85 LendablePercent: &lendable, 86 BorrowingLimitPercent: &borrowingLimit, 87 LimitResponse: flowcontrol.LimitResponse{ 88 Type: flowcontrol.LimitResponseTypeQueue, 89 Queuing: &flowcontrol.QueuingConfiguration{ 90 Queues: 10, 91 HandSize: 2, 92 QueueLengthLimit: 10, 93 }, 94 }, 95 }, 96 }, 97 } 98 cfgObjs = append(cfgObjs, fsObj, plcObj) 99 100 clientset := clientsetfake.NewSimpleClientset(cfgObjs...) 101 informerFactory := informers.NewSharedInformerFactory(clientset, time.Second) 102 flowcontrolClient := clientset.FlowcontrolV1() 103 startTime := time.Now() 104 clk, _ := eventclock.NewFake(startTime, 0, nil) 105 controller := newTestableController(TestableConfig{ 106 Name: "Controller", 107 Clock: clk, 108 AsFieldManager: ConfigConsumerAsFieldManager, 109 FoundToDangling: func(found bool) bool { return !found }, 110 InformerFactory: informerFactory, 111 FlowcontrolClient: flowcontrolClient, 112 ServerConcurrencyLimit: 24, 113 ReqsGaugeVec: metrics.PriorityLevelConcurrencyGaugeVec, 114 ExecSeatsGaugeVec: metrics.PriorityLevelExecutionSeatsGaugeVec, 115 QueueSetFactory: fqs.NewQueueSetFactory(clk), 116 }) 117 118 stopCh := make(chan struct{}) 119 defer close(stopCh) 120 121 informerFactory.Start(stopCh) 122 status := informerFactory.WaitForCacheSync(stopCh) 123 if names := unsynced(status); len(names) > 0 { 124 t.Fatalf("WaitForCacheSync did not successfully complete, resources=%#v", names) 125 } 126 127 go func() { 128 controller.Run(stopCh) 129 }() 130 131 // ensure that the controller has run its first loop. 132 err := wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (done bool, err error) { 133 return controller.hasPriorityLevelState(plcObj.Name), nil 134 }) 135 if err != nil { 136 t.Errorf("expected the controller to reconcile the priority level configuration object: %s, error: %s", plcObj.Name, err) 137 } 138 139 reqInfo := &request.RequestInfo{ 140 IsResourceRequest: false, 141 Path: "/foobar", 142 Verb: "GET", 143 } 144 noteFn := func(fs *flowcontrol.FlowSchema, plc *flowcontrol.PriorityLevelConfiguration, fd string) {} 145 workEstr := func() fcrequest.WorkEstimate { return fcrequest.WorkEstimate{InitialSeats: 1} } 146 147 flowUser := testUser{name: "test-user"} 148 rd := RequestDigest{ 149 RequestInfo: reqInfo, 150 User: flowUser, 151 } 152 153 // Add 1 second to the fake clock during QueueNoteFn 154 newTime := startTime.Add(time.Second) 155 qnf := fq.QueueNoteFn(func(bool) { clk.FakePassiveClock.SetTime(newTime) }) 156 ctx := request.WithLatencyTrackers(context.Background()) 157 controller.Handle(ctx, rd, noteFn, workEstr, qnf, func() {}) 158 159 latencyTracker, ok := request.LatencyTrackersFrom(ctx) 160 if !ok { 161 t.Fatalf("error getting latency tracker: %v", err) 162 } 163 164 expectedLatency := time.Second // newTime - startTime 165 latency := latencyTracker.APFQueueWaitTracker.GetLatency() 166 if latency != expectedLatency { 167 t.Errorf("unexpected latency, got %s, expected %s", latency, expectedLatency) 168 } 169 }