...

Source file src/k8s.io/kubernetes/pkg/volume/util/resize_util_test.go

Documentation: k8s.io/kubernetes/pkg/volume/util

     1  /*
     2  Copyright 2018 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 util
    18  
    19  import (
    20  	"encoding/json"
    21  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	"k8s.io/apimachinery/pkg/api/resource"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    29  	clientset "k8s.io/client-go/kubernetes"
    30  	"k8s.io/client-go/kubernetes/fake"
    31  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    32  	"k8s.io/kubernetes/pkg/features"
    33  )
    34  
    35  type conditionMergeTestCase struct {
    36  	description     string
    37  	pvc             *v1.PersistentVolumeClaim
    38  	newConditions   []v1.PersistentVolumeClaimCondition
    39  	finalConditions []v1.PersistentVolumeClaimCondition
    40  }
    41  
    42  func TestMergeResizeCondition(t *testing.T) {
    43  	currentTime := metav1.Now()
    44  
    45  	pvc := makePVC([]v1.PersistentVolumeClaimCondition{
    46  		{
    47  			Type:               v1.PersistentVolumeClaimResizing,
    48  			Status:             v1.ConditionTrue,
    49  			LastTransitionTime: currentTime,
    50  		},
    51  	}).get()
    52  
    53  	noConditionPVC := makePVC([]v1.PersistentVolumeClaimCondition{}).get()
    54  
    55  	conditionFalseTime := metav1.Now()
    56  	newTime := metav1.NewTime(time.Now().Add(1 * time.Hour))
    57  
    58  	testCases := []conditionMergeTestCase{
    59  		{
    60  			description:     "when removing all conditions",
    61  			pvc:             pvc.DeepCopy(),
    62  			newConditions:   []v1.PersistentVolumeClaimCondition{},
    63  			finalConditions: []v1.PersistentVolumeClaimCondition{},
    64  		},
    65  		{
    66  			description: "adding new condition",
    67  			pvc:         pvc.DeepCopy(),
    68  			newConditions: []v1.PersistentVolumeClaimCondition{
    69  				{
    70  					Type:   v1.PersistentVolumeClaimFileSystemResizePending,
    71  					Status: v1.ConditionTrue,
    72  				},
    73  			},
    74  			finalConditions: []v1.PersistentVolumeClaimCondition{
    75  				{
    76  					Type:   v1.PersistentVolumeClaimFileSystemResizePending,
    77  					Status: v1.ConditionTrue,
    78  				},
    79  			},
    80  		},
    81  		{
    82  			description: "adding same condition with new timestamp",
    83  			pvc:         pvc.DeepCopy(),
    84  			newConditions: []v1.PersistentVolumeClaimCondition{
    85  				{
    86  					Type:               v1.PersistentVolumeClaimResizing,
    87  					Status:             v1.ConditionTrue,
    88  					LastTransitionTime: newTime,
    89  				},
    90  			},
    91  			finalConditions: []v1.PersistentVolumeClaimCondition{
    92  				{
    93  					Type:               v1.PersistentVolumeClaimResizing,
    94  					Status:             v1.ConditionTrue,
    95  					LastTransitionTime: currentTime,
    96  				},
    97  			},
    98  		},
    99  		{
   100  			description: "adding same condition but with different status",
   101  			pvc:         pvc.DeepCopy(),
   102  			newConditions: []v1.PersistentVolumeClaimCondition{
   103  				{
   104  					Type:               v1.PersistentVolumeClaimResizing,
   105  					Status:             v1.ConditionFalse,
   106  					LastTransitionTime: conditionFalseTime,
   107  				},
   108  			},
   109  			finalConditions: []v1.PersistentVolumeClaimCondition{
   110  				{
   111  					Type:               v1.PersistentVolumeClaimResizing,
   112  					Status:             v1.ConditionFalse,
   113  					LastTransitionTime: conditionFalseTime,
   114  				},
   115  			},
   116  		},
   117  		{
   118  			description: "when no condition exists on pvc",
   119  			pvc:         noConditionPVC.DeepCopy(),
   120  			newConditions: []v1.PersistentVolumeClaimCondition{
   121  				{
   122  					Type:               v1.PersistentVolumeClaimResizing,
   123  					Status:             v1.ConditionTrue,
   124  					LastTransitionTime: currentTime,
   125  				},
   126  			},
   127  			finalConditions: []v1.PersistentVolumeClaimCondition{
   128  				{
   129  					Type:               v1.PersistentVolumeClaimResizing,
   130  					Status:             v1.ConditionTrue,
   131  					LastTransitionTime: currentTime,
   132  				},
   133  			},
   134  		},
   135  	}
   136  
   137  	for _, testcase := range testCases {
   138  		updatePVC := MergeResizeConditionOnPVC(testcase.pvc, testcase.newConditions)
   139  
   140  		updateConditions := updatePVC.Status.Conditions
   141  		if !reflect.DeepEqual(updateConditions, testcase.finalConditions) {
   142  			t.Errorf("Expected updated conditions for test %s to be %v but got %v",
   143  				testcase.description,
   144  				testcase.finalConditions, updateConditions)
   145  		}
   146  	}
   147  
   148  }
   149  
   150  func TestResizeFunctions(t *testing.T) {
   151  	basePVC := makePVC([]v1.PersistentVolumeClaimCondition{})
   152  
   153  	tests := []struct {
   154  		name        string
   155  		pvc         *v1.PersistentVolumeClaim
   156  		expectedPVC *v1.PersistentVolumeClaim
   157  		testFunc    func(*v1.PersistentVolumeClaim, clientset.Interface, resource.Quantity) (*v1.PersistentVolumeClaim, error)
   158  	}{
   159  		{
   160  			name:        "mark fs resize, with no other conditions",
   161  			pvc:         basePVC.get(),
   162  			expectedPVC: basePVC.withStorageResourceStatus(v1.PersistentVolumeClaimNodeResizePending).get(),
   163  			testFunc: func(pvc *v1.PersistentVolumeClaim, c clientset.Interface, _ resource.Quantity) (*v1.PersistentVolumeClaim, error) {
   164  				return MarkForFSResize(pvc, c)
   165  			},
   166  		},
   167  		{
   168  			name: "mark fs resize, when other resource statuses are present",
   169  			pvc:  basePVC.withResourceStatus(v1.ResourceCPU, v1.PersistentVolumeClaimControllerResizeFailed).get(),
   170  			expectedPVC: basePVC.withResourceStatus(v1.ResourceCPU, v1.PersistentVolumeClaimControllerResizeFailed).
   171  				withStorageResourceStatus(v1.PersistentVolumeClaimNodeResizePending).get(),
   172  			testFunc: func(pvc *v1.PersistentVolumeClaim, c clientset.Interface, _ resource.Quantity) (*v1.PersistentVolumeClaim, error) {
   173  				return MarkForFSResize(pvc, c)
   174  			},
   175  		},
   176  		{
   177  			name:        "mark controller resize in-progress",
   178  			pvc:         basePVC.get(),
   179  			expectedPVC: basePVC.withStorageResourceStatus(v1.PersistentVolumeClaimControllerResizeInProgress).get(),
   180  			testFunc: func(pvc *v1.PersistentVolumeClaim, i clientset.Interface, q resource.Quantity) (*v1.PersistentVolumeClaim, error) {
   181  				return MarkControllerReisizeInProgress(pvc, "foobar", q, i)
   182  			},
   183  		},
   184  		{
   185  			name: "mark resize finished",
   186  			pvc: basePVC.withResourceStatus(v1.ResourceCPU, v1.PersistentVolumeClaimControllerResizeFailed).
   187  				withStorageResourceStatus(v1.PersistentVolumeClaimNodeResizePending).get(),
   188  			expectedPVC: basePVC.withResourceStatus(v1.ResourceCPU, v1.PersistentVolumeClaimControllerResizeFailed).
   189  				withStorageResourceStatus("").get(),
   190  			testFunc: func(pvc *v1.PersistentVolumeClaim, i clientset.Interface, q resource.Quantity) (*v1.PersistentVolumeClaim, error) {
   191  				return MarkFSResizeFinished(pvc, q, i)
   192  			},
   193  		},
   194  	}
   195  
   196  	for _, test := range tests {
   197  		tc := test
   198  		t.Run(tc.name, func(t *testing.T) {
   199  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, true)()
   200  			pvc := tc.pvc
   201  			kubeClient := fake.NewSimpleClientset(pvc)
   202  
   203  			var err error
   204  
   205  			pvc, err = tc.testFunc(pvc, kubeClient, resource.MustParse("10Gi"))
   206  			if err != nil {
   207  				t.Errorf("Expected no error but got %v", err)
   208  			}
   209  			realStatus := pvc.Status.AllocatedResourceStatuses
   210  			expectedStatus := tc.expectedPVC.Status.AllocatedResourceStatuses
   211  			if !reflect.DeepEqual(realStatus, expectedStatus) {
   212  				t.Errorf("expected %+v got %+v", expectedStatus, realStatus)
   213  			}
   214  		})
   215  	}
   216  
   217  }
   218  
   219  func TestCreatePVCPatch(t *testing.T) {
   220  	pvc1 := makePVC([]v1.PersistentVolumeClaimCondition{
   221  		{
   222  			Type:               v1.PersistentVolumeClaimFileSystemResizePending,
   223  			Status:             v1.ConditionTrue,
   224  			LastTransitionTime: metav1.Now(),
   225  		},
   226  	}).get()
   227  	pvc1.SetResourceVersion("10")
   228  	pvc2 := pvc1.DeepCopy()
   229  	pvc2.Status.Capacity = v1.ResourceList{
   230  		v1.ResourceName("size"): resource.MustParse("10G"),
   231  	}
   232  	patchBytes, err := createPVCPatch(pvc1, pvc2, true /* addResourceVersionCheck */)
   233  	if err != nil {
   234  		t.Errorf("error creating patch bytes %v", err)
   235  	}
   236  	var patchMap map[string]interface{}
   237  	err = json.Unmarshal(patchBytes, &patchMap)
   238  	if err != nil {
   239  		t.Errorf("error unmarshalling json patch : %v", err)
   240  	}
   241  	metadata, ok := patchMap["metadata"].(map[string]interface{})
   242  	if !ok {
   243  		t.Errorf("error converting metadata to version map")
   244  	}
   245  	resourceVersion, _ := metadata["resourceVersion"].(string)
   246  	if resourceVersion != "10" {
   247  		t.Errorf("expected resource version to 10 got %s", resourceVersion)
   248  	}
   249  }
   250  
   251  type pvcModifier struct {
   252  	pvc *v1.PersistentVolumeClaim
   253  }
   254  
   255  func (m pvcModifier) get() *v1.PersistentVolumeClaim {
   256  	return m.pvc.DeepCopy()
   257  }
   258  
   259  func makePVC(conditions []v1.PersistentVolumeClaimCondition) pvcModifier {
   260  	pvc := &v1.PersistentVolumeClaim{
   261  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "resize"},
   262  		Spec: v1.PersistentVolumeClaimSpec{
   263  			AccessModes: []v1.PersistentVolumeAccessMode{
   264  				v1.ReadWriteOnce,
   265  				v1.ReadOnlyMany,
   266  			},
   267  			Resources: v1.VolumeResourceRequirements{
   268  				Requests: v1.ResourceList{
   269  					v1.ResourceName(v1.ResourceStorage): resource.MustParse("2Gi"),
   270  				},
   271  			},
   272  		},
   273  		Status: v1.PersistentVolumeClaimStatus{
   274  			Phase:      v1.ClaimBound,
   275  			Conditions: conditions,
   276  			Capacity: v1.ResourceList{
   277  				v1.ResourceStorage: resource.MustParse("2Gi"),
   278  			},
   279  		},
   280  	}
   281  	return pvcModifier{pvc}
   282  }
   283  
   284  func (m pvcModifier) withStorageResourceStatus(status v1.ClaimResourceStatus) pvcModifier {
   285  	return m.withResourceStatus(v1.ResourceStorage, status)
   286  }
   287  
   288  func (m pvcModifier) withResourceStatus(resource v1.ResourceName, status v1.ClaimResourceStatus) pvcModifier {
   289  	if m.pvc.Status.AllocatedResourceStatuses != nil && status == "" {
   290  		delete(m.pvc.Status.AllocatedResourceStatuses, resource)
   291  		return m
   292  	}
   293  	if m.pvc.Status.AllocatedResourceStatuses != nil {
   294  		m.pvc.Status.AllocatedResourceStatuses[resource] = status
   295  	} else {
   296  		m.pvc.Status.AllocatedResourceStatuses = map[v1.ResourceName]v1.ClaimResourceStatus{
   297  			resource: status,
   298  		}
   299  	}
   300  	return m
   301  }
   302  

View as plain text