k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/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 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: "PersistentVolumeReclaimRecycle deprecation warning", 179 template: &api.PersistentVolume{ 180 Spec: api.PersistentVolumeSpec{ 181 PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRecycle, 182 }, 183 }, 184 expected: []string{ 185 `spec.persistentVolumeReclaimPolicy: The Recycle reclaim policy is deprecated. Instead, the recommended approach is to use dynamic provisioning.`, 186 }, 187 }, 188 { 189 name: "PV CephFS deprecation warning", 190 template: &api.PersistentVolume{ 191 Spec: api.PersistentVolumeSpec{ 192 PersistentVolumeSource: api.PersistentVolumeSource{ 193 CephFS: &api.CephFSPersistentVolumeSource{ 194 Monitors: nil, 195 Path: "", 196 User: "", 197 SecretFile: "", 198 SecretRef: nil, 199 ReadOnly: false, 200 }, 201 }, 202 }, 203 }, 204 expected: []string{ 205 `spec.cephfs: deprecated in v1.28, non-functional in v1.31+`, 206 }, 207 }, 208 { 209 name: "PV PhotonPersistentDisk deprecation warning", 210 template: &api.PersistentVolume{ 211 Spec: api.PersistentVolumeSpec{ 212 PersistentVolumeSource: api.PersistentVolumeSource{ 213 PhotonPersistentDisk: &api.PhotonPersistentDiskVolumeSource{ 214 PdID: "", 215 FSType: "", 216 }, 217 }, 218 }, 219 }, 220 expected: []string{ 221 `spec.photonPersistentDisk: deprecated in v1.11, non-functional in v1.16+`, 222 }, 223 }, 224 { 225 name: "PV RBD deprecation warning", 226 template: &api.PersistentVolume{ 227 Spec: api.PersistentVolumeSpec{ 228 PersistentVolumeSource: api.PersistentVolumeSource{ 229 RBD: &api.RBDPersistentVolumeSource{ 230 CephMonitors: nil, 231 RBDImage: "", 232 FSType: "", 233 RBDPool: "", 234 RadosUser: "", 235 Keyring: "", 236 SecretRef: nil, 237 ReadOnly: false, 238 }, 239 }, 240 }, 241 }, 242 expected: []string{ 243 `spec.rbd: deprecated in v1.28, non-functional in v1.31+`}, 244 }, 245 { 246 name: "PV ScaleIO deprecation warning", 247 template: &api.PersistentVolume{ 248 Spec: api.PersistentVolumeSpec{ 249 PersistentVolumeSource: api.PersistentVolumeSource{ 250 ScaleIO: &api.ScaleIOPersistentVolumeSource{ 251 Gateway: "", 252 System: "", 253 SecretRef: nil, 254 SSLEnabled: false, 255 ProtectionDomain: "", 256 StoragePool: "", 257 StorageMode: "", 258 VolumeName: "", 259 FSType: "", 260 ReadOnly: false, 261 }, 262 }, 263 }, 264 }, 265 expected: []string{ 266 `spec.scaleIO: deprecated in v1.16, non-functional in v1.22+`, 267 }, 268 }, 269 { 270 name: "PV StorageOS deprecation warning", 271 template: &api.PersistentVolume{ 272 Spec: api.PersistentVolumeSpec{ 273 PersistentVolumeSource: api.PersistentVolumeSource{ 274 StorageOS: &api.StorageOSPersistentVolumeSource{ 275 VolumeName: "", 276 VolumeNamespace: "", 277 FSType: "", 278 ReadOnly: false, 279 SecretRef: nil, 280 }, 281 }, 282 }, 283 }, 284 expected: []string{ 285 `spec.storageOS: deprecated in v1.22, non-functional in v1.25+`, 286 }, 287 }, 288 { 289 name: "PV GlusterFS deprecation warning", 290 template: &api.PersistentVolume{ 291 Spec: api.PersistentVolumeSpec{ 292 PersistentVolumeSource: api.PersistentVolumeSource{ 293 Glusterfs: &api.GlusterfsPersistentVolumeSource{ 294 EndpointsName: "", 295 Path: "", 296 ReadOnly: false, 297 EndpointsNamespace: nil, 298 }, 299 }, 300 }, 301 }, 302 expected: []string{ 303 `spec.glusterfs: deprecated in v1.25, non-functional in v1.26+`, 304 }, 305 }, 306 } 307 308 for _, tc := range testcases { 309 t.Run("podspec_"+tc.name, func(t *testing.T) { 310 actual := sets.New[string](GetWarningsForPersistentVolume(tc.template)...) 311 expected := sets.New[string](tc.expected...) 312 for _, missing := range sets.List[string](expected.Difference(actual)) { 313 t.Errorf("missing: %s", missing) 314 } 315 for _, extra := range sets.List[string](actual.Difference(expected)) { 316 t.Errorf("extra: %s", extra) 317 } 318 }) 319 320 } 321 }