k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/exempt_borrowing_test.go (about) 1 /* 2 Copyright 2024 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 "testing" 21 "time" 22 23 fcboot "k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap" 24 fqs "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/queueset" 25 testeventclock "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/testing/eventclock" 26 "k8s.io/apiserver/pkg/util/flowcontrol/metrics" 27 "k8s.io/client-go/informers" 28 clientsetfake "k8s.io/client-go/kubernetes/fake" 29 30 flowcontrol "k8s.io/api/flowcontrol/v1" 31 ) 32 33 func TestUpdateBorrowing(t *testing.T) { 34 startTime := time.Now() 35 clk, _ := testeventclock.NewFake(startTime, 0, nil) 36 plcExempt := fcboot.MandatoryPriorityLevelConfigurationExempt 37 plcHigh := fcboot.SuggestedPriorityLevelConfigurationWorkloadHigh 38 plcMid := fcboot.SuggestedPriorityLevelConfigurationWorkloadLow 39 plcLow := fcboot.MandatoryPriorityLevelConfigurationCatchAll 40 plcs := []*flowcontrol.PriorityLevelConfiguration{plcHigh, plcExempt, plcMid, plcLow} 41 fses := []*flowcontrol.FlowSchema{} 42 k8sClient := clientsetfake.NewSimpleClientset(plcLow, plcExempt, plcHigh, plcMid) 43 informerFactory := informers.NewSharedInformerFactory(k8sClient, 0) 44 flowcontrolClient := k8sClient.FlowcontrolV1() 45 serverCL := int(*plcHigh.Spec.Limited.NominalConcurrencyShares+ 46 *plcMid.Spec.Limited.NominalConcurrencyShares+ 47 *plcLow.Spec.Limited.NominalConcurrencyShares) * 6 48 config := TestableConfig{ 49 Name: "test", 50 Clock: clk, 51 AsFieldManager: "testfm", 52 FoundToDangling: func(found bool) bool { return !found }, 53 InformerFactory: informerFactory, 54 FlowcontrolClient: flowcontrolClient, 55 ServerConcurrencyLimit: serverCL, 56 ReqsGaugeVec: metrics.PriorityLevelConcurrencyGaugeVec, 57 ExecSeatsGaugeVec: metrics.PriorityLevelExecutionSeatsGaugeVec, 58 QueueSetFactory: fqs.NewQueueSetFactory(clk), 59 } 60 ctlr := newTestableController(config) 61 _ = ctlr.lockAndDigestConfigObjects(plcs, fses) 62 if ctlr.nominalCLSum != serverCL { 63 t.Fatalf("Unexpected rounding: nominalCLSum=%d", ctlr.nominalCLSum) 64 } 65 stateExempt := ctlr.priorityLevelStates[plcExempt.Name] 66 stateHigh := ctlr.priorityLevelStates[plcHigh.Name] 67 stateMid := ctlr.priorityLevelStates[plcMid.Name] 68 stateLow := ctlr.priorityLevelStates[plcLow.Name] 69 70 // Scenario 1: everybody wants more than ServerConcurrencyLimit. 71 // Test the case of exempt borrowing so much that less than minCL 72 // is available to each non-exempt. 73 stateExempt.seatDemandIntegrator.Set(float64(serverCL + 100)) 74 stateHigh.seatDemandIntegrator.Set(float64(serverCL + 100)) 75 stateMid.seatDemandIntegrator.Set(float64(serverCL + 100)) 76 stateLow.seatDemandIntegrator.Set(float64(serverCL + 100)) 77 clk.SetTime(startTime.Add(borrowingAdjustmentPeriod)) 78 ctlr.updateBorrowing() 79 if expected, actual := serverCL+100, stateExempt.currentCL; expected != actual { 80 t.Errorf("Scenario 1: expected %d, got %d for exempt", expected, actual) 81 } else { 82 t.Logf("Scenario 1: expected and got %d for exempt", expected) 83 } 84 if expected, actual := stateHigh.minCL, stateHigh.currentCL; expected != actual { 85 t.Errorf("Scenario 1: expected %d, got %d for hi", expected, actual) 86 } else { 87 t.Logf("Scenario 1: expected and got %d for hi", expected) 88 } 89 if expected, actual := stateMid.minCL, stateMid.currentCL; expected != actual { 90 t.Errorf("Scenario 1: expected %d, got %d for mid", expected, actual) 91 } else { 92 t.Logf("Scenario 1: expected and got %d for mid", expected) 93 } 94 if expected, actual := stateLow.minCL, stateLow.currentCL; expected != actual { 95 t.Errorf("Scenario 1: expected %d, got %d for lo", expected, actual) 96 } else { 97 t.Logf("Scenario 1: expected and got %d for lo", expected) 98 } 99 100 // Scenario 2: non-exempt want more than serverCL but get halfway between minCL and minCurrentCL. 101 expectedHigh := (stateHigh.nominalCL + stateHigh.minCL) / 2 102 expectedMid := (stateMid.nominalCL + stateMid.minCL) / 2 103 expectedLow := (stateLow.nominalCL + stateLow.minCL) / 2 104 expectedExempt := serverCL - (expectedHigh + expectedMid + expectedLow) 105 stateExempt.seatDemandIntegrator.Set(float64(expectedExempt)) 106 clk.SetTime(startTime.Add(2 * borrowingAdjustmentPeriod)) 107 ctlr.updateBorrowing() 108 clk.SetTime(startTime.Add(3 * borrowingAdjustmentPeriod)) 109 ctlr.updateBorrowing() 110 if expected, actual := expectedExempt, stateExempt.currentCL; expected != actual { 111 t.Errorf("Scenario 2: expected %d, got %d for exempt", expected, actual) 112 } else { 113 t.Logf("Scenario 2: expected and got %d for exempt", expected) 114 } 115 if expected, actual := expectedHigh, stateHigh.currentCL; expected != actual { 116 t.Errorf("Scenario 2: expected %d, got %d for hi", expected, actual) 117 } else { 118 t.Logf("Scenario 2: expected and got %d for hi", expected) 119 } 120 if expected, actual := expectedMid, stateMid.currentCL; expected != actual { 121 t.Errorf("Scenario 2: expected %d, got %d for mid", expected, actual) 122 } else { 123 t.Logf("Scenario 2: expected and got %d for mid", expected) 124 } 125 if expected, actual := expectedLow, stateLow.currentCL; expected != actual { 126 t.Errorf("Scenario 2: expected %d, got %d for lo", expected, actual) 127 } else { 128 t.Logf("Scenario 2: expected and got %d for lo", expected) 129 } 130 131 // Scenario 3: only mid is willing to lend, and exempt borrows all of that. 132 // Test the case of regular borrowing. 133 expectedHigh = stateHigh.nominalCL 134 expectedMid = stateMid.minCL 135 expectedLow = stateLow.nominalCL 136 expectedExempt = serverCL - (expectedHigh + expectedMid + expectedLow) 137 stateExempt.seatDemandIntegrator.Set(float64(expectedExempt)) 138 stateMid.seatDemandIntegrator.Set(float64(1)) 139 clk.SetTime(startTime.Add(4 * borrowingAdjustmentPeriod)) 140 ctlr.updateBorrowing() 141 clk.SetTime(startTime.Add(5 * borrowingAdjustmentPeriod)) 142 ctlr.updateBorrowing() 143 if expected, actual := expectedExempt, stateExempt.currentCL; expected != actual { 144 t.Errorf("Scenario 3: expected %d, got %d for exempt", expected, actual) 145 } else { 146 t.Logf("Scenario 3: expected and got %d for exempt", expected) 147 } 148 if expected, actual := expectedHigh, stateHigh.currentCL; expected != actual { 149 t.Errorf("Scenario 3: expected %d, got %d for hi", expected, actual) 150 } else { 151 t.Logf("Scenario 3: expected and got %d for hi", expected) 152 } 153 if expected, actual := expectedMid, stateMid.currentCL; expected != actual { 154 t.Errorf("Scenario 3: expected %d, got %d for mid", expected, actual) 155 } else { 156 t.Logf("Scenario 3: expected and got %d for mid", expected) 157 } 158 if expected, actual := expectedLow, stateLow.currentCL; expected != actual { 159 t.Errorf("Scenario 3: expected %d, got %d for lo", expected, actual) 160 } else { 161 t.Logf("Scenario 3: expected and got %d for lo", expected) 162 } 163 164 }