...

Source file src/k8s.io/kubernetes/pkg/controller/storageversiongc/gc_controller_test.go

Documentation: k8s.io/kubernetes/pkg/controller/storageversiongc

     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  }
   349  

View as plain text