github.com/cilium/cilium@v1.16.2/pkg/k8s/apis/cilium.io/utils/utils_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package utils 5 6 import ( 7 "fmt" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 "k8s.io/apimachinery/pkg/types" 12 13 k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 14 slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 15 "github.com/cilium/cilium/pkg/labels" 16 "github.com/cilium/cilium/pkg/policy/api" 17 ) 18 19 func Test_namespacesAreValid(t *testing.T) { 20 require.Equal(t, true, namespacesAreValid("default", []string{})) 21 require.Equal(t, true, namespacesAreValid("default", []string{"default"})) 22 require.Equal(t, false, namespacesAreValid("default", []string{"foo"})) 23 require.Equal(t, false, namespacesAreValid("default", []string{"default", "foo"})) 24 } 25 26 func Test_ParseToCiliumRule(t *testing.T) { 27 role := fmt.Sprintf("%s.role", labels.LabelSourceAny) 28 namespace := fmt.Sprintf("%s.%s", labels.LabelSourceK8s, k8sConst.PodNamespaceLabel) 29 uuid := types.UID("11bba160-ddca-11e8-b697-0800273b04ff") 30 type args struct { 31 namespace string 32 rule *api.Rule 33 uid types.UID 34 } 35 tests := []struct { 36 name string 37 args args 38 want *api.Rule 39 }{ 40 { 41 // When the rule has no namespace match, the namespace 42 // is inherited from the namespace where the rule is 43 // added. 44 name: "parse-in-namespace", 45 args: args{ 46 namespace: slim_metav1.NamespaceDefault, 47 uid: uuid, 48 rule: &api.Rule{ 49 EndpointSelector: api.NewESFromMatchRequirements( 50 map[string]string{ 51 role: "backend", 52 }, 53 nil, 54 ), 55 }, 56 }, 57 want: api.NewRule().WithEndpointSelector( 58 api.NewESFromMatchRequirements( 59 map[string]string{ 60 role: "backend", 61 namespace: "default", 62 }, 63 nil, 64 ), 65 ).WithLabels( 66 labels.LabelArray{ 67 { 68 Key: "io.cilium.k8s.policy.derived-from", 69 Value: "CiliumNetworkPolicy", 70 Source: labels.LabelSourceK8s, 71 }, 72 { 73 Key: "io.cilium.k8s.policy.name", 74 Value: "parse-in-namespace", 75 Source: labels.LabelSourceK8s, 76 }, 77 { 78 Key: "io.cilium.k8s.policy.namespace", 79 Value: "default", 80 Source: labels.LabelSourceK8s, 81 }, 82 { 83 Key: "io.cilium.k8s.policy.uid", 84 Value: string(uuid), 85 Source: labels.LabelSourceK8s, 86 }, 87 }, 88 ), 89 }, 90 { 91 // When the rule specifies a namespace, it is overridden 92 // by the namespace where the rule was inserted. 93 name: "parse-in-namespace-with-ns-selector", 94 args: args{ 95 namespace: slim_metav1.NamespaceDefault, 96 uid: uuid, 97 rule: &api.Rule{ 98 EndpointSelector: api.NewESFromMatchRequirements( 99 map[string]string{ 100 role: "backend", 101 namespace: "foo", 102 }, 103 nil, 104 ), 105 }, 106 }, 107 want: api.NewRule().WithEndpointSelector( 108 api.NewESFromMatchRequirements( 109 map[string]string{ 110 role: "backend", 111 namespace: "default", 112 }, 113 nil, 114 ), 115 ).WithLabels( 116 labels.LabelArray{ 117 { 118 Key: "io.cilium.k8s.policy.derived-from", 119 Value: "CiliumNetworkPolicy", 120 Source: labels.LabelSourceK8s, 121 }, 122 { 123 Key: "io.cilium.k8s.policy.name", 124 Value: "parse-in-namespace-with-ns-selector", 125 Source: labels.LabelSourceK8s, 126 }, 127 { 128 Key: "io.cilium.k8s.policy.namespace", 129 Value: "default", 130 Source: labels.LabelSourceK8s, 131 }, 132 { 133 Key: "io.cilium.k8s.policy.uid", 134 Value: string(uuid), 135 Source: labels.LabelSourceK8s, 136 }, 137 }, 138 ), 139 }, 140 { 141 // Don't insert a namespace selection when the rule 142 // is for init policies. 143 name: "parse-init-policy", 144 args: args{ 145 uid: uuid, 146 rule: &api.Rule{ 147 EndpointSelector: api.NewESFromMatchRequirements( 148 map[string]string{ 149 role: "backend", 150 podInitLbl: "", 151 }, 152 nil, 153 ), 154 }, 155 }, 156 want: api.NewRule().WithEndpointSelector( 157 api.NewESFromMatchRequirements( 158 map[string]string{ 159 role: "backend", 160 podInitLbl: "", 161 // No namespace because it's init. 162 // namespace: "default", 163 }, 164 nil, 165 ), 166 ).WithLabels( 167 labels.LabelArray{ 168 { 169 Key: "io.cilium.k8s.policy.derived-from", 170 Value: "CiliumClusterwideNetworkPolicy", 171 Source: labels.LabelSourceK8s, 172 }, 173 { 174 Key: "io.cilium.k8s.policy.name", 175 Value: "parse-init-policy", 176 Source: labels.LabelSourceK8s, 177 }, 178 { 179 Key: "io.cilium.k8s.policy.uid", 180 Value: string(uuid), 181 Source: labels.LabelSourceK8s, 182 }, 183 }, 184 ), 185 }, 186 { 187 // CNP with endpoint selectors should always select the 188 // current namespace 189 name: "parse-init-policy-namespaced", 190 args: args{ 191 namespace: slim_metav1.NamespaceDefault, 192 uid: uuid, 193 rule: &api.Rule{ 194 EndpointSelector: api.NewESFromMatchRequirements( 195 nil, 196 []slim_metav1.LabelSelectorRequirement{ 197 { 198 Key: "reserved.init", 199 Operator: slim_metav1.LabelSelectorOpDoesNotExist, 200 }, 201 }, 202 ), 203 Ingress: []api.IngressRule{ 204 { 205 IngressCommonRule: api.IngressCommonRule{ 206 FromEndpoints: []api.EndpointSelector{ 207 { 208 LabelSelector: &slim_metav1.LabelSelector{}, 209 }, 210 }, 211 }, 212 }, 213 }, 214 }, 215 }, 216 want: api.NewRule().WithEndpointSelector( 217 api.NewESFromMatchRequirements( 218 map[string]string{ 219 namespace: "default", 220 }, 221 []slim_metav1.LabelSelectorRequirement{ 222 { 223 Key: "reserved.init", 224 Operator: slim_metav1.LabelSelectorOpDoesNotExist, 225 }, 226 }, 227 ), 228 ).WithIngressRules( 229 []api.IngressRule{ 230 { 231 IngressCommonRule: api.IngressCommonRule{ 232 FromEndpoints: []api.EndpointSelector{ 233 api.NewESFromK8sLabelSelector( 234 labels.LabelSourceK8sKeyPrefix, 235 &slim_metav1.LabelSelector{ 236 MatchLabels: map[string]string{ 237 k8sConst.PodNamespaceLabel: "default", 238 }, 239 }), 240 }, 241 }, 242 }, 243 }, 244 ).WithLabels( 245 labels.LabelArray{ 246 { 247 Key: "io.cilium.k8s.policy.derived-from", 248 Value: "CiliumNetworkPolicy", 249 Source: labels.LabelSourceK8s, 250 }, 251 { 252 Key: "io.cilium.k8s.policy.name", 253 Value: "parse-init-policy-namespaced", 254 Source: labels.LabelSourceK8s, 255 }, 256 { 257 Key: "io.cilium.k8s.policy.namespace", 258 Value: "default", 259 Source: labels.LabelSourceK8s, 260 }, 261 { 262 Key: "io.cilium.k8s.policy.uid", 263 Value: string(uuid), 264 Source: labels.LabelSourceK8s, 265 }, 266 }, 267 ), 268 }, 269 { 270 name: "set-any-source-for-namespace", 271 args: args{ 272 namespace: slim_metav1.NamespaceDefault, 273 uid: uuid, 274 rule: &api.Rule{ 275 EndpointSelector: api.NewESFromMatchRequirements( 276 map[string]string{ 277 role: "backend", 278 }, 279 nil, 280 ), 281 Ingress: []api.IngressRule{ 282 { 283 IngressCommonRule: api.IngressCommonRule{ 284 FromEndpoints: []api.EndpointSelector{ 285 { 286 LabelSelector: &slim_metav1.LabelSelector{ 287 MatchLabels: map[string]string{ 288 podAnyPrefixLbl: "ns-2", 289 }, 290 }, 291 }, 292 }, 293 }, 294 }, 295 }, 296 }, 297 }, 298 want: api.NewRule().WithEndpointSelector( 299 api.NewESFromMatchRequirements( 300 map[string]string{ 301 role: "backend", 302 namespace: "default", 303 }, 304 nil, 305 ), 306 ).WithIngressRules( 307 []api.IngressRule{ 308 { 309 IngressCommonRule: api.IngressCommonRule{ 310 FromEndpoints: []api.EndpointSelector{ 311 api.NewESFromK8sLabelSelector( 312 labels.LabelSourceAnyKeyPrefix, 313 &slim_metav1.LabelSelector{ 314 MatchLabels: map[string]string{ 315 k8sConst.PodNamespaceLabel: "ns-2", 316 }, 317 }), 318 }, 319 }, 320 }, 321 }, 322 ).WithLabels( 323 labels.LabelArray{ 324 { 325 Key: "io.cilium.k8s.policy.derived-from", 326 Value: "CiliumNetworkPolicy", 327 Source: labels.LabelSourceK8s, 328 }, 329 { 330 Key: "io.cilium.k8s.policy.name", 331 Value: "set-any-source-for-namespace", 332 Source: labels.LabelSourceK8s, 333 }, 334 { 335 Key: "io.cilium.k8s.policy.namespace", 336 Value: "default", 337 Source: labels.LabelSourceK8s, 338 }, 339 { 340 Key: "io.cilium.k8s.policy.uid", 341 Value: string(uuid), 342 Source: labels.LabelSourceK8s, 343 }, 344 }, 345 ), 346 }, 347 { 348 // When the rule specifies namespace labels, namespace label is not added 349 // by the namespace where the rule was inserted. 350 name: "parse-in-namespace-with-ns-labels-selector", 351 args: args{ 352 namespace: slim_metav1.NamespaceDefault, 353 uid: uuid, 354 rule: &api.Rule{ 355 EndpointSelector: api.NewESFromMatchRequirements( 356 map[string]string{ 357 role: "backend", 358 }, 359 nil, 360 ), 361 Ingress: []api.IngressRule{ 362 { 363 IngressCommonRule: api.IngressCommonRule{ 364 FromEndpoints: []api.EndpointSelector{ 365 { 366 LabelSelector: &slim_metav1.LabelSelector{ 367 MatchLabels: map[string]string{ 368 podAnyNamespaceLabelsPrefix + "team": "team-a", 369 }, 370 }, 371 }, 372 }, 373 }, 374 }, 375 }, 376 }, 377 }, 378 want: api.NewRule().WithEndpointSelector( 379 api.NewESFromMatchRequirements( 380 map[string]string{ 381 role: "backend", 382 namespace: "default", 383 }, 384 nil, 385 ), 386 ).WithIngressRules( 387 []api.IngressRule{ 388 { 389 IngressCommonRule: api.IngressCommonRule{ 390 FromEndpoints: []api.EndpointSelector{ 391 api.NewESFromK8sLabelSelector( 392 labels.LabelSourceAnyKeyPrefix, 393 &slim_metav1.LabelSelector{ 394 MatchLabels: map[string]string{ 395 k8sConst.PodNamespaceMetaLabelsPrefix + "team": "team-a", 396 }, 397 }), 398 }, 399 }, 400 }, 401 }, 402 ).WithLabels( 403 labels.LabelArray{ 404 { 405 Key: "io.cilium.k8s.policy.derived-from", 406 Value: "CiliumNetworkPolicy", 407 Source: labels.LabelSourceK8s, 408 }, 409 { 410 Key: "io.cilium.k8s.policy.name", 411 Value: "parse-in-namespace-with-ns-labels-selector", 412 Source: labels.LabelSourceK8s, 413 }, 414 { 415 Key: "io.cilium.k8s.policy.namespace", 416 Value: "default", 417 Source: labels.LabelSourceK8s, 418 }, 419 { 420 Key: "io.cilium.k8s.policy.uid", 421 Value: string(uuid), 422 Source: labels.LabelSourceK8s, 423 }, 424 }, 425 ), 426 }, 427 { 428 // For a clusterwide policy the namespace is empty but when a to/fromEndpoint 429 // rule is added that represents a wildcard we add a match expression 430 // to account only for endpoints managed by cilium. 431 name: "wildcard-to-from-endpoints-with-ccnp", 432 args: args{ 433 // Empty namespace for Clusterwide policy 434 namespace: "", 435 uid: uuid, 436 rule: &api.Rule{ 437 EndpointSelector: api.NewESFromMatchRequirements( 438 map[string]string{ 439 role: "backend", 440 }, 441 nil, 442 ), 443 Ingress: []api.IngressRule{ 444 { 445 IngressCommonRule: api.IngressCommonRule{ 446 FromEndpoints: []api.EndpointSelector{ 447 { 448 LabelSelector: &slim_metav1.LabelSelector{}, 449 }, 450 }, 451 }, 452 }, 453 }, 454 }, 455 }, 456 want: api.NewRule().WithEndpointSelector( 457 api.NewESFromMatchRequirements( 458 map[string]string{ 459 role: "backend", 460 }, 461 nil, 462 ), 463 ).WithIngressRules( 464 []api.IngressRule{ 465 { 466 IngressCommonRule: api.IngressCommonRule{ 467 FromEndpoints: []api.EndpointSelector{ 468 api.NewESFromK8sLabelSelector( 469 labels.LabelSourceK8sKeyPrefix, 470 &slim_metav1.LabelSelector{ 471 MatchExpressions: []slim_metav1.LabelSelectorRequirement{ 472 { 473 Key: k8sConst.PodNamespaceLabel, 474 Operator: slim_metav1.LabelSelectorOpExists, 475 Values: []string{}, 476 }, 477 }, 478 }), 479 }, 480 }, 481 }, 482 }, 483 ).WithLabels( 484 labels.LabelArray{ 485 { 486 Key: "io.cilium.k8s.policy.derived-from", 487 Value: "CiliumClusterwideNetworkPolicy", 488 Source: labels.LabelSourceK8s, 489 }, 490 { 491 Key: "io.cilium.k8s.policy.name", 492 Value: "wildcard-to-from-endpoints-with-ccnp", 493 Source: labels.LabelSourceK8s, 494 }, 495 { 496 Key: "io.cilium.k8s.policy.uid", 497 Value: string(uuid), 498 Source: labels.LabelSourceK8s, 499 }, 500 }, 501 ), 502 }, 503 } 504 for _, tt := range tests { 505 t.Run(tt.name, func(t *testing.T) { 506 tt.args.rule.Sanitize() 507 got := ParseToCiliumRule(tt.args.namespace, tt.name, tt.args.uid, tt.args.rule) 508 509 // Sanitize to set AggregatedSelectors field. 510 tt.want.Sanitize() 511 require.EqualValues(t, tt.want, got, "Test Name: %s", tt.name) 512 }) 513 } 514 } 515 516 func TestParseToCiliumLabels(t *testing.T) { 517 518 uuid := types.UID("11bba160-ddca-11e8-b697-0800273b04ff") 519 type args struct { 520 namespace string 521 name string 522 uid types.UID 523 ruleLbs labels.LabelArray 524 } 525 tests := []struct { 526 name string 527 args args 528 want labels.LabelArray 529 }{ 530 { 531 name: "parse labels", 532 args: args{ 533 name: "foo", 534 namespace: "bar", 535 uid: uuid, 536 ruleLbs: labels.LabelArray{ 537 { 538 Key: "hello", 539 Value: "world", 540 Source: labels.LabelSourceK8s, 541 }, 542 }, 543 }, 544 want: labels.LabelArray{ 545 { 546 Key: "hello", 547 Value: "world", 548 Source: labels.LabelSourceK8s, 549 }, 550 { 551 Key: "io.cilium.k8s.policy.derived-from", 552 Value: "CiliumNetworkPolicy", 553 Source: labels.LabelSourceK8s, 554 }, 555 { 556 Key: "io.cilium.k8s.policy.name", 557 Value: "foo", 558 Source: labels.LabelSourceK8s, 559 }, 560 { 561 Key: "io.cilium.k8s.policy.namespace", 562 Value: "bar", 563 Source: labels.LabelSourceK8s, 564 }, 565 { 566 Key: "io.cilium.k8s.policy.uid", 567 Value: string(uuid), 568 Source: labels.LabelSourceK8s, 569 }, 570 }, 571 }, 572 } 573 for _, tt := range tests { 574 got := ParseToCiliumLabels(tt.args.namespace, tt.args.name, tt.args.uid, tt.args.ruleLbs) 575 require.EqualValuesf(t, tt.want, got, "Test Name: %s", tt.name) 576 } 577 }