k8s.io/kubernetes@v1.29.3/pkg/controller/storageversiongc/gc_controller_test.go (about) 1 /* 2 Copyright 2022 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 storageversiongc 18 19 import ( 20 "context" 21 "reflect" 22 "testing" 23 "time" 24 25 apiserverinternalv1alpha1 "k8s.io/api/apiserverinternal/v1alpha1" 26 coordinationv1 "k8s.io/api/coordination/v1" 27 apierrors "k8s.io/apimachinery/pkg/api/errors" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/client-go/informers" 30 "k8s.io/client-go/kubernetes" 31 "k8s.io/client-go/kubernetes/fake" 32 "k8s.io/klog/v2/ktesting" 33 utilpointer "k8s.io/utils/pointer" 34 ) 35 36 func setupController(ctx context.Context, clientset kubernetes.Interface) { 37 informerFactory := informers.NewSharedInformerFactory(clientset, 100*time.Millisecond) 38 leaseInformer := informerFactory.Coordination().V1().Leases() 39 storageVersionInformer := informerFactory.Internal().V1alpha1().StorageVersions() 40 41 controller := NewStorageVersionGC(ctx, clientset, leaseInformer, storageVersionInformer) 42 go controller.Run(context.Background()) 43 informerFactory.Start(nil) 44 } 45 46 func newKubeApiserverLease(name, holderIdentity string) *coordinationv1.Lease { 47 return &coordinationv1.Lease{ 48 ObjectMeta: metav1.ObjectMeta{ 49 Name: name, 50 Namespace: metav1.NamespaceSystem, 51 Labels: map[string]string{ 52 "apiserver.kubernetes.io/identity": "kube-apiserver", 53 }, 54 }, 55 Spec: coordinationv1.LeaseSpec{ 56 HolderIdentity: utilpointer.StringPtr(holderIdentity), 57 }, 58 } 59 } 60 61 // Test_StorageVersionUpdatedWithAllEncodingVersionsEqualOnLeaseDeletion validates that 62 // status.serverStorageVersions is updated when a kube-apiserver Lease is deleted. 63 // If the remaining Leases agree on a new encoding version, status.commonEncodingVersion 64 // should reflect the newly agreed version. 65 func Test_StorageVersionUpdatedWithAllEncodingVersionsEqualOnLeaseDeletion(t *testing.T) { 66 lease1 := newKubeApiserverLease("kube-apiserver-1", "kube-apiserver-1") 67 lease2 := newKubeApiserverLease("kube-apiserver-2", "kube-apiserver-2") 68 lease3 := newKubeApiserverLease("kube-apiserver-3", "kube-apiserver-3") 69 70 storageVersion := &apiserverinternalv1alpha1.StorageVersion{ 71 ObjectMeta: metav1.ObjectMeta{ 72 Name: "k8s.test.resources", 73 }, 74 Status: apiserverinternalv1alpha1.StorageVersionStatus{ 75 StorageVersions: []apiserverinternalv1alpha1.ServerStorageVersion{ 76 { 77 APIServerID: "kube-apiserver-1", 78 EncodingVersion: "v1", 79 DecodableVersions: []string{"v1"}, 80 }, 81 { 82 APIServerID: "kube-apiserver-2", 83 EncodingVersion: "v2", 84 DecodableVersions: []string{"v2"}, 85 }, 86 { 87 APIServerID: "kube-apiserver-3", 88 EncodingVersion: "v2", 89 DecodableVersions: []string{"v2"}, 90 }, 91 }, 92 CommonEncodingVersion: utilpointer.String("v1"), 93 }, 94 } 95 96 clientset := fake.NewSimpleClientset(lease1, lease2, lease3, storageVersion) 97 _, ctx := ktesting.NewTestContext(t) 98 setupController(ctx, clientset) 99 100 // Delete the lease object and verify that storage version status is updated 101 if err := clientset.CoordinationV1().Leases(metav1.NamespaceSystem).Delete(context.Background(), "kube-apiserver-1", metav1.DeleteOptions{}); err != nil { 102 t.Fatalf("error deleting lease object: %v", err) 103 } 104 105 // add a delay to ensure controller had a chance to reconcile 106 time.Sleep(2 * time.Second) 107 108 storageVersion, err := clientset.InternalV1alpha1().StorageVersions().Get(context.Background(), "k8s.test.resources", metav1.GetOptions{}) 109 if err != nil { 110 t.Fatalf("error getting StorageVersion: %v", err) 111 } 112 113 expectedServerStorageVersions := []apiserverinternalv1alpha1.ServerStorageVersion{ 114 { 115 APIServerID: "kube-apiserver-2", 116 EncodingVersion: "v2", 117 DecodableVersions: []string{"v2"}, 118 }, 119 { 120 APIServerID: "kube-apiserver-3", 121 EncodingVersion: "v2", 122 DecodableVersions: []string{"v2"}, 123 }, 124 } 125 126 if !reflect.DeepEqual(storageVersion.Status.StorageVersions, expectedServerStorageVersions) { 127 t.Error("unexpected storage version object") 128 t.Logf("got: %+v", storageVersion) 129 t.Logf("expected: %+v", expectedServerStorageVersions) 130 } 131 132 if *storageVersion.Status.CommonEncodingVersion != "v2" { 133 t.Errorf("unexpected common encoding version") 134 t.Logf("got: %q", *storageVersion.Status.CommonEncodingVersion) 135 t.Logf("expected: %q", "v2") 136 } 137 138 if len(storageVersion.Status.Conditions) != 1 { 139 t.Errorf("expected 1 condition, got: %d", len(storageVersion.Status.Conditions)) 140 } 141 142 if storageVersion.Status.Conditions[0].Type != apiserverinternalv1alpha1.AllEncodingVersionsEqual { 143 t.Errorf("expected condition type 'AllEncodingVersionsEqual', got: %q", storageVersion.Status.Conditions[0].Type) 144 } 145 146 if storageVersion.Status.Conditions[0].Status != apiserverinternalv1alpha1.ConditionTrue { 147 t.Errorf("expected condition status 'True', got: %q", storageVersion.Status.Conditions[0].Status) 148 } 149 } 150 151 // Test_StorageVersionUpdatedWithDifferentEncodingVersionsOnLeaseDeletion validates that 152 // status.serverStorageVersions is updated when a kube-apiserver Lease is deleted. 153 // If the remaining Leases do not agree on a new encoding version, status.commonEncodingVersion 154 // should remain unchanged. 155 func Test_StorageVersionUpdatedWithDifferentEncodingVersionsOnLeaseDeletion(t *testing.T) { 156 lease1 := newKubeApiserverLease("kube-apiserver-1", "kube-apiserver-1") 157 lease2 := newKubeApiserverLease("kube-apiserver-2", "kube-apiserver-2") 158 lease3 := newKubeApiserverLease("kube-apiserver-3", "kube-apiserver-3") 159 160 storageVersion := &apiserverinternalv1alpha1.StorageVersion{ 161 ObjectMeta: metav1.ObjectMeta{ 162 Name: "k8s.test.resources", 163 }, 164 Status: apiserverinternalv1alpha1.StorageVersionStatus{ 165 StorageVersions: []apiserverinternalv1alpha1.ServerStorageVersion{ 166 { 167 APIServerID: "kube-apiserver-1", 168 EncodingVersion: "v1", 169 DecodableVersions: []string{"v1"}, 170 }, 171 { 172 APIServerID: "kube-apiserver-3", 173 EncodingVersion: "v2", 174 DecodableVersions: []string{"v2"}, 175 }, 176 }, 177 CommonEncodingVersion: utilpointer.String("v1"), 178 }, 179 } 180 181 clientset := fake.NewSimpleClientset(lease1, lease2, lease3, storageVersion) 182 _, ctx := ktesting.NewTestContext(t) 183 setupController(ctx, clientset) 184 185 // Delete the lease object and verify that storage version status is updated 186 if err := clientset.CoordinationV1().Leases(metav1.NamespaceSystem).Delete(context.Background(), "kube-apiserver-2", metav1.DeleteOptions{}); err != nil { 187 t.Fatalf("error deleting lease object: %v", err) 188 } 189 190 // add a delay to ensure controller had a chance to reconcile 191 time.Sleep(2 * time.Second) 192 193 storageVersion, err := clientset.InternalV1alpha1().StorageVersions().Get(context.Background(), "k8s.test.resources", metav1.GetOptions{}) 194 if err != nil { 195 t.Fatalf("error getting StorageVersion: %v", err) 196 } 197 198 expectedServerStorageVersions := []apiserverinternalv1alpha1.ServerStorageVersion{ 199 { 200 APIServerID: "kube-apiserver-1", 201 EncodingVersion: "v1", 202 DecodableVersions: []string{"v1"}, 203 }, 204 { 205 APIServerID: "kube-apiserver-3", 206 EncodingVersion: "v2", 207 DecodableVersions: []string{"v2"}, 208 }, 209 } 210 211 if !reflect.DeepEqual(storageVersion.Status.StorageVersions, expectedServerStorageVersions) { 212 t.Error("unexpected storage version object") 213 t.Logf("got: %+v", storageVersion) 214 t.Logf("expected: %+v", expectedServerStorageVersions) 215 } 216 217 if *storageVersion.Status.CommonEncodingVersion != "v1" { 218 t.Errorf("unexpected common encoding version") 219 t.Logf("got: %q", *storageVersion.Status.CommonEncodingVersion) 220 t.Logf("expected: %q", "v1") 221 } 222 } 223 224 // Test_StorageVersionContainsInvalidLeaseID validates that status.serverStorageVersions 225 // only contains the holder identity from kube-apiserver Leases that exist. 226 func Test_StorageVersionContainsInvalidLeaseID(t *testing.T) { 227 lease1 := newKubeApiserverLease("kube-apiserver-1", "kube-apiserver-1") 228 lease2 := newKubeApiserverLease("kube-apiserver-2", "kube-apiserver-2") 229 lease3 := newKubeApiserverLease("kube-apiserver-3", "kube-apiserver-3") 230 231 storageVersion := &apiserverinternalv1alpha1.StorageVersion{ 232 ObjectMeta: metav1.ObjectMeta{ 233 Name: "k8s.test.resources", 234 }, 235 Status: apiserverinternalv1alpha1.StorageVersionStatus{ 236 StorageVersions: []apiserverinternalv1alpha1.ServerStorageVersion{ 237 { 238 APIServerID: "kube-apiserver-1", 239 EncodingVersion: "v1", 240 DecodableVersions: []string{"v1"}, 241 }, 242 { 243 APIServerID: "kube-apiserver-2", 244 EncodingVersion: "v2", 245 DecodableVersions: []string{"v2"}, 246 }, 247 { 248 APIServerID: "kube-apiserver-3", 249 EncodingVersion: "v2", 250 DecodableVersions: []string{"v2"}, 251 }, 252 { 253 APIServerID: "kube-apiserver-4", // doesn't exist 254 EncodingVersion: "v2", 255 DecodableVersions: []string{"v1"}, 256 }, 257 }, 258 CommonEncodingVersion: utilpointer.String("v1"), 259 }, 260 } 261 262 clientset := fake.NewSimpleClientset(lease1, lease2, lease3, storageVersion) 263 _, ctx := ktesting.NewTestContext(t) 264 setupController(ctx, clientset) 265 266 // add a delay to ensure controller had a chance to reconcile 267 time.Sleep(2 * time.Second) 268 269 storageVersion, err := clientset.InternalV1alpha1().StorageVersions().Get(context.Background(), "k8s.test.resources", metav1.GetOptions{}) 270 if err != nil { 271 t.Fatalf("error getting StorageVersion: %v", err) 272 } 273 274 expectedServerStorageVersions := []apiserverinternalv1alpha1.ServerStorageVersion{ 275 { 276 APIServerID: "kube-apiserver-1", 277 EncodingVersion: "v1", 278 DecodableVersions: []string{"v1"}, 279 }, 280 { 281 APIServerID: "kube-apiserver-2", 282 EncodingVersion: "v2", 283 DecodableVersions: []string{"v2"}, 284 }, 285 { 286 APIServerID: "kube-apiserver-3", 287 EncodingVersion: "v2", 288 DecodableVersions: []string{"v2"}, 289 }, 290 } 291 292 if !reflect.DeepEqual(storageVersion.Status.StorageVersions, expectedServerStorageVersions) { 293 t.Error("unexpected storage version object") 294 t.Logf("got: %+v", storageVersion) 295 t.Logf("expected: %+v", expectedServerStorageVersions) 296 } 297 298 if len(storageVersion.Status.Conditions) != 1 { 299 t.Errorf("expected 1 condition, got: %d", len(storageVersion.Status.Conditions)) 300 } 301 302 if storageVersion.Status.Conditions[0].Type != apiserverinternalv1alpha1.AllEncodingVersionsEqual { 303 t.Errorf("expected condition type 'AllEncodingVersionsEqual', got: %q", storageVersion.Status.Conditions[0].Type) 304 } 305 306 if storageVersion.Status.Conditions[0].Status != apiserverinternalv1alpha1.ConditionFalse { 307 t.Errorf("expected condition status 'True', got: %q", storageVersion.Status.Conditions[0].Status) 308 } 309 } 310 311 // Test_StorageVersionDeletedOnLeaseDeletion validates that a StorageVersion 312 // object is deleted if there are no kube-apiserver Leases. 313 func Test_StorageVersionDeletedOnLeaseDeletion(t *testing.T) { 314 lease1 := newKubeApiserverLease("kube-apiserver-1", "kube-apiserver-1") 315 316 storageVersion := &apiserverinternalv1alpha1.StorageVersion{ 317 ObjectMeta: metav1.ObjectMeta{ 318 Name: "k8s.test.resources", 319 }, 320 Status: apiserverinternalv1alpha1.StorageVersionStatus{ 321 StorageVersions: []apiserverinternalv1alpha1.ServerStorageVersion{ 322 { 323 APIServerID: "kube-apiserver-1", 324 EncodingVersion: "v1", 325 DecodableVersions: []string{"v1"}, 326 }, 327 }, 328 }, 329 } 330 331 clientset := fake.NewSimpleClientset(lease1, storageVersion) 332 _, ctx := ktesting.NewTestContext(t) 333 setupController(ctx, clientset) 334 335 // Delete the lease object and verify that storage version status is updated 336 if err := clientset.CoordinationV1().Leases(metav1.NamespaceSystem).Delete(context.Background(), "kube-apiserver-1", metav1.DeleteOptions{}); err != nil { 337 t.Fatalf("error deleting lease object: %v", err) 338 } 339 340 // add a delay to ensure controller had a chance to reconcile 341 time.Sleep(2 * time.Second) 342 343 // expect deleted 344 _, err := clientset.InternalV1alpha1().StorageVersions().Get(context.Background(), "k8s.test.resources", metav1.GetOptions{}) 345 if !apierrors.IsNotFound(err) { 346 t.Fatalf("expected IsNotFound error, got: %v", err) 347 } 348 }