k8s.io/kubernetes@v1.29.3/pkg/registry/storage/csistoragecapacity/strategy_test.go (about) 1 /* 2 Copyright 2020 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 csistoragecapacity 18 19 import ( 20 "testing" 21 22 "github.com/google/go-cmp/cmp" 23 apiequality "k8s.io/apimachinery/pkg/api/equality" 24 "k8s.io/apimachinery/pkg/api/resource" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 27 "k8s.io/kubernetes/pkg/apis/storage" 28 ) 29 30 // getValidCSIStorageCapacity returns a fully-populated CSIStorageCapacity. 31 func getValidCSIStorageCapacity(name string, capacityStr string) *storage.CSIStorageCapacity { 32 mib := resource.MustParse("1Mi") 33 c := &storage.CSIStorageCapacity{ 34 ObjectMeta: metav1.ObjectMeta{ 35 Name: name, 36 Namespace: metav1.NamespaceDefault, 37 ResourceVersion: "1", 38 }, 39 StorageClassName: "bar", 40 NodeTopology: &metav1.LabelSelector{ 41 MatchExpressions: []metav1.LabelSelectorRequirement{ 42 { 43 Key: "node", 44 Operator: metav1.LabelSelectorOpIn, 45 Values: []string{ 46 "node1", 47 }, 48 }, 49 }, 50 }, 51 Capacity: &mib, 52 } 53 if capacityStr != "" { 54 capacityQuantity := resource.MustParse(capacityStr) 55 c.Capacity = &capacityQuantity 56 } 57 return c 58 } 59 60 func TestCSIStorageCapacityStrategy(t *testing.T) { 61 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{ 62 APIGroup: "storage.k8s.io", 63 APIVersion: "v1alphav1", 64 Resource: "csistoragecapacities", 65 }) 66 if !Strategy.NamespaceScoped() { 67 t.Errorf("CSIStorageCapacity must be namespace scoped") 68 } 69 if Strategy.AllowCreateOnUpdate() { 70 t.Errorf("CSIStorageCapacity should not allow create on update") 71 } 72 73 capacity := getValidCSIStorageCapacity("valid", "") 74 original := capacity.DeepCopy() 75 Strategy.PrepareForCreate(ctx, capacity) 76 errs := Strategy.Validate(ctx, capacity) 77 if len(errs) != 0 { 78 t.Errorf("unexpected error validating %v", errs) 79 } 80 81 // Create with status should have kept status and all other fields. 82 if !apiequality.Semantic.DeepEqual(capacity, original) { 83 t.Errorf("unexpected objects difference after creation: %v", cmp.Diff(original, capacity)) 84 } 85 86 // Update of immutable fields is disallowed 87 fields := []struct { 88 name string 89 update func(capacity *storage.CSIStorageCapacity) 90 }{ 91 { 92 name: "Topology", 93 update: func(capacity *storage.CSIStorageCapacity) { 94 capacity.NodeTopology.MatchLabels = map[string]string{"some-label": "some-value"} 95 }, 96 }, 97 { 98 name: "StorageClass", 99 update: func(capacity *storage.CSIStorageCapacity) { 100 capacity.StorageClassName += "-suffix" 101 }, 102 }, 103 } 104 for _, field := range fields { 105 t.Run(field.name, func(t *testing.T) { 106 newCapacity := capacity.DeepCopy() 107 field.update(newCapacity) 108 Strategy.PrepareForUpdate(ctx, newCapacity, capacity) 109 errs = Strategy.ValidateUpdate(ctx, newCapacity, capacity) 110 if len(errs) == 0 { 111 t.Errorf("Expected a validation error") 112 } 113 }) 114 } 115 } 116 117 func TestCSIStorageCapacityValidation(t *testing.T) { 118 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{ 119 APIGroup: "storage.k8s.io", 120 APIVersion: "v1alphav1", 121 Resource: "csistoragecapacities", 122 }) 123 124 tests := []struct { 125 name string 126 expectError bool 127 old, update *storage.CSIStorageCapacity 128 }{ 129 { 130 name: "before: no capacity, update: 1Gi capacity", 131 old: getValidCSIStorageCapacity("test", ""), 132 update: getValidCSIStorageCapacity("test", "1Gi"), 133 }, 134 { 135 name: "before: 1Gi capacity, update: no capacity", 136 old: getValidCSIStorageCapacity("test", "1Gi"), 137 update: getValidCSIStorageCapacity("test", ""), 138 }, 139 { 140 name: "name change", 141 expectError: true, 142 old: getValidCSIStorageCapacity("a", ""), 143 update: getValidCSIStorageCapacity("b", ""), 144 }, 145 { 146 name: "storage class name change", 147 expectError: true, 148 old: getValidCSIStorageCapacity("test", ""), 149 update: func() *storage.CSIStorageCapacity { 150 capacity := getValidCSIStorageCapacity("test", "") 151 capacity.StorageClassName += "-update" 152 return capacity 153 }(), 154 }, 155 } 156 157 for _, test := range tests { 158 t.Run(test.name, func(t *testing.T) { 159 oldCapacity := test.old.DeepCopy() 160 Strategy.PrepareForCreate(ctx, oldCapacity) 161 errs := Strategy.Validate(ctx, oldCapacity) 162 if len(errs) != 0 { 163 t.Errorf("unexpected validating errors for create: %v", errs) 164 } 165 166 newCapacity := test.update.DeepCopy() 167 Strategy.PrepareForUpdate(ctx, newCapacity, test.old) 168 errs = Strategy.ValidateUpdate(ctx, newCapacity, oldCapacity) 169 if len(errs) > 0 && !test.expectError { 170 t.Errorf("unexpected validation failure: %+v", errs) 171 } 172 if len(errs) == 0 && test.expectError { 173 t.Errorf("validation unexpectedly succeeded") 174 } 175 }) 176 } 177 }