k8s.io/kubernetes@v1.29.3/pkg/controller/ttl/ttl_controller_test.go (about) 1 /* 2 Copyright 2017 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 ttl 18 19 import ( 20 "context" 21 "testing" 22 23 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/client-go/kubernetes/fake" 26 listers "k8s.io/client-go/listers/core/v1" 27 core "k8s.io/client-go/testing" 28 "k8s.io/client-go/tools/cache" 29 "k8s.io/client-go/util/workqueue" 30 "k8s.io/klog/v2/ktesting" 31 32 "github.com/stretchr/testify/assert" 33 ) 34 35 func TestPatchNode(t *testing.T) { 36 testCases := []struct { 37 node *v1.Node 38 ttlSeconds int 39 patch string 40 }{ 41 { 42 node: &v1.Node{}, 43 ttlSeconds: 0, 44 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"0\"}}}", 45 }, 46 { 47 node: &v1.Node{}, 48 ttlSeconds: 10, 49 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}", 50 }, 51 { 52 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name"}}, 53 ttlSeconds: 10, 54 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}", 55 }, 56 { 57 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, 58 ttlSeconds: 10, 59 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}", 60 }, 61 { 62 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "0"}}}, 63 ttlSeconds: 10, 64 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}", 65 }, 66 { 67 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "0", "a": "b"}}}, 68 ttlSeconds: 10, 69 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}", 70 }, 71 { 72 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "10", "a": "b"}}}, 73 ttlSeconds: 10, 74 patch: "{}", 75 }, 76 } 77 78 for i, testCase := range testCases { 79 fakeClient := &fake.Clientset{} 80 ttlController := &Controller{ 81 kubeClient: fakeClient, 82 } 83 err := ttlController.patchNodeWithAnnotation(context.TODO(), testCase.node, v1.ObjectTTLAnnotationKey, testCase.ttlSeconds) 84 if err != nil { 85 t.Errorf("%d: unexpected error: %v", i, err) 86 continue 87 } 88 actions := fakeClient.Actions() 89 assert.Equal(t, 1, len(actions), "unexpected actions: %#v", actions) 90 patchAction := actions[0].(core.PatchActionImpl) 91 assert.Equal(t, testCase.patch, string(patchAction.Patch), "%d: unexpected patch: %s", i, string(patchAction.Patch)) 92 } 93 } 94 95 func TestUpdateNodeIfNeeded(t *testing.T) { 96 testCases := []struct { 97 node *v1.Node 98 desiredTTL int 99 patch string 100 }{ 101 { 102 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name"}}, 103 desiredTTL: 0, 104 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"0\"}}}", 105 }, 106 { 107 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name"}}, 108 desiredTTL: 15, 109 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"15\"}}}", 110 }, 111 { 112 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name"}}, 113 desiredTTL: 30, 114 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"30\"}}}", 115 }, 116 { 117 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name", Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "0"}}}, 118 desiredTTL: 60, 119 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"60\"}}}", 120 }, 121 { 122 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name", Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "60"}}}, 123 desiredTTL: 60, 124 patch: "", 125 }, 126 { 127 node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name", Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "60"}}}, 128 desiredTTL: 30, 129 patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"30\"}}}", 130 }, 131 } 132 133 for i, testCase := range testCases { 134 fakeClient := &fake.Clientset{} 135 nodeStore := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) 136 nodeStore.Add(testCase.node) 137 ttlController := &Controller{ 138 kubeClient: fakeClient, 139 nodeStore: listers.NewNodeLister(nodeStore), 140 desiredTTLSeconds: testCase.desiredTTL, 141 } 142 if err := ttlController.updateNodeIfNeeded(context.TODO(), testCase.node.Name); err != nil { 143 t.Errorf("%d: unexpected error: %v", i, err) 144 continue 145 } 146 actions := fakeClient.Actions() 147 if testCase.patch == "" { 148 assert.Equal(t, 0, len(actions), "unexpected actions: %#v", actions) 149 } else { 150 assert.Equal(t, 1, len(actions), "unexpected actions: %#v", actions) 151 patchAction := actions[0].(core.PatchActionImpl) 152 assert.Equal(t, testCase.patch, string(patchAction.Patch), "%d: unexpected patch: %s", i, string(patchAction.Patch)) 153 } 154 } 155 } 156 157 func TestDesiredTTL(t *testing.T) { 158 testCases := []struct { 159 addNode bool 160 deleteNode bool 161 nodeCount int 162 desiredTTL int 163 boundaryStep int 164 expectedTTL int 165 }{ 166 { 167 addNode: true, 168 nodeCount: 0, 169 desiredTTL: 0, 170 boundaryStep: 0, 171 expectedTTL: 0, 172 }, 173 { 174 addNode: true, 175 nodeCount: 99, 176 desiredTTL: 0, 177 boundaryStep: 0, 178 expectedTTL: 0, 179 }, 180 { 181 addNode: true, 182 nodeCount: 100, 183 desiredTTL: 0, 184 boundaryStep: 0, 185 expectedTTL: 15, 186 }, 187 { 188 deleteNode: true, 189 nodeCount: 101, 190 desiredTTL: 15, 191 boundaryStep: 1, 192 expectedTTL: 15, 193 }, 194 { 195 deleteNode: true, 196 nodeCount: 91, 197 desiredTTL: 15, 198 boundaryStep: 1, 199 expectedTTL: 15, 200 }, 201 { 202 addNode: true, 203 nodeCount: 91, 204 desiredTTL: 15, 205 boundaryStep: 1, 206 expectedTTL: 15, 207 }, 208 { 209 deleteNode: true, 210 nodeCount: 90, 211 desiredTTL: 15, 212 boundaryStep: 1, 213 expectedTTL: 0, 214 }, 215 { 216 deleteNode: true, 217 nodeCount: 1800, 218 desiredTTL: 300, 219 boundaryStep: 4, 220 expectedTTL: 60, 221 }, 222 { 223 deleteNode: true, 224 nodeCount: 10000, 225 desiredTTL: 300, 226 boundaryStep: 4, 227 expectedTTL: 300, 228 }, 229 } 230 231 for i, testCase := range testCases { 232 ttlController := &Controller{ 233 queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), 234 nodeCount: testCase.nodeCount, 235 desiredTTLSeconds: testCase.desiredTTL, 236 boundaryStep: testCase.boundaryStep, 237 } 238 if testCase.addNode { 239 logger, _ := ktesting.NewTestContext(t) 240 ttlController.addNode(logger, &v1.Node{}) 241 } 242 if testCase.deleteNode { 243 ttlController.deleteNode(&v1.Node{}) 244 } 245 assert.Equal(t, testCase.expectedTTL, ttlController.getDesiredTTLSeconds(), 246 "%d: unexpected ttl: %d", i, ttlController.getDesiredTTLSeconds()) 247 } 248 }