k8s.io/kubernetes@v1.29.3/pkg/fieldpath/fieldpath_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 fieldpath 18 19 import ( 20 "strings" 21 "testing" 22 23 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 ) 26 27 func BenchmarkFormatMap(b *testing.B) { 28 var s string 29 m := map[string]string{ 30 "spec.pod.beta.kubernetes.io/statefulset-index": "1", 31 "Www.k8s.io/test": "1", 32 "foo": "bar", 33 "flannel.alpha.coreos.com/backend-data": `{"VNI":1,"VtepMAC":"ce:f9:c7:a4:de:64"}`, 34 "flannel.alpha.coreos.com/backend-type": "vxlan", 35 "flannel.alpha.coreos.com/kube-subnet-manager": "true", 36 "flannel.alpha.coreos.com/public-ip": "192.168.19.160", 37 "management.cattle.io/pod-limits": `{"cpu":"11400m","memory":"7965Mi"}`, 38 "management.cattle.io/pod-requests": `{"cpu":"2482m","memory":"7984Mi","pods":"26"}`, 39 "node.alpha.kubernetes.io/ttl": "0", 40 "volumes.kubernetes.io/controller-managed-attach-detach": "true", 41 } 42 b.ReportAllocs() 43 b.ResetTimer() 44 for i := 0; i < b.N; i++ { 45 s = FormatMap(m) 46 } 47 // Avoid compiler optimizations 48 _ = s 49 } 50 51 func TestExtractFieldPathAsString(t *testing.T) { 52 cases := []struct { 53 name string 54 fieldPath string 55 obj interface{} 56 expectedValue string 57 expectedMessageFragment string 58 }{ 59 { 60 name: "not an API object", 61 fieldPath: "metadata.name", 62 obj: "", 63 expectedMessageFragment: "object does not implement the Object interfaces", 64 }, 65 { 66 name: "ok - namespace", 67 fieldPath: "metadata.namespace", 68 obj: &v1.Pod{ 69 ObjectMeta: metav1.ObjectMeta{ 70 Namespace: "object-namespace", 71 }, 72 }, 73 expectedValue: "object-namespace", 74 }, 75 { 76 name: "ok - name", 77 fieldPath: "metadata.name", 78 obj: &v1.Pod{ 79 ObjectMeta: metav1.ObjectMeta{ 80 Name: "object-name", 81 }, 82 }, 83 expectedValue: "object-name", 84 }, 85 { 86 name: "ok - labels", 87 fieldPath: "metadata.labels", 88 obj: &v1.Pod{ 89 ObjectMeta: metav1.ObjectMeta{ 90 Labels: map[string]string{"key": "value"}, 91 }, 92 }, 93 expectedValue: "key=\"value\"", 94 }, 95 { 96 name: "ok - labels bslash n", 97 fieldPath: "metadata.labels", 98 obj: &v1.Pod{ 99 ObjectMeta: metav1.ObjectMeta{ 100 Labels: map[string]string{"key": "value\n"}, 101 }, 102 }, 103 expectedValue: "key=\"value\\n\"", 104 }, 105 { 106 name: "ok - annotations", 107 fieldPath: "metadata.annotations", 108 obj: &v1.Pod{ 109 ObjectMeta: metav1.ObjectMeta{ 110 Annotations: map[string]string{"builder": "john-doe"}, 111 }, 112 }, 113 expectedValue: "builder=\"john-doe\"", 114 }, 115 { 116 name: "ok - annotation", 117 fieldPath: "metadata.annotations['spec.pod.beta.kubernetes.io/statefulset-index']", 118 obj: &v1.Pod{ 119 ObjectMeta: metav1.ObjectMeta{ 120 Annotations: map[string]string{"spec.pod.beta.kubernetes.io/statefulset-index": "1"}, 121 }, 122 }, 123 expectedValue: "1", 124 }, 125 { 126 name: "ok - annotation", 127 fieldPath: "metadata.annotations['Www.k8s.io/test']", 128 obj: &v1.Pod{ 129 ObjectMeta: metav1.ObjectMeta{ 130 Annotations: map[string]string{"Www.k8s.io/test": "1"}, 131 }, 132 }, 133 expectedValue: "1", 134 }, 135 { 136 name: "ok - uid", 137 fieldPath: "metadata.uid", 138 obj: &v1.Pod{ 139 ObjectMeta: metav1.ObjectMeta{ 140 UID: "b70b3269-858e-12a8-9cf2-1232a194038a", 141 }, 142 }, 143 expectedValue: "b70b3269-858e-12a8-9cf2-1232a194038a", 144 }, 145 { 146 name: "ok - label", 147 fieldPath: "metadata.labels['something']", 148 obj: &v1.Pod{ 149 ObjectMeta: metav1.ObjectMeta{ 150 Labels: map[string]string{ 151 "something": "label value", 152 }, 153 }, 154 }, 155 expectedValue: "label value", 156 }, 157 { 158 name: "invalid expression", 159 fieldPath: "metadata.whoops", 160 obj: &v1.Pod{ 161 ObjectMeta: metav1.ObjectMeta{ 162 Namespace: "object-namespace", 163 }, 164 }, 165 expectedMessageFragment: "unsupported fieldPath", 166 }, 167 { 168 name: "invalid annotation key", 169 fieldPath: "metadata.annotations['invalid~key']", 170 obj: &v1.Pod{ 171 ObjectMeta: metav1.ObjectMeta{ 172 Annotations: map[string]string{"foo": "bar"}, 173 }, 174 }, 175 expectedMessageFragment: "invalid key subscript in metadata.annotations", 176 }, 177 { 178 name: "invalid label key", 179 fieldPath: "metadata.labels['Www.k8s.io/test']", 180 obj: &v1.Pod{ 181 ObjectMeta: metav1.ObjectMeta{ 182 Annotations: map[string]string{"foo": "bar"}, 183 }, 184 }, 185 expectedMessageFragment: "invalid key subscript in metadata.labels", 186 }, 187 { 188 name: "invalid subscript", 189 fieldPath: "metadata.notexisting['something']", 190 obj: &v1.Pod{}, 191 expectedMessageFragment: "fieldPath \"metadata.notexisting['something']\" does not support subscript", 192 }, 193 } 194 195 for _, tc := range cases { 196 actual, err := ExtractFieldPathAsString(tc.obj, tc.fieldPath) 197 if err != nil { 198 if tc.expectedMessageFragment != "" { 199 if !strings.Contains(err.Error(), tc.expectedMessageFragment) { 200 t.Errorf("%v: unexpected error message: %q, expected to contain %q", tc.name, err, tc.expectedMessageFragment) 201 } 202 } else { 203 t.Errorf("%v: unexpected error: %v", tc.name, err) 204 } 205 } else if tc.expectedMessageFragment != "" { 206 t.Errorf("%v: expected error: %v", tc.name, tc.expectedMessageFragment) 207 } else if e := tc.expectedValue; e != "" && e != actual { 208 t.Errorf("%v: unexpected result; got %q, expected %q", tc.name, actual, e) 209 } 210 } 211 } 212 213 func TestSplitMaybeSubscriptedPath(t *testing.T) { 214 cases := []struct { 215 fieldPath string 216 expectedPath string 217 expectedSubscript string 218 expectedOK bool 219 }{ 220 { 221 fieldPath: "metadata.annotations['key']", 222 expectedPath: "metadata.annotations", 223 expectedSubscript: "key", 224 expectedOK: true, 225 }, 226 { 227 fieldPath: "metadata.annotations['a[b']c']", 228 expectedPath: "metadata.annotations", 229 expectedSubscript: "a[b']c", 230 expectedOK: true, 231 }, 232 { 233 fieldPath: "metadata.labels['['key']", 234 expectedPath: "metadata.labels", 235 expectedSubscript: "['key", 236 expectedOK: true, 237 }, 238 { 239 fieldPath: "metadata.labels['key']']", 240 expectedPath: "metadata.labels", 241 expectedSubscript: "key']", 242 expectedOK: true, 243 }, 244 { 245 fieldPath: "metadata.labels['']", 246 expectedPath: "metadata.labels", 247 expectedSubscript: "", 248 expectedOK: true, 249 }, 250 { 251 fieldPath: "metadata.labels[' ']", 252 expectedPath: "metadata.labels", 253 expectedSubscript: " ", 254 expectedOK: true, 255 }, 256 { 257 fieldPath: "metadata.labels[ 'key' ]", 258 expectedOK: false, 259 }, 260 { 261 fieldPath: "metadata.labels[]", 262 expectedOK: false, 263 }, 264 { 265 fieldPath: "metadata.labels[']", 266 expectedOK: false, 267 }, 268 { 269 fieldPath: "metadata.labels['key']foo", 270 expectedOK: false, 271 }, 272 { 273 fieldPath: "['key']", 274 expectedOK: false, 275 }, 276 { 277 fieldPath: "metadata.labels", 278 expectedOK: false, 279 }, 280 } 281 for _, tc := range cases { 282 path, subscript, ok := SplitMaybeSubscriptedPath(tc.fieldPath) 283 if !ok { 284 if tc.expectedOK { 285 t.Errorf("SplitMaybeSubscriptedPath(%q) expected to return (_, _, true)", tc.fieldPath) 286 } 287 continue 288 } 289 if path != tc.expectedPath || subscript != tc.expectedSubscript { 290 t.Errorf("SplitMaybeSubscriptedPath(%q) = (%q, %q, true), expect (%q, %q, true)", 291 tc.fieldPath, path, subscript, tc.expectedPath, tc.expectedSubscript) 292 } 293 } 294 } 295 296 // TestFormatMap 297 func TestFormatMap(t *testing.T) { 298 type args struct { 299 m map[string]string 300 } 301 tests := []struct { 302 name string 303 args args 304 wantFmtStr string 305 }{ 306 { 307 name: "nil", 308 args: args{ 309 m: nil, 310 }, 311 wantFmtStr: "", 312 }, 313 { 314 name: "label", 315 args: args{ 316 m: map[string]string{ 317 "beta.kubernetes.io/os": "linux", 318 "kubernetes.io/arch": "amd64", 319 "kubernetes.io/hostname": "master01", 320 "kubernetes.io/os": "linux", 321 "node-role.kubernetes.io/control-plane": "true", 322 "node-role.kubernetes.io/master": "true", 323 }, 324 }, 325 wantFmtStr: `beta.kubernetes.io/os="linux" 326 kubernetes.io/arch="amd64" 327 kubernetes.io/hostname="master01" 328 kubernetes.io/os="linux" 329 node-role.kubernetes.io/control-plane="true" 330 node-role.kubernetes.io/master="true"`, 331 }, 332 { 333 name: "annotation", 334 args: args{ 335 m: map[string]string{ 336 "flannel.alpha.coreos.com/backend-data": `{"VNI":1,"VtepMAC":"ce:f9:c7:a4:de:64"}`, 337 "flannel.alpha.coreos.com/backend-type": "vxlan", 338 "flannel.alpha.coreos.com/kube-subnet-manager": "true", 339 "flannel.alpha.coreos.com/public-ip": "192.168.19.160", 340 "management.cattle.io/pod-limits": `{"cpu":"11400m","memory":"7965Mi"}`, 341 "management.cattle.io/pod-requests": `{"cpu":"2482m","memory":"7984Mi","pods":"26"}`, 342 "node.alpha.kubernetes.io/ttl": "0", 343 "volumes.kubernetes.io/controller-managed-attach-detach": "true", 344 }, 345 }, 346 wantFmtStr: `flannel.alpha.coreos.com/backend-data="{\"VNI\":1,\"VtepMAC\":\"ce:f9:c7:a4:de:64\"}" 347 flannel.alpha.coreos.com/backend-type="vxlan" 348 flannel.alpha.coreos.com/kube-subnet-manager="true" 349 flannel.alpha.coreos.com/public-ip="192.168.19.160" 350 management.cattle.io/pod-limits="{\"cpu\":\"11400m\",\"memory\":\"7965Mi\"}" 351 management.cattle.io/pod-requests="{\"cpu\":\"2482m\",\"memory\":\"7984Mi\",\"pods\":\"26\"}" 352 node.alpha.kubernetes.io/ttl="0" 353 volumes.kubernetes.io/controller-managed-attach-detach="true"`, 354 }, 355 } 356 for _, tt := range tests { 357 t.Run(tt.name, func(t *testing.T) { 358 if gotFmtStr := FormatMap(tt.args.m); gotFmtStr != tt.wantFmtStr { 359 t.Errorf("FormatMap() = %v, want %v", gotFmtStr, tt.wantFmtStr) 360 } 361 }) 362 } 363 }