k8s.io/kubernetes@v1.29.3/pkg/registry/storage/volumeattachment/strategy_test.go (about) 1 /* 2 Copyright 2017 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 volumeattachment 18 19 import ( 20 "context" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 apiequality "k8s.io/apimachinery/pkg/api/equality" 25 "k8s.io/apimachinery/pkg/api/resource" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 28 api "k8s.io/kubernetes/pkg/apis/core" 29 "k8s.io/kubernetes/pkg/apis/storage" 30 ) 31 32 func getValidVolumeAttachment(name string) *storage.VolumeAttachment { 33 return &storage.VolumeAttachment{ 34 ObjectMeta: metav1.ObjectMeta{ 35 Name: name, 36 }, 37 Spec: storage.VolumeAttachmentSpec{ 38 Attacher: "valid-attacher", 39 Source: storage.VolumeAttachmentSource{ 40 PersistentVolumeName: &name, 41 }, 42 NodeName: "valid-node", 43 }, 44 } 45 } 46 47 func getValidVolumeAttachmentWithInlineSpec(name string) *storage.VolumeAttachment { 48 volumeAttachment := getValidVolumeAttachment(name) 49 volumeAttachment.Spec.Source.PersistentVolumeName = nil 50 volumeAttachment.Spec.Source.InlineVolumeSpec = &api.PersistentVolumeSpec{ 51 Capacity: api.ResourceList{ 52 api.ResourceName(api.ResourceStorage): resource.MustParse("10"), 53 }, 54 AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, 55 PersistentVolumeSource: api.PersistentVolumeSource{ 56 CSI: &api.CSIPersistentVolumeSource{ 57 Driver: "com.test.foo", 58 VolumeHandle: name, 59 }, 60 }, 61 MountOptions: []string{"soft"}, 62 } 63 return volumeAttachment 64 } 65 66 func TestVolumeAttachmentStrategy(t *testing.T) { 67 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{ 68 APIGroup: "storage.k8s.io", 69 APIVersion: "v1", 70 Resource: "volumeattachments", 71 }) 72 if Strategy.NamespaceScoped() { 73 t.Errorf("VolumeAttachment must not be namespace scoped") 74 } 75 if Strategy.AllowCreateOnUpdate() { 76 t.Errorf("VolumeAttachment should not allow create on update") 77 } 78 79 volumeAttachment := getValidVolumeAttachment("valid-attachment") 80 81 Strategy.PrepareForCreate(ctx, volumeAttachment) 82 83 errs := Strategy.Validate(ctx, volumeAttachment) 84 if len(errs) != 0 { 85 t.Errorf("unexpected error validating %v", errs) 86 } 87 88 // Create with status should drop status 89 statusVolumeAttachment := volumeAttachment.DeepCopy() 90 statusVolumeAttachment.Status = storage.VolumeAttachmentStatus{Attached: true} 91 Strategy.PrepareForCreate(ctx, statusVolumeAttachment) 92 if !apiequality.Semantic.DeepEqual(statusVolumeAttachment, volumeAttachment) { 93 t.Errorf("unexpected objects difference after creating with status: %v", cmp.Diff(statusVolumeAttachment, volumeAttachment)) 94 } 95 96 // Update of spec is disallowed 97 newVolumeAttachment := volumeAttachment.DeepCopy() 98 newVolumeAttachment.Spec.NodeName = "valid-node-2" 99 100 Strategy.PrepareForUpdate(ctx, newVolumeAttachment, volumeAttachment) 101 102 errs = Strategy.ValidateUpdate(ctx, newVolumeAttachment, volumeAttachment) 103 if len(errs) == 0 { 104 t.Errorf("Expected a validation error") 105 } 106 107 // modifying status should be dropped 108 statusVolumeAttachment = volumeAttachment.DeepCopy() 109 statusVolumeAttachment.Status = storage.VolumeAttachmentStatus{Attached: true} 110 111 Strategy.PrepareForUpdate(ctx, statusVolumeAttachment, volumeAttachment) 112 113 if !apiequality.Semantic.DeepEqual(statusVolumeAttachment, volumeAttachment) { 114 t.Errorf("unexpected objects difference after modifying status: %v", cmp.Diff(statusVolumeAttachment, volumeAttachment)) 115 } 116 } 117 118 func TestVolumeAttachmentStrategySourceInlineSpec(t *testing.T) { 119 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{ 120 APIGroup: "storage.k8s.io", 121 APIVersion: "v1", 122 Resource: "volumeattachments", 123 }) 124 125 volumeAttachment := getValidVolumeAttachmentWithInlineSpec("valid-attachment") 126 volumeAttachmentSaved := volumeAttachment.DeepCopy() 127 Strategy.PrepareForCreate(ctx, volumeAttachment) 128 if volumeAttachment.Spec.Source.InlineVolumeSpec == nil { 129 t.Errorf("InlineVolumeSpec unexpectedly set to nil during PrepareForCreate") 130 } 131 if !apiequality.Semantic.DeepEqual(volumeAttachmentSaved, volumeAttachment) { 132 t.Errorf("unexpected difference in object after creation: %v", cmp.Diff(volumeAttachment, volumeAttachmentSaved)) 133 } 134 Strategy.PrepareForUpdate(ctx, volumeAttachmentSaved, volumeAttachment) 135 if volumeAttachmentSaved.Spec.Source.InlineVolumeSpec == nil { 136 t.Errorf("InlineVolumeSpec unexpectedly set to nil during PrepareForUpdate") 137 } 138 Strategy.PrepareForUpdate(ctx, volumeAttachmentSaved, volumeAttachment) 139 if volumeAttachmentSaved.Spec.Source.InlineVolumeSpec == nil { 140 t.Errorf("InlineVolumeSpec unexpectedly set to nil during PrepareForUpdate") 141 } 142 } 143 144 func TestVolumeAttachmentStatusStrategy(t *testing.T) { 145 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{ 146 APIGroup: "storage.k8s.io", 147 APIVersion: "v1", 148 Resource: "volumeattachments", 149 }) 150 151 volumeAttachment := getValidVolumeAttachment("valid-attachment") 152 153 // modifying status should be allowed 154 statusVolumeAttachment := volumeAttachment.DeepCopy() 155 statusVolumeAttachment.Status = storage.VolumeAttachmentStatus{Attached: true} 156 157 expectedVolumeAttachment := statusVolumeAttachment.DeepCopy() 158 StatusStrategy.PrepareForUpdate(ctx, statusVolumeAttachment, volumeAttachment) 159 if !apiequality.Semantic.DeepEqual(statusVolumeAttachment, expectedVolumeAttachment) { 160 t.Errorf("unexpected objects difference after modifying status: %v", cmp.Diff(statusVolumeAttachment, expectedVolumeAttachment)) 161 } 162 163 // spec and metadata modifications should be dropped 164 newVolumeAttachment := volumeAttachment.DeepCopy() 165 newVolumeAttachment.Spec.NodeName = "valid-node-2" 166 newVolumeAttachment.Labels = map[string]string{"foo": "bar"} 167 newVolumeAttachment.Annotations = map[string]string{"foo": "baz"} 168 newVolumeAttachment.OwnerReferences = []metav1.OwnerReference{ 169 { 170 APIVersion: "v1", 171 Kind: "Pod", 172 Name: "Foo", 173 }, 174 } 175 176 StatusStrategy.PrepareForUpdate(ctx, newVolumeAttachment, volumeAttachment) 177 if !apiequality.Semantic.DeepEqual(newVolumeAttachment, volumeAttachment) { 178 t.Errorf("unexpected objects difference after modifying spec: %v", cmp.Diff(newVolumeAttachment, volumeAttachment)) 179 } 180 } 181 182 func TestUpdatePreventsStatusWrite(t *testing.T) { 183 va := getValidVolumeAttachment("valid-attachment") 184 newAttachment := va.DeepCopy() 185 newAttachment.Status.Attached = true 186 Strategy.PrepareForUpdate(context.TODO(), newAttachment, va) 187 if newAttachment.Status.Attached { 188 t.Errorf("expected status to be %v got %v", false, newAttachment.Status.Attached) 189 } 190 } 191 192 func TestCreatePreventsStatusWrite(t *testing.T) { 193 va := getValidVolumeAttachment("valid-attachment") 194 va.Status.Attached = true 195 Strategy.PrepareForCreate(context.TODO(), va) 196 if va.Status.Attached { 197 t.Errorf("expected status to be false got %v", va.Status.Attached) 198 } 199 } 200 201 func TestVolumeAttachmentValidation(t *testing.T) { 202 invalidPVName := "invalid-!@#$%^&*()" 203 validPVName := "valid-volume-name" 204 tests := []struct { 205 name string 206 volumeAttachment *storage.VolumeAttachment 207 expectError bool 208 }{ 209 { 210 "valid attachment", 211 getValidVolumeAttachment("foo"), 212 false, 213 }, 214 { 215 "invalid PV name", 216 &storage.VolumeAttachment{ 217 ObjectMeta: metav1.ObjectMeta{ 218 Name: "foo", 219 }, 220 Spec: storage.VolumeAttachmentSpec{ 221 Attacher: "valid-attacher", 222 Source: storage.VolumeAttachmentSource{ 223 PersistentVolumeName: &invalidPVName, 224 }, 225 NodeName: "valid-node", 226 }, 227 }, 228 true, 229 }, 230 { 231 "invalid attacher name", 232 &storage.VolumeAttachment{ 233 ObjectMeta: metav1.ObjectMeta{ 234 Name: "foo", 235 }, 236 Spec: storage.VolumeAttachmentSpec{ 237 Attacher: "invalid!@#$%^&*()", 238 Source: storage.VolumeAttachmentSource{ 239 PersistentVolumeName: &validPVName, 240 }, 241 NodeName: "valid-node", 242 }, 243 }, 244 true, 245 }, 246 { 247 "invalid volume attachment", 248 &storage.VolumeAttachment{ 249 ObjectMeta: metav1.ObjectMeta{ 250 Name: "foo", 251 }, 252 Spec: storage.VolumeAttachmentSpec{ 253 Attacher: "invalid!@#$%^&*()", 254 Source: storage.VolumeAttachmentSource{ 255 PersistentVolumeName: nil, 256 }, 257 NodeName: "valid-node", 258 }, 259 }, 260 true, 261 }, 262 } 263 264 for _, test := range tests { 265 t.Run(test.name, func(t *testing.T) { 266 err := Strategy.Validate(context.TODO(), test.volumeAttachment) 267 if len(err) > 0 && !test.expectError { 268 t.Errorf("Validation of object failed: %+v", err) 269 } 270 if len(err) == 0 && test.expectError { 271 t.Errorf("Validation of object unexpectedly succeeded") 272 } 273 }) 274 } 275 }