k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/registry/core/persistentvolume/strategy_test.go (about)

     1  /*
     2  Copyright 2015 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 persistentvolume
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    25  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    26  	apitesting "k8s.io/kubernetes/pkg/api/testing"
    27  	api "k8s.io/kubernetes/pkg/apis/core"
    28  	"k8s.io/kubernetes/pkg/features"
    29  	"reflect"
    30  	"testing"
    31  	"time"
    32  
    33  	// ensure types are installed
    34  	_ "k8s.io/kubernetes/pkg/apis/core/install"
    35  )
    36  
    37  func TestSelectableFieldLabelConversions(t *testing.T) {
    38  	apitesting.TestSelectableFieldLabelConversionsOfKind(t,
    39  		"v1",
    40  		"PersistentVolume",
    41  		PersistentVolumeToSelectableFields(&api.PersistentVolume{}),
    42  		map[string]string{"name": "metadata.name"},
    43  	)
    44  }
    45  
    46  func TestStatusUpdate(t *testing.T) {
    47  	now := metav1.Now()
    48  	origin := metav1.NewTime(now.Add(time.Hour))
    49  	later := metav1.NewTime(now.Add(time.Hour * 2))
    50  	NowFunc = func() metav1.Time { return now }
    51  	defer func() {
    52  		NowFunc = metav1.Now
    53  	}()
    54  	tests := []struct {
    55  		name        string
    56  		fg          bool
    57  		oldObj      *api.PersistentVolume
    58  		newObj      *api.PersistentVolume
    59  		expectedObj *api.PersistentVolume
    60  	}{
    61  		{
    62  			name: "feature enabled: timestamp is updated when phase changes",
    63  			fg:   true,
    64  			oldObj: &api.PersistentVolume{
    65  				ObjectMeta: metav1.ObjectMeta{
    66  					Name: "foo",
    67  				},
    68  				Status: api.PersistentVolumeStatus{
    69  					Phase: api.VolumePending,
    70  				},
    71  			},
    72  			newObj: &api.PersistentVolume{
    73  				ObjectMeta: metav1.ObjectMeta{
    74  					Name: "foo",
    75  				},
    76  				Status: api.PersistentVolumeStatus{
    77  					Phase: api.VolumeBound,
    78  				},
    79  			},
    80  			expectedObj: &api.PersistentVolume{
    81  				ObjectMeta: metav1.ObjectMeta{
    82  					Name: "foo",
    83  				},
    84  				Status: api.PersistentVolumeStatus{
    85  					Phase:                   api.VolumeBound,
    86  					LastPhaseTransitionTime: &now,
    87  				},
    88  			},
    89  		},
    90  		{
    91  			name: "feature enabled: timestamp is updated when phase changes and old pv has a timestamp",
    92  			fg:   true,
    93  			oldObj: &api.PersistentVolume{
    94  				ObjectMeta: metav1.ObjectMeta{
    95  					Name: "foo",
    96  				},
    97  				Status: api.PersistentVolumeStatus{
    98  					Phase:                   api.VolumePending,
    99  					LastPhaseTransitionTime: &origin,
   100  				},
   101  			},
   102  			newObj: &api.PersistentVolume{
   103  				ObjectMeta: metav1.ObjectMeta{
   104  					Name: "foo",
   105  				},
   106  				Status: api.PersistentVolumeStatus{
   107  					Phase: api.VolumeBound,
   108  				},
   109  			},
   110  			expectedObj: &api.PersistentVolume{
   111  				ObjectMeta: metav1.ObjectMeta{
   112  					Name: "foo",
   113  				},
   114  				Status: api.PersistentVolumeStatus{
   115  					Phase:                   api.VolumeBound,
   116  					LastPhaseTransitionTime: &now,
   117  				},
   118  			},
   119  		},
   120  		{
   121  			name: "feature enabled: user timestamp change is respected on no phase change",
   122  			fg:   true,
   123  			oldObj: &api.PersistentVolume{
   124  				ObjectMeta: metav1.ObjectMeta{
   125  					Name: "foo",
   126  				},
   127  				Status: api.PersistentVolumeStatus{
   128  					Phase: api.VolumePending,
   129  				},
   130  			},
   131  			newObj: &api.PersistentVolume{
   132  				ObjectMeta: metav1.ObjectMeta{
   133  					Name: "foo",
   134  				},
   135  				Status: api.PersistentVolumeStatus{
   136  					Phase:                   api.VolumePending,
   137  					LastPhaseTransitionTime: &later,
   138  				},
   139  			},
   140  			expectedObj: &api.PersistentVolume{
   141  				ObjectMeta: metav1.ObjectMeta{
   142  					Name: "foo",
   143  				},
   144  				Status: api.PersistentVolumeStatus{
   145  					Phase:                   api.VolumePending,
   146  					LastPhaseTransitionTime: &later,
   147  				},
   148  			},
   149  		},
   150  		{
   151  			name: "feature enabled: user timestamp is respected on phase change",
   152  			fg:   true,
   153  			oldObj: &api.PersistentVolume{
   154  				ObjectMeta: metav1.ObjectMeta{
   155  					Name: "foo",
   156  				},
   157  				Status: api.PersistentVolumeStatus{
   158  					Phase:                   api.VolumePending,
   159  					LastPhaseTransitionTime: &origin,
   160  				},
   161  			},
   162  			newObj: &api.PersistentVolume{
   163  				ObjectMeta: metav1.ObjectMeta{
   164  					Name: "foo",
   165  				},
   166  				Status: api.PersistentVolumeStatus{
   167  					Phase:                   api.VolumeBound,
   168  					LastPhaseTransitionTime: &later,
   169  				},
   170  			},
   171  			expectedObj: &api.PersistentVolume{
   172  				ObjectMeta: metav1.ObjectMeta{
   173  					Name: "foo",
   174  				},
   175  				Status: api.PersistentVolumeStatus{
   176  					Phase:                   api.VolumeBound,
   177  					LastPhaseTransitionTime: &later,
   178  				},
   179  			},
   180  		},
   181  		{
   182  			name: "feature enabled: user timestamp change is respected on no phase change when old pv has a timestamp",
   183  			fg:   true,
   184  			oldObj: &api.PersistentVolume{
   185  				ObjectMeta: metav1.ObjectMeta{
   186  					Name: "foo",
   187  				},
   188  				Status: api.PersistentVolumeStatus{
   189  					Phase:                   api.VolumeBound,
   190  					LastPhaseTransitionTime: &origin,
   191  				},
   192  			},
   193  			newObj: &api.PersistentVolume{
   194  				ObjectMeta: metav1.ObjectMeta{
   195  					Name: "foo",
   196  				},
   197  				Status: api.PersistentVolumeStatus{
   198  					Phase:                   api.VolumeBound,
   199  					LastPhaseTransitionTime: &later,
   200  				},
   201  			},
   202  			expectedObj: &api.PersistentVolume{
   203  				ObjectMeta: metav1.ObjectMeta{
   204  					Name: "foo",
   205  				},
   206  				Status: api.PersistentVolumeStatus{
   207  					Phase:                   api.VolumeBound,
   208  					LastPhaseTransitionTime: &later,
   209  				},
   210  			},
   211  		},
   212  		{
   213  			name: "feature enabled: timestamp is updated when phase changes and both new and old timestamp matches",
   214  			fg:   true,
   215  			oldObj: &api.PersistentVolume{
   216  				ObjectMeta: metav1.ObjectMeta{
   217  					Name: "foo",
   218  				},
   219  				Status: api.PersistentVolumeStatus{
   220  					Phase:                   api.VolumePending,
   221  					LastPhaseTransitionTime: &origin,
   222  				},
   223  			},
   224  			newObj: &api.PersistentVolume{
   225  				ObjectMeta: metav1.ObjectMeta{
   226  					Name: "foo",
   227  				},
   228  				Status: api.PersistentVolumeStatus{
   229  					Phase:                   api.VolumeBound,
   230  					LastPhaseTransitionTime: &origin,
   231  				},
   232  			},
   233  			expectedObj: &api.PersistentVolume{
   234  				ObjectMeta: metav1.ObjectMeta{
   235  					Name: "foo",
   236  				},
   237  				Status: api.PersistentVolumeStatus{
   238  					Phase:                   api.VolumeBound,
   239  					LastPhaseTransitionTime: &now,
   240  				},
   241  			},
   242  		},
   243  		{
   244  			name: "feature disabled: timestamp is not updated",
   245  			fg:   false,
   246  			oldObj: &api.PersistentVolume{
   247  				ObjectMeta: metav1.ObjectMeta{
   248  					Name: "foo",
   249  				},
   250  				Status: api.PersistentVolumeStatus{
   251  					Phase: api.VolumePending,
   252  				},
   253  			},
   254  			newObj: &api.PersistentVolume{
   255  				ObjectMeta: metav1.ObjectMeta{
   256  					Name: "foo",
   257  				},
   258  				Status: api.PersistentVolumeStatus{
   259  					Phase: api.VolumeBound,
   260  				},
   261  			},
   262  			expectedObj: &api.PersistentVolume{
   263  				ObjectMeta: metav1.ObjectMeta{
   264  					Name: "foo",
   265  				},
   266  				Status: api.PersistentVolumeStatus{
   267  					Phase: api.VolumeBound,
   268  				},
   269  			},
   270  		},
   271  		{
   272  			name: "feature disabled: user timestamp is overwritten on phase change to nil",
   273  			fg:   false,
   274  			oldObj: &api.PersistentVolume{
   275  				ObjectMeta: metav1.ObjectMeta{
   276  					Name: "foo",
   277  				},
   278  				Status: api.PersistentVolumeStatus{
   279  					Phase: api.VolumePending,
   280  				},
   281  			},
   282  			newObj: &api.PersistentVolume{
   283  				ObjectMeta: metav1.ObjectMeta{
   284  					Name: "foo",
   285  				},
   286  				Status: api.PersistentVolumeStatus{
   287  					Phase:                   api.VolumePending,
   288  					LastPhaseTransitionTime: &later,
   289  				},
   290  			},
   291  			expectedObj: &api.PersistentVolume{
   292  				ObjectMeta: metav1.ObjectMeta{
   293  					Name: "foo",
   294  				},
   295  				Status: api.PersistentVolumeStatus{
   296  					Phase:                   api.VolumePending,
   297  					LastPhaseTransitionTime: nil,
   298  				},
   299  			},
   300  		},
   301  		{
   302  			name: "feature disabled: user timestamp change is respected on phase change",
   303  			fg:   false,
   304  			oldObj: &api.PersistentVolume{
   305  				ObjectMeta: metav1.ObjectMeta{
   306  					Name: "foo",
   307  				},
   308  				Status: api.PersistentVolumeStatus{
   309  					Phase:                   api.VolumePending,
   310  					LastPhaseTransitionTime: &origin,
   311  				},
   312  			},
   313  			newObj: &api.PersistentVolume{
   314  				ObjectMeta: metav1.ObjectMeta{
   315  					Name: "foo",
   316  				},
   317  				Status: api.PersistentVolumeStatus{
   318  					Phase:                   api.VolumeBound,
   319  					LastPhaseTransitionTime: &later,
   320  				},
   321  			},
   322  			expectedObj: &api.PersistentVolume{
   323  				ObjectMeta: metav1.ObjectMeta{
   324  					Name: "foo",
   325  				},
   326  				Status: api.PersistentVolumeStatus{
   327  					Phase:                   api.VolumeBound,
   328  					LastPhaseTransitionTime: &later,
   329  				},
   330  			},
   331  		},
   332  		{
   333  			name: "feature disabled: user timestamp change is respected on no phase change",
   334  			fg:   false,
   335  			oldObj: &api.PersistentVolume{
   336  				ObjectMeta: metav1.ObjectMeta{
   337  					Name: "foo",
   338  				},
   339  				Status: api.PersistentVolumeStatus{
   340  					Phase:                   api.VolumeBound,
   341  					LastPhaseTransitionTime: &origin,
   342  				},
   343  			},
   344  			newObj: &api.PersistentVolume{
   345  				ObjectMeta: metav1.ObjectMeta{
   346  					Name: "foo",
   347  				},
   348  				Status: api.PersistentVolumeStatus{
   349  					Phase:                   api.VolumeBound,
   350  					LastPhaseTransitionTime: &later,
   351  				},
   352  			},
   353  			expectedObj: &api.PersistentVolume{
   354  				ObjectMeta: metav1.ObjectMeta{
   355  					Name: "foo",
   356  				},
   357  				Status: api.PersistentVolumeStatus{
   358  					Phase:                   api.VolumeBound,
   359  					LastPhaseTransitionTime: &later,
   360  				},
   361  			},
   362  		},
   363  	}
   364  
   365  	for _, tc := range tests {
   366  		t.Run(tc.name, func(t *testing.T) {
   367  			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, tc.fg)
   368  
   369  			obj := tc.newObj.DeepCopy()
   370  			StatusStrategy.PrepareForUpdate(context.TODO(), obj, tc.oldObj.DeepCopy())
   371  			if !reflect.DeepEqual(obj, tc.expectedObj) {
   372  				t.Errorf("object diff: %s", cmp.Diff(obj, tc.expectedObj))
   373  			}
   374  		})
   375  	}
   376  }
   377  
   378  func TestStatusCreate(t *testing.T) {
   379  	now := metav1.Now()
   380  	NowFunc = func() metav1.Time { return now }
   381  	defer func() {
   382  		NowFunc = metav1.Now
   383  	}()
   384  	tests := []struct {
   385  		name        string
   386  		fg          bool
   387  		newObj      *api.PersistentVolume
   388  		expectedObj *api.PersistentVolume
   389  	}{
   390  		{
   391  			name: "feature enabled: pv is in pending phase and has a timestamp",
   392  			fg:   true,
   393  			newObj: &api.PersistentVolume{
   394  				ObjectMeta: metav1.ObjectMeta{
   395  					Name: "foo",
   396  				},
   397  			},
   398  			expectedObj: &api.PersistentVolume{
   399  				ObjectMeta: metav1.ObjectMeta{
   400  					Name: "foo",
   401  				},
   402  				Status: api.PersistentVolumeStatus{
   403  					Phase:                   api.VolumePending,
   404  					LastPhaseTransitionTime: &now,
   405  				},
   406  			},
   407  		},
   408  		{
   409  			name: "feature disabled: pv does not have phase and timestamp",
   410  			fg:   false,
   411  			newObj: &api.PersistentVolume{
   412  				ObjectMeta: metav1.ObjectMeta{
   413  					Name: "foo",
   414  				},
   415  			},
   416  			expectedObj: &api.PersistentVolume{
   417  				ObjectMeta: metav1.ObjectMeta{
   418  					Name: "foo",
   419  				},
   420  			},
   421  		},
   422  	}
   423  
   424  	for _, tc := range tests {
   425  		t.Run(tc.name, func(t *testing.T) {
   426  
   427  			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, tc.fg)
   428  			obj := tc.newObj.DeepCopy()
   429  			StatusStrategy.PrepareForCreate(context.TODO(), obj)
   430  			if !reflect.DeepEqual(obj, tc.expectedObj) {
   431  				t.Errorf("object diff: %s", cmp.Diff(obj, tc.expectedObj))
   432  			}
   433  		})
   434  	}
   435  }