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  }