k8s.io/kubernetes@v1.29.3/pkg/api/persistentvolume/util_test.go (about)

     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 persistentvolume
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/util/sets"
    27  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    28  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    29  	api "k8s.io/kubernetes/pkg/apis/core"
    30  	"k8s.io/kubernetes/pkg/features"
    31  	"k8s.io/utils/ptr"
    32  )
    33  
    34  func TestDropDisabledFields(t *testing.T) {
    35  	vacName := ptr.To("vac")
    36  
    37  	tests := map[string]struct {
    38  		oldSpec       *api.PersistentVolumeSpec
    39  		newSpec       *api.PersistentVolumeSpec
    40  		expectOldSpec *api.PersistentVolumeSpec
    41  		expectNewSpec *api.PersistentVolumeSpec
    42  		vacEnabled    bool
    43  	}{
    44  		"disabled vac clears volume attributes class name": {
    45  			vacEnabled:    false,
    46  			newSpec:       specWithVACName(vacName),
    47  			expectNewSpec: specWithVACName(nil),
    48  			oldSpec:       nil,
    49  			expectOldSpec: nil,
    50  		},
    51  		"enabled vac preserve volume attributes class name": {
    52  			vacEnabled:    true,
    53  			newSpec:       specWithVACName(vacName),
    54  			expectNewSpec: specWithVACName(vacName),
    55  			oldSpec:       nil,
    56  			expectOldSpec: nil,
    57  		},
    58  		"enabled vac preserve volume attributes class name when both old and new have it": {
    59  			vacEnabled:    true,
    60  			newSpec:       specWithVACName(vacName),
    61  			expectNewSpec: specWithVACName(vacName),
    62  			oldSpec:       specWithVACName(vacName),
    63  			expectOldSpec: specWithVACName(vacName),
    64  		},
    65  		"disabled vac old pv had volume attributes class name": {
    66  			vacEnabled:    false,
    67  			newSpec:       specWithVACName(vacName),
    68  			expectNewSpec: specWithVACName(vacName),
    69  			oldSpec:       specWithVACName(vacName),
    70  			expectOldSpec: specWithVACName(vacName),
    71  		},
    72  		"enabled vac preserves volume attributes class name when old pv did not had it": {
    73  			vacEnabled:    true,
    74  			newSpec:       specWithVACName(vacName),
    75  			expectNewSpec: specWithVACName(vacName),
    76  			oldSpec:       specWithVACName(nil),
    77  			expectOldSpec: specWithVACName(nil),
    78  		},
    79  		"disabled vac neither new pv nor old pv had volume attributes class name": {
    80  			vacEnabled:    false,
    81  			newSpec:       specWithVACName(nil),
    82  			expectNewSpec: specWithVACName(nil),
    83  			oldSpec:       specWithVACName(nil),
    84  			expectOldSpec: specWithVACName(nil),
    85  		},
    86  	}
    87  
    88  	for name, tc := range tests {
    89  		t.Run(name, func(t *testing.T) {
    90  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeAttributesClass, tc.vacEnabled)()
    91  
    92  			DropDisabledSpecFields(tc.newSpec, tc.oldSpec)
    93  			if !reflect.DeepEqual(tc.newSpec, tc.expectNewSpec) {
    94  				t.Error(cmp.Diff(tc.newSpec, tc.expectNewSpec))
    95  			}
    96  			if !reflect.DeepEqual(tc.oldSpec, tc.expectOldSpec) {
    97  				t.Error(cmp.Diff(tc.oldSpec, tc.expectOldSpec))
    98  			}
    99  		})
   100  	}
   101  }
   102  
   103  func specWithVACName(vacName *string) *api.PersistentVolumeSpec {
   104  	pvSpec := &api.PersistentVolumeSpec{
   105  		PersistentVolumeSource: api.PersistentVolumeSource{
   106  			CSI: &api.CSIPersistentVolumeSource{
   107  				Driver:       "com.google.gcepd",
   108  				VolumeHandle: "foobar",
   109  			},
   110  		},
   111  	}
   112  
   113  	if vacName != nil {
   114  		pvSpec.VolumeAttributesClassName = vacName
   115  	}
   116  	return pvSpec
   117  }
   118  
   119  func TestWarnings(t *testing.T) {
   120  	testcases := []struct {
   121  		name     string
   122  		template *api.PersistentVolume
   123  		expected []string
   124  	}{
   125  		{
   126  			name:     "null",
   127  			template: nil,
   128  			expected: nil,
   129  		},
   130  		{
   131  			name: "no warning",
   132  			template: &api.PersistentVolume{
   133  				ObjectMeta: metav1.ObjectMeta{
   134  					Name: "foo",
   135  				},
   136  				Status: api.PersistentVolumeStatus{
   137  					Phase: api.VolumeBound,
   138  				},
   139  			},
   140  			expected: nil,
   141  		},
   142  		{
   143  			name: "warning",
   144  			template: &api.PersistentVolume{
   145  				ObjectMeta: metav1.ObjectMeta{
   146  					Name: "foo",
   147  					Annotations: map[string]string{
   148  						api.BetaStorageClassAnnotation: "",
   149  					},
   150  				},
   151  				Spec: api.PersistentVolumeSpec{
   152  					NodeAffinity: &api.VolumeNodeAffinity{
   153  						Required: &api.NodeSelector{
   154  							NodeSelectorTerms: []api.NodeSelectorTerm{
   155  								{
   156  									MatchExpressions: []api.NodeSelectorRequirement{
   157  										{
   158  											Key:      "beta.kubernetes.io/os",
   159  											Operator: "Equal",
   160  											Values:   []string{"windows"},
   161  										},
   162  									},
   163  								},
   164  							},
   165  						},
   166  					},
   167  				},
   168  				Status: api.PersistentVolumeStatus{
   169  					Phase: api.VolumeBound,
   170  				},
   171  			},
   172  			expected: []string{
   173  				`metadata.annotations[volume.beta.kubernetes.io/storage-class]: deprecated since v1.8; use "storageClassName" attribute instead`,
   174  				`spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`,
   175  			},
   176  		},
   177  		{
   178  			name: "PV CephFS deprecation warning",
   179  			template: &api.PersistentVolume{
   180  				Spec: api.PersistentVolumeSpec{
   181  					PersistentVolumeSource: api.PersistentVolumeSource{
   182  						CephFS: &api.CephFSPersistentVolumeSource{
   183  							Monitors:   nil,
   184  							Path:       "",
   185  							User:       "",
   186  							SecretFile: "",
   187  							SecretRef:  nil,
   188  							ReadOnly:   false,
   189  						},
   190  					},
   191  				},
   192  			},
   193  			expected: []string{
   194  				`spec.cephfs: deprecated in v1.28, non-functional in v1.31+`,
   195  			},
   196  		},
   197  		{
   198  			name: "PV PhotonPersistentDisk deprecation warning",
   199  			template: &api.PersistentVolume{
   200  				Spec: api.PersistentVolumeSpec{
   201  					PersistentVolumeSource: api.PersistentVolumeSource{
   202  						PhotonPersistentDisk: &api.PhotonPersistentDiskVolumeSource{
   203  							PdID:   "",
   204  							FSType: "",
   205  						},
   206  					},
   207  				},
   208  			},
   209  			expected: []string{
   210  				`spec.photonPersistentDisk: deprecated in v1.11, non-functional in v1.16+`,
   211  			},
   212  		},
   213  		{
   214  			name: "PV RBD deprecation warning",
   215  			template: &api.PersistentVolume{
   216  				Spec: api.PersistentVolumeSpec{
   217  					PersistentVolumeSource: api.PersistentVolumeSource{
   218  						RBD: &api.RBDPersistentVolumeSource{
   219  							CephMonitors: nil,
   220  							RBDImage:     "",
   221  							FSType:       "",
   222  							RBDPool:      "",
   223  							RadosUser:    "",
   224  							Keyring:      "",
   225  							SecretRef:    nil,
   226  							ReadOnly:     false,
   227  						},
   228  					},
   229  				},
   230  			},
   231  			expected: []string{
   232  				`spec.rbd: deprecated in v1.28, non-functional in v1.31+`},
   233  		},
   234  		{
   235  			name: "PV ScaleIO deprecation warning",
   236  			template: &api.PersistentVolume{
   237  				Spec: api.PersistentVolumeSpec{
   238  					PersistentVolumeSource: api.PersistentVolumeSource{
   239  						ScaleIO: &api.ScaleIOPersistentVolumeSource{
   240  							Gateway:          "",
   241  							System:           "",
   242  							SecretRef:        nil,
   243  							SSLEnabled:       false,
   244  							ProtectionDomain: "",
   245  							StoragePool:      "",
   246  							StorageMode:      "",
   247  							VolumeName:       "",
   248  							FSType:           "",
   249  							ReadOnly:         false,
   250  						},
   251  					},
   252  				},
   253  			},
   254  			expected: []string{
   255  				`spec.scaleIO: deprecated in v1.16, non-functional in v1.22+`,
   256  			},
   257  		},
   258  		{
   259  			name: "PV StorageOS deprecation warning",
   260  			template: &api.PersistentVolume{
   261  				Spec: api.PersistentVolumeSpec{
   262  					PersistentVolumeSource: api.PersistentVolumeSource{
   263  						StorageOS: &api.StorageOSPersistentVolumeSource{
   264  							VolumeName:      "",
   265  							VolumeNamespace: "",
   266  							FSType:          "",
   267  							ReadOnly:        false,
   268  							SecretRef:       nil,
   269  						},
   270  					},
   271  				},
   272  			},
   273  			expected: []string{
   274  				`spec.storageOS: deprecated in v1.22, non-functional in v1.25+`,
   275  			},
   276  		},
   277  		{
   278  			name: "PV GlusterFS deprecation warning",
   279  			template: &api.PersistentVolume{
   280  				Spec: api.PersistentVolumeSpec{
   281  					PersistentVolumeSource: api.PersistentVolumeSource{
   282  						Glusterfs: &api.GlusterfsPersistentVolumeSource{
   283  							EndpointsName:      "",
   284  							Path:               "",
   285  							ReadOnly:           false,
   286  							EndpointsNamespace: nil,
   287  						},
   288  					},
   289  				},
   290  			},
   291  			expected: []string{
   292  				`spec.glusterfs: deprecated in v1.25, non-functional in v1.26+`,
   293  			},
   294  		},
   295  	}
   296  
   297  	for _, tc := range testcases {
   298  		t.Run("podspec_"+tc.name, func(t *testing.T) {
   299  			actual := sets.NewString(GetWarningsForPersistentVolume(tc.template)...)
   300  			expected := sets.NewString(tc.expected...)
   301  			for _, missing := range expected.Difference(actual).List() {
   302  				t.Errorf("missing: %s", missing)
   303  			}
   304  			for _, extra := range actual.Difference(expected).List() {
   305  				t.Errorf("extra: %s", extra)
   306  			}
   307  		})
   308  
   309  	}
   310  }